From f13cbdfebafd3034c4eb3e0a5eb8840d240dd70c Mon Sep 17 00:00:00 2001
From: Pavel Tkachyk
Date: Mon, 17 Aug 2026 16:11:16 -0400
Subject: [PATCH 1/3] Prepare resilient onboarding release v2.0.3
Signed-off-by: Pavel Tkachyk
---
CHANGELOG.md | 5 ++
COMPATIBILITY.md | 1 +
SECURITY.md | 2 +-
docs/extraction-manifest.json | 22 +++----
pyproject.toml | 2 +-
scripts/install_hermes_plugin.py | 2 +-
scripts/verify_public_plugin_candidate.py | 75 ++++++++++++++++++-----
src/substrate_wiki/client.py | 2 +-
src/substrate_wiki/onboarding.py | 2 +-
src/substrate_wiki/plugin.yaml | 2 +-
tests/test_entity_memory.py | 2 +-
tests/test_packaging.py | 26 ++++----
12 files changed, 96 insertions(+), 47 deletions(-)
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..a13fd2d 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": "bf66cf494def9e79d7a1ebee12f413e14928629dd21cdf96213615bb7334f46d",
"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..8311d43 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'}),
}
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-*"))
From 10319e4ed56db443364e497601e6f549fc61f7fc Mon Sep 17 00:00:00 2001
From: Pavel Tkachyk
Date: Mon, 17 Aug 2026 16:11:40 -0400
Subject: [PATCH 2/3] Seal v2.0.3 publication history
Signed-off-by: Pavel Tkachyk
---
docs/extraction-manifest.json | 2 +-
scripts/verify_public_plugin_candidate.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/extraction-manifest.json b/docs/extraction-manifest.json
index a13fd2d..737b919 100644
--- a/docs/extraction-manifest.json
+++ b/docs/extraction-manifest.json
@@ -353,7 +353,7 @@
{
"class": "build-and-install",
"destination": "scripts/verify_public_plugin_candidate.py",
- "destination_sha256": "bf66cf494def9e79d7a1ebee12f413e14928629dd21cdf96213615bb7334f46d",
+ "destination_sha256": "bb65b6dc45852e8b4ab573b1f87aecf07278761bb8e77b4113d5b9dfd9cd3e88",
"source": "scripts/verify_public_plugin_candidate.py",
"source_sha256": "4130935d530075fce1758e2e89bd5d973a722e2293b5b1058cfe0d17f326172b",
"transformation": "modified_for_standalone"
diff --git a/scripts/verify_public_plugin_candidate.py b/scripts/verify_public_plugin_candidate.py
index 8311d43..5cbb622 100644
--- a/scripts/verify_public_plugin_candidate.py
+++ b/scripts/verify_public_plugin_candidate.py
@@ -276,7 +276,7 @@
"4b444b2583fbdd340b17d279fd169103c57f87a56dece39988d784b311222920"
)
TRUSTED_HISTORICAL_BLOB_POLICY_SHA256 = (
- "2a94f0ce0443952df1c7be7eb1f12cf7aabad96a4e9ccf482acc0ae73d2606ae"
+ "85df3a1f9500e8900e449cc727d9538355a08de2380a816ef5d0f29e2982e3e9"
)
SCANNER_PATH = "scripts/verify_public_plugin_candidate.py"
DESTINATION_MANIFEST_PATH = "docs/extraction-manifest.json"
From 13b033ed5eb83f22987bd917d7a385571a2b453c Mon Sep 17 00:00:00 2001
From: Pavel Tkachyk
Date: Mon, 17 Aug 2026 16:17:36 -0400
Subject: [PATCH 3/3] Align v2.0.3 seal with active refs
Signed-off-by: Pavel Tkachyk
---
docs/extraction-manifest.json | 2 +-
scripts/verify_public_plugin_candidate.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/extraction-manifest.json b/docs/extraction-manifest.json
index 737b919..d876d1b 100644
--- a/docs/extraction-manifest.json
+++ b/docs/extraction-manifest.json
@@ -353,7 +353,7 @@
{
"class": "build-and-install",
"destination": "scripts/verify_public_plugin_candidate.py",
- "destination_sha256": "bb65b6dc45852e8b4ab573b1f87aecf07278761bb8e77b4113d5b9dfd9cd3e88",
+ "destination_sha256": "342076e02ac0d40620c859aa9f9af828110a0f818a862dd3bd75afff9222455c",
"source": "scripts/verify_public_plugin_candidate.py",
"source_sha256": "4130935d530075fce1758e2e89bd5d973a722e2293b5b1058cfe0d17f326172b",
"transformation": "modified_for_standalone"
diff --git a/scripts/verify_public_plugin_candidate.py b/scripts/verify_public_plugin_candidate.py
index 5cbb622..09d2bef 100644
--- a/scripts/verify_public_plugin_candidate.py
+++ b/scripts/verify_public_plugin_candidate.py
@@ -276,7 +276,7 @@
"4b444b2583fbdd340b17d279fd169103c57f87a56dece39988d784b311222920"
)
TRUSTED_HISTORICAL_BLOB_POLICY_SHA256 = (
- "85df3a1f9500e8900e449cc727d9538355a08de2380a816ef5d0f29e2982e3e9"
+ "7c56d7b749266ad9506b03d6ed5487efe6cc0b7a5bb7f38142348386b00a4968"
)
SCANNER_PATH = "scripts/verify_public_plugin_candidate.py"
DESTINATION_MANIFEST_PATH = "docs/extraction-manifest.json"