diff --git a/CHANGELOG.md b/CHANGELOG.md index de52c77..6642e22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 2.0.3 + +- Keep approved device polling active across transient hosted transport and edge failures. +- Use a 60-second OAuth request timeout and fixed content-free failure categories. + ## 2.0.2 - Retry transient post-approval capability checks across one hosted tenant cold start. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index e4c516e..d45555e 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -6,6 +6,7 @@ Compatibility has three independent axes: Hermes host, hosted Substrate capabili | Plugin | Hermes | Required hosted capabilities | Status | |---|---|---|---| +| 2.0.3 | 0.20.x | Same as 2.0.2; resilient approved-device polling across hosted stalls | Contract and lifecycle integration tested | | 2.0.2 | 0.20.x | Same as 2.0.1; bounded retry for hosted tenant cold starts | Contract and lifecycle integration tested | | 2.0.1 | 0.20.x | Same as 2.0.0; complete email authorization URL in agent/headless prompts | Contract and lifecycle integration tested | | 2.0.0 | 0.20.x | `stream-v2`, `entity-wiki-v1`, `entity-quality-v2`, hosted device authorization | Contract and lifecycle integration tested | diff --git a/SECURITY.md b/SECURITY.md index efe0ee8..5cd5a63 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,7 +2,7 @@ ## Supported versions -The standalone `v2.0.0`, `v2.0.1`, and `v2.0.2` releases target Hermes 0.20.x and the hosted service at `https://app.trysubstrate.co`. +The standalone `v2.0.0`, `v2.0.1`, and `v2.0.2` releases and pending `v2.0.3` patch target Hermes 0.20.x and the hosted service at `https://app.trysubstrate.co`. Candidate CI artifacts are not supported releases. ## Report a vulnerability diff --git a/docs/extraction-manifest.json b/docs/extraction-manifest.json index 58353a3..d876d1b 100644 --- a/docs/extraction-manifest.json +++ b/docs/extraction-manifest.json @@ -59,13 +59,13 @@ "class": "standalone_repository_policy_or_test", "path": "CHANGELOG.md", "reason": "Required only by the independent public repository.", - "sha256": "47387d0cf3880d640f8912e5cb479f8669776625576174e48c21b68103d66469" + "sha256": "57f9eb855dc41b263892d8229fa9fed046420da8a67d65fc34bd93f02c9eaa9c" }, { "class": "standalone_repository_policy_or_test", "path": "COMPATIBILITY.md", "reason": "Required only by the independent public repository.", - "sha256": "1f18a49d552f2912872c10b0e41e93ff50ccd923d2fa9c108d35084dc5a5b22c" + "sha256": "67bcb14e4988ed5b49379e93f93fa588e31a068b394a8787d1ea81ba8f6a6c70" }, { "class": "standalone_repository_policy_or_test", @@ -83,7 +83,7 @@ "class": "standalone_repository_policy_or_test", "path": "SECURITY.md", "reason": "Required only by the independent public repository.", - "sha256": "48509dbbefd182381675499ff5636df18073411db580b05a1430e5eec1714df7" + "sha256": "756264bc4dbd9f9df5c6e4bc016545fc1ac0921b384346e40d557706c2137fc5" }, { "class": "standalone_repository_policy_or_test", @@ -119,7 +119,7 @@ "class": "standalone_repository_policy_or_test", "path": "pyproject.toml", "reason": "Required only by the independent public repository.", - "sha256": "7320bc7c90bcafb9b11d9bb7d40e4128f351ea897567c6f40b7f4e618b75954f" + "sha256": "3187d3621ddb1c227c0db74ee1180ed99c28200f738e02b17e2857f4c9a82e47" }, { "class": "standalone_repository_policy_or_test", @@ -137,7 +137,7 @@ "class": "standalone_plugin_implementation", "path": "src/substrate_wiki/onboarding.py", "reason": "Added for hosted Hermes 0.20 automatic onboarding.", - "sha256": "2f1d35c0c568a6d817a3cfcb36b99a4447636ed8ab512d656a4c4411f4a48967" + "sha256": "a835092092bca7266978609763d36f5767ac363fe7180bda482d713df2ecc439" }, { "class": "standalone_repository_policy_or_test", @@ -337,7 +337,7 @@ { "class": "build-and-install", "destination": "scripts/install_hermes_plugin.py", - "destination_sha256": "4b34ee40d0d08ef24d03128e1cfc5ef73c69b39ca77b3fff59f4a4133cef76f2", + "destination_sha256": "72247d3537140098365350020cce29658c0743fee1aa738d7143db82316acce4", "source": "scripts/install_hermes_plugin.py", "source_sha256": "7600b2681c3aebcb1b1492b0a04be38bbbec637089cbbcfb1cc26e8c10865b8d", "transformation": "modified_for_standalone" @@ -353,7 +353,7 @@ { "class": "build-and-install", "destination": "scripts/verify_public_plugin_candidate.py", - "destination_sha256": "78dee8e9765fd0bda094a8b3ec25b582680506cb0ea4f7229bcaf9d6499697be", + "destination_sha256": "342076e02ac0d40620c859aa9f9af828110a0f818a862dd3bd75afff9222455c", "source": "scripts/verify_public_plugin_candidate.py", "source_sha256": "4130935d530075fce1758e2e89bd5d973a722e2293b5b1058cfe0d17f326172b", "transformation": "modified_for_standalone" @@ -393,7 +393,7 @@ { "class": "plugin-package", "destination": "src/substrate_wiki/client.py", - "destination_sha256": "a6e7c18e916057835e8cae9e3aa89bcc7357b397f014b8bc5bb71d995a5aa841", + "destination_sha256": "59413ab9fa2eef0d943a70a53e2b5d9d5163c0d9e154538735715e8f88585616", "source": "hermes-plugin/substrate_wiki/client.py", "source_sha256": "8684b3ecc788ee058c630747b1c72578177cfc948c72ac539ddcc4a1df9e791b", "transformation": "modified_for_standalone" @@ -417,7 +417,7 @@ { "class": "plugin-package", "destination": "src/substrate_wiki/plugin.yaml", - "destination_sha256": "75710c33846e9aebd7008b225977dd608b6809efd0b9b3dcc28204147bc293ab", + "destination_sha256": "0e1c680eb2822ca6c58315bd91a058468d107065fde0881ab39aafe54bd13690", "source": "hermes-plugin/substrate_wiki/plugin.yaml", "source_sha256": "5bfc8b20bfe99b8dd4fcb35616724e9a4a93bdfd1f3b270cfea1ccff18eaa116", "transformation": "modified_for_standalone" @@ -481,7 +481,7 @@ { "class": "plugin-tests", "destination": "tests/test_entity_memory.py", - "destination_sha256": "7fdd0c9053b91ae414d35bf6449cb32d7308dee715ef641f36d7c9a12cbffa14", + "destination_sha256": "9956679141ec085ab00dabca699fd4afc3638d039f9c567bca7b301929bf595a", "source": "tests/contract/test_hermes_entity_memory_v13.py", "source_sha256": "297d7dffa2a53dd08cb1737ac024a97eb190c4ce42b338a952d8bec9aeba37f4", "transformation": "modified_for_standalone" @@ -537,7 +537,7 @@ { "class": "plugin-tests", "destination": "tests/test_packaging.py", - "destination_sha256": "a5d10ca8098d0bc2bcc38aee4a0e0aa752a9aec4e0bd9f0041c2c566d45e8ef6", + "destination_sha256": "c4033e38f9e832779062381b0d0dcf71eecc25554d8df9af383a2d0c334dcd78", "source": "tests/contract/test_hermes_plugin_packaging.py", "source_sha256": "c54967830788555b8d7bd5d2f871baeb6b98d7804a2bc7410ab297078bc20ee7", "transformation": "modified_for_standalone" diff --git a/pyproject.toml b/pyproject.toml index 1aae430..ce9dcb0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "hermes-substrate-wiki" -version = "2.0.2" +version = "2.0.3" description = "Substrate memory-provider plugin for Hermes Agent" readme = "README.md" requires-python = ">=3.11" diff --git a/scripts/install_hermes_plugin.py b/scripts/install_hermes_plugin.py index d72bad5..76daf84 100644 --- a/scripts/install_hermes_plugin.py +++ b/scripts/install_hermes_plugin.py @@ -19,7 +19,7 @@ from typing import Any, cast PLUGIN_NAME = "substrate_wiki" -EXPECTED_VERSION = "2.0.2" +EXPECTED_VERSION = "2.0.3" EXPECTED_HERMES_VERSION = "0.20.0" LICENSE_FILENAME = "LICENSE" REQUIRED_FILES = { diff --git a/scripts/verify_public_plugin_candidate.py b/scripts/verify_public_plugin_candidate.py index a637aab..09d2bef 100644 --- a/scripts/verify_public_plugin_candidate.py +++ b/scripts/verify_public_plugin_candidate.py @@ -105,8 +105,11 @@ {"0a3c7af6a761a1b22c5b94f7e1738d5d420e5f085f775d792728ed9ca684ac13"} ), "tests/test_packaging.py": frozenset( - {"c7a8e84d116319e62b0b7817c1a049225088c8b3baf59c42ad82cbefc3c172b5"} - ), + { + "c4033e38f9e832779062381b0d0dcf71eecc25554d8df9af383a2d0c334dcd78", + "c7a8e84d116319e62b0b7817c1a049225088c8b3baf59c42ad82cbefc3c172b5", + } + ), "tests/test_publication_scanner.py": frozenset( { "c2352cc593ab08d452479f9ed59651e95acdf0cd5511a94f3830e7910bf65a21", @@ -123,15 +126,16 @@ # These are exact reviewed bytes only; any modification fails closed. EXACT_PROTECTED_KEY_REFERENCE_SHA256_ALLOWLIST: dict[str, frozenset[str]] = { "SECURITY.md": frozenset( - { - "34a9cd6cd747589e1ff34fe412d29a04ecc076df19871eaea8a6a078d0b17e06", - "bb4ba0542582cf3a89a37906651074c52bd1c28754b079bd67fd856c7fe24362", - "85320ec1e6a8c655b1f4e278b95c1087a6382bf3141366f1290c0933590f4645", - "99aed8e5a9a712f0fd2d9caa5223f0b332220f98699351565a3bb35a23d2848c", - "ef0cdf7a6c2fdc4ec7e781be15122f9b20331769929517a0f3c82ab15363480b", - "48509dbbefd182381675499ff5636df18073411db580b05a1430e5eec1714df7" - } - ), + { + "34a9cd6cd747589e1ff34fe412d29a04ecc076df19871eaea8a6a078d0b17e06", + "48509dbbefd182381675499ff5636df18073411db580b05a1430e5eec1714df7", + "756264bc4dbd9f9df5c6e4bc016545fc1ac0921b384346e40d557706c2137fc5", + "85320ec1e6a8c655b1f4e278b95c1087a6382bf3141366f1290c0933590f4645", + "99aed8e5a9a712f0fd2d9caa5223f0b332220f98699351565a3bb35a23d2848c", + "bb4ba0542582cf3a89a37906651074c52bd1c28754b079bd67fd856c7fe24362", + "ef0cdf7a6c2fdc4ec7e781be15122f9b20331769929517a0f3c82ab15363480b", + } + ), "docs/api-ownership.json": frozenset( {"3112d0d252749b45c6e081c6d7987af2c7aa665936ee37eadab2ea7d01f4eb74"} ), @@ -206,17 +210,56 @@ # file digests. Any byte change re-enables all endpoint and credential-name detectors. _HOSTED_ONBOARDING_EXACT_ALLOWLIST = { 'README.md': frozenset({'87734abc087a4267e77aaeae7fb349d4c19f56dea2f7f3d173ab833dfb1da5e9', '6361beb1a5153f5dbcfca70740c1905096f3bc32ddfd4cc22157c6576c61b83a', 'f139b1328c7884dfa664aa59b47d5d707b2a72894747911b8f99d5e89f6c7eca', 'cdc48a8f8dbe1d68dfe329d5106c55895c5345924544c2cc4b3d7190c837705b', '0d74ffbf410fe559478ee6a67e105f275d72d0477c71e86c81531798bc07e599'}), - 'COMPATIBILITY.md': frozenset({'e14a7a057ee64449041a2073590641a44cfe648cff696d4d20c58eed9e4509a3', '1f8fc410a88c7b410c4e72f4210226629b573a3c7f0cc410bc35939cd2317cfc', '1f18a49d552f2912872c10b0e41e93ff50ccd923d2fa9c108d35084dc5a5b22c'}), - 'SECURITY.md': frozenset({'9cf9406266336996c57227413514cf97a04eb13264d80fa58c54d325fecf3d9f', '85320ec1e6a8c655b1f4e278b95c1087a6382bf3141366f1290c0933590f4645', '99aed8e5a9a712f0fd2d9caa5223f0b332220f98699351565a3bb35a23d2848c', 'ef0cdf7a6c2fdc4ec7e781be15122f9b20331769929517a0f3c82ab15363480b', '48509dbbefd182381675499ff5636df18073411db580b05a1430e5eec1714df7'}), + 'COMPATIBILITY.md': frozenset( + { + "1f18a49d552f2912872c10b0e41e93ff50ccd923d2fa9c108d35084dc5a5b22c", + "1f8fc410a88c7b410c4e72f4210226629b573a3c7f0cc410bc35939cd2317cfc", + "67bcb14e4988ed5b49379e93f93fa588e31a068b394a8787d1ea81ba8f6a6c70", + "e14a7a057ee64449041a2073590641a44cfe648cff696d4d20c58eed9e4509a3", + } + ), + 'SECURITY.md': frozenset( + { + "48509dbbefd182381675499ff5636df18073411db580b05a1430e5eec1714df7", + "756264bc4dbd9f9df5c6e4bc016545fc1ac0921b384346e40d557706c2137fc5", + "85320ec1e6a8c655b1f4e278b95c1087a6382bf3141366f1290c0933590f4645", + "99aed8e5a9a712f0fd2d9caa5223f0b332220f98699351565a3bb35a23d2848c", + "9cf9406266336996c57227413514cf97a04eb13264d80fa58c54d325fecf3d9f", + "ef0cdf7a6c2fdc4ec7e781be15122f9b20331769929517a0f3c82ab15363480b", + } + ), 'docs/api-ownership.json': frozenset({'f1894c4653c51c6f02d23194617a3a1e9035d2cbe2b7c282f4cd6fbb26d8b65c'}), 'docs/operation.md': frozenset({'fadd72791e097d878bbfbf338922d568f3c3d47958f26a41f53d3597471988fa'}), 'docs/public-boundary.json': frozenset({'976403a6a802832adddd5ab1ff56fdc2e4e4847a1fe37ea5e6a2d5f106bbbbe2', '648904170d6c66de6f15cfe51fb494225467730ff5167225d6fd427a6eac3571'}), 'docs/public-boundary.md': frozenset({'c9228e2abd22a8af558cd272988435c1caa69ad79c625cb08c8ef32e639eb0f3'}), 'src/substrate_wiki/README.md': frozenset({'44a11fda85d6d8170d772beadba2149bd114624479344b69a69cd49634309204'}), 'src/substrate_wiki/__init__.py': frozenset({'425cd191f723805bed85d965d2d74daeac272dd0660eadf6bac92f1fa02d6f4a'}), - 'src/substrate_wiki/client.py': frozenset({'082164ad24c879f6ca6434a8f28c251cd1ee7f4b413c5a788a70b351e2187f2a', 'f98518e2eea1d57e813130822ea95de1fcc5b550adf9b65164347468eadc6818', 'a6e7c18e916057835e8cae9e3aa89bcc7357b397f014b8bc5bb71d995a5aa841'}), - 'src/substrate_wiki/onboarding.py': frozenset({'2f1d35c0c568a6d817a3cfcb36b99a4447636ed8ab512d656a4c4411f4a48967', '4ae1938346a5af64b3936913c4b8438891d8170c6caa617b2eb3b5c816079ec0', '52e11806e20017c920d4c0caef394570a8fdfb2cabff8a87cdbde3dc7e5a64fc', 'd58fc9693f78cb4d5f8d9738c9895f009e098d73400d4e9530e7454662fabff1', 'da01090b5f007d9741a06d7ff4a9c8140036e4ef26512665b4802800e0b543a3'}), - 'scripts/install_hermes_plugin.py': frozenset({'69af75e4240166896031f3a396fd0b2bdc4d00adbc836d1a4f22019bc6713b75', '33adb95c93f478a91991a97f0b9b6a1c9d2cee77e7894ed37fe331a4403b0bb8', '4b34ee40d0d08ef24d03128e1cfc5ef73c69b39ca77b3fff59f4a4133cef76f2'}), + 'src/substrate_wiki/client.py': frozenset( + { + "082164ad24c879f6ca6434a8f28c251cd1ee7f4b413c5a788a70b351e2187f2a", + "59413ab9fa2eef0d943a70a53e2b5d9d5163c0d9e154538735715e8f88585616", + "a6e7c18e916057835e8cae9e3aa89bcc7357b397f014b8bc5bb71d995a5aa841", + "f98518e2eea1d57e813130822ea95de1fcc5b550adf9b65164347468eadc6818", + } + ), + 'src/substrate_wiki/onboarding.py': frozenset( + { + "2f1d35c0c568a6d817a3cfcb36b99a4447636ed8ab512d656a4c4411f4a48967", + "4ae1938346a5af64b3936913c4b8438891d8170c6caa617b2eb3b5c816079ec0", + "52e11806e20017c920d4c0caef394570a8fdfb2cabff8a87cdbde3dc7e5a64fc", + "a835092092bca7266978609763d36f5767ac363fe7180bda482d713df2ecc439", + "d58fc9693f78cb4d5f8d9738c9895f009e098d73400d4e9530e7454662fabff1", + "da01090b5f007d9741a06d7ff4a9c8140036e4ef26512665b4802800e0b543a3", + } + ), + 'scripts/install_hermes_plugin.py': frozenset( + { + "33adb95c93f478a91991a97f0b9b6a1c9d2cee77e7894ed37fe331a4403b0bb8", + "4b34ee40d0d08ef24d03128e1cfc5ef73c69b39ca77b3fff59f4a4133cef76f2", + "69af75e4240166896031f3a396fd0b2bdc4d00adbc836d1a4f22019bc6713b75", + "72247d3537140098365350020cce29658c0743fee1aa738d7143db82316acce4", + } + ), 'tests/test_hardening.py': frozenset({'f5f87125f1edd37bff1d44301d6bb0f44cc7faf3ba6122bdbe7569f349fea7a3'}), 'tests/test_memory_provider.py': frozenset({'2c4517847dfad341063a69afcc737316a74574471fc98a45eaff21cfe4e271fd'}), } @@ -233,7 +276,7 @@ "4b444b2583fbdd340b17d279fd169103c57f87a56dece39988d784b311222920" ) TRUSTED_HISTORICAL_BLOB_POLICY_SHA256 = ( - "2a94f0ce0443952df1c7be7eb1f12cf7aabad96a4e9ccf482acc0ae73d2606ae" + "7c56d7b749266ad9506b03d6ed5487efe6cc0b7a5bb7f38142348386b00a4968" ) SCANNER_PATH = "scripts/verify_public_plugin_candidate.py" DESTINATION_MANIFEST_PATH = "docs/extraction-manifest.json" diff --git a/src/substrate_wiki/client.py b/src/substrate_wiki/client.py index c92efa1..0022ac3 100644 --- a/src/substrate_wiki/client.py +++ b/src/substrate_wiki/client.py @@ -26,7 +26,7 @@ _MAX_SHORT_FIELD_CHARS = 2048 _MAX_TEXT_FIELD_CHARS = 65536 _MAX_MEMORY_CARD_CHARS = 8192 -_USER_AGENT = "substrate_wiki-hermes-plugin/2.0.2" +_USER_AGENT = "substrate_wiki-hermes-plugin/2.0.3" _PLUGIN_VERSION = (2, 0, 0) diff --git a/src/substrate_wiki/onboarding.py b/src/substrate_wiki/onboarding.py index d00957d..c466117 100644 --- a/src/substrate_wiki/onboarding.py +++ b/src/substrate_wiki/onboarding.py @@ -29,7 +29,7 @@ SCOPES = "capture retrieve" DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code" _STATE_VERSION = 1 -_PLUGIN_VERSION = "2.0.2" +_PLUGIN_VERSION = "2.0.3" _MAX_RESPONSE = 64 * 1024 _CAPABILITY_TIMEOUT_SECONDS = 60.0 _CAPABILITY_ATTEMPTS = 2 diff --git a/src/substrate_wiki/plugin.yaml b/src/substrate_wiki/plugin.yaml index a1d6a19..cb307c4 100644 --- a/src/substrate_wiki/plugin.yaml +++ b/src/substrate_wiki/plugin.yaml @@ -1,5 +1,5 @@ name: substrate_wiki -version: 2.0.2 +version: 2.0.3 description: "Unified entity-centric Substrate Markdown wiki memory with durable asynchronous capture." hooks: - on_pre_compress diff --git a/tests/test_entity_memory.py b/tests/test_entity_memory.py index ca7c12d..1019587 100644 --- a/tests/test_entity_memory.py +++ b/tests/test_entity_memory.py @@ -251,6 +251,6 @@ def test_semantic_gate_rejects_non_strict_versions( def test_v141_manifest_and_prompt_describe_one_published_memory() -> None: manifest = (PLUGIN_ROOT / "substrate_wiki" / "plugin.yaml").read_text(encoding="utf-8") prompt = SubstrateWikiProvider().system_prompt_block() - assert "version: 2.0.2" in manifest + assert "version: 2.0.3" in manifest assert "single published memory" in prompt assert "canonical published entity" in prompt diff --git a/tests/test_packaging.py b/tests/test_packaging.py index f09e506..c8e531d 100644 --- a/tests/test_packaging.py +++ b/tests/test_packaging.py @@ -90,7 +90,7 @@ def test_root_readme_keeps_published_release_state_truthful() -> None: assert boundary["repository"]["source_of_truth"] is True assert boundary["legal"]["status"] == "published" assert "canonical editable source" in readme - assert "`v2.0.2` is not published yet" not in readme + assert "`v2.0.3` is not published yet" not in readme assert "releases/download/v2.0.2" in readme assert "6140d4f9baa9601e4adc9f75ef96eaf75e031a40767d76795fb08c412344a296" in readme @@ -148,7 +148,7 @@ def test_archive_provenance_identifies_version_and_source_hashes(tmp_path: Path) assert provenance == { "build_format_version": 3, "license_sha256": hashlib.sha256((REPOSITORY_ROOT / "LICENSE").read_bytes()).hexdigest(), - "plugin_version": "2.0.2", + "plugin_version": "2.0.3", "provider_id": "substrate_wiki", "source_commit": "unknown", "source_files": { @@ -237,8 +237,8 @@ def test_publish_release_creates_exact_current_and_immutable_aliases(tmp_path: P releases_path=releases_path, ) - assert release_archive == releases_path / "2.0.2" / "substrate_wiki.zip" - assert release_installer == releases_path / "2.0.2" / "install_hermes_plugin.py" + assert release_archive == releases_path / "2.0.3" / "substrate_wiki.zip" + assert release_installer == releases_path / "2.0.3" / "install_hermes_plugin.py" assert archive_path.read_bytes() == release_archive.read_bytes() == archive assert release_installer.read_bytes() == INSTALLER_PATH.read_bytes() assert builder.check_release( @@ -250,7 +250,7 @@ def test_publish_release_creates_exact_current_and_immutable_aliases(tmp_path: P def test_publish_release_refuses_to_replace_versioned_bytes(tmp_path: Path) -> None: builder = load_builder() releases_path = tmp_path / "releases" - versioned = releases_path / "2.0.2" / "substrate_wiki.zip" + versioned = releases_path / "2.0.3" / "substrate_wiki.zip" versioned.parent.mkdir(parents=True) versioned.write_bytes(b"different immutable bytes") archive_path = tmp_path / "current" / "substrate_wiki.zip" @@ -269,7 +269,7 @@ def test_publish_release_refuses_to_replace_versioned_bytes(tmp_path: Path) -> N def test_publish_release_preflights_both_immutable_artifacts(tmp_path: Path) -> None: builder = load_builder() releases_path = tmp_path / "releases" - versioned_installer = releases_path / "2.0.2" / "install_hermes_plugin.py" + versioned_installer = releases_path / "2.0.3" / "install_hermes_plugin.py" versioned_installer.parent.mkdir(parents=True) versioned_installer.write_bytes(b"conflicting immutable installer") archive_path = tmp_path / "current" / "substrate_wiki.zip" @@ -281,14 +281,14 @@ def test_publish_release_preflights_both_immutable_artifacts(tmp_path: Path) -> releases_path=releases_path, ) - assert not (releases_path / "2.0.2" / "substrate_wiki.zip").exists() + assert not (releases_path / "2.0.3" / "substrate_wiki.zip").exists() assert not archive_path.exists() def test_publish_release_rejects_symlinked_immutable_artifact(tmp_path: Path) -> None: builder = load_builder() releases_path = tmp_path / "releases" - release_directory = releases_path / "2.0.2" + release_directory = releases_path / "2.0.3" release_directory.mkdir(parents=True) target = tmp_path / "elsewhere.zip" archive = builder.build_archive_bytes(source_commit="a" * 40) @@ -328,7 +328,7 @@ def test_installer_verifies_and_atomically_upgrades_with_rollback(tmp_path: Path assert result["action"] == "upgraded" assert result["source_commit"] == "a" * 40 - assert "version: 2.0.2" in (existing / "plugin.yaml").read_text(encoding="utf-8") + assert "version: 2.0.3" in (existing / "plugin.yaml").read_text(encoding="utf-8") rollback = Path(result["rollback"]) assert "version: 1.0.0" in (rollback / "plugin.yaml").read_text(encoding="utf-8") assert checkpoint.read_bytes() == b"content-free-checkpoint" @@ -386,7 +386,7 @@ def test_plugin_swap_restores_previous_version_when_hardening_fails( ) def fail_hardening(target: Path) -> None: - assert "version: 2.0.2" in (target / "plugin.yaml").read_text(encoding="utf-8") + assert "version: 2.0.3" in (target / "plugin.yaml").read_text(encoding="utf-8") raise OSError("permission hardening failed") monkeypatch.setattr(installer, "_harden_plugin_permissions", fail_hardening) @@ -396,7 +396,7 @@ def fail_hardening(target: Path) -> None: assert "version: 1.3.0" in (existing / "plugin.yaml").read_text(encoding="utf-8") failed = list((hermes_home / "plugins").glob("substrate_wiki.failed-*")) assert len(failed) == 1 - assert "version: 2.0.2" in (failed[0] / "plugin.yaml").read_text(encoding="utf-8") + assert "version: 2.0.3" in (failed[0] / "plugin.yaml").read_text(encoding="utf-8") def test_check_archive_preserves_sha_provenance_without_environment( @@ -592,7 +592,7 @@ def fail_checked_reload( systemctl_calls.append((command, kwargs)) if kwargs.get("check") is True: assert command == ("systemctl", "--user", "daemon-reload") - assert "version: 2.0.2" in (existing / "plugin.yaml").read_text( + assert "version: 2.0.3" in (existing / "plugin.yaml").read_text( encoding="utf-8" ) plugin_rollbacks = list( @@ -624,7 +624,7 @@ def fail_checked_reload( assert (existing / "v13-sentinel.txt").read_text(encoding="utf-8") == "prior plugin" failed_plugins = list((hermes_home / "plugins").glob("substrate_wiki.failed-*")) assert len(failed_plugins) == 1 - assert "version: 2.0.2" in (failed_plugins[0] / "plugin.yaml").read_text( + assert "version: 2.0.3" in (failed_plugins[0] / "plugin.yaml").read_text( encoding="utf-8" ) assert not list((hermes_home / "plugins").glob("substrate_wiki.rollback-*"))