Admit reviewed receiver evidence batch 1

This commit is contained in:
codex 2026-10-17 03:39:27 +02:00
parent f8c7dfa2a9
commit 4b647dc32f
24 changed files with 2857 additions and 0 deletions

View file

@ -0,0 +1,30 @@
{
"schema": "statehub.conditional-T07-completion-overlay.v1",
"status": "prepared_not_applied_no_completion_claim",
"source_preimage": {
"path": "workplans/STATE-WP-0095-historical-source-receiving.md",
"sha256": "146caec8f37833d9cd0b509750e5131640725b5617f4b2eede989185e1869d5c"
},
"guards": [
"Actual normal cluster owner publication and central receiving status applied, with exact pushed commit receipt",
"RMGR-WP-0019-T06 actually done and source status/UUID projection verified",
"All three exact archived source SHA256 bytes remain equal to frozen manifest",
"All three historical parents/15 tasks full DB row hashes unchanged after normal receiving",
"No missing/unresolved/foreign-holder identity or retirement refusal hidden as success"
],
"edits_after_guards_only": [
{
"task_id": "STATE-WP-0095-T07",
"state_hub_task_id": "cfd1ea99-e75b-542e-a434-acefb62f3c7d",
"status": "done",
"evidence": "insert exact actual cluster commit/publication/receiving and preservation receipt refs at activation"
},
{
"workplan_id": "STATE-WP-0095",
"frontmatter_status": "finished",
"only_if": "all eight retained task IDs done and no unresolved retained work remains"
}
],
"preserve_all_IDs_UUIDs_history": true,
"normal_owner_commit_push_projection_required": true
}

View file

@ -0,0 +1,53 @@
{
"schema": "statehub.source-owner-followthrough.v1",
"status": "isolated_source_candidate_not_applied",
"base_revision": "086d79a2736e34b916bd7de3c8a65e2ea38a3d64",
"files": [
{
"path": "workplans/STATE-WP-0095-historical-source-receiving.md",
"base_sha256": "ef10359cc5974283b5bdbfc602adf14e3c24362adf00640698d2937c6538483a",
"candidate_sha256": "146caec8f37833d9cd0b509750e5131640725b5617f4b2eede989185e1869d5c",
"action": "modify"
},
{
"path": "Makefile",
"base_sha256": "5584ef94071044cca8215141ecd8aee8d6ae5cfe73e8f5719358c680eb8af0d4",
"candidate_sha256": "c5b70e8d4b8ae70380e0ade1c35f9e7fe80038de10080a4723b21ef07a98001e",
"action": "modify"
},
{
"path": "deploy/railiance/apps/charts/state-hub/Chart.yaml",
"base_sha256": "7fe02b390ffd2ba4ce6f82bd62df900826826401e31bdc2138dd5bf36b51a6ae",
"candidate_sha256": "61a989fcf277d05ffbececb7332ee3ca1d8f369c72f960ab1bc4dbc6815536b8",
"action": "modify"
},
{
"path": "deploy/railiance/apps/helm/state-hub-values.yaml",
"base_sha256": "f7cc0e9968598f6ce0faa0d56f62d6817845c3570c18b6ac39ed6561b952b197",
"candidate_sha256": "b51296fc937450f4ecf0ae43243262a631b551389c0d09c8bfe58fa10e2f3ee9",
"action": "modify"
},
{
"path": "docs/evidence/2026-10-17-cluster-historical-receiving-rollout.json",
"base_sha256": null,
"candidate_sha256": "db6cd6931c5763ff390e54c6eb9430aad2c4c7c9f42dd6652f436e549e6f6297",
"action": "add"
}
],
"patch_sha256": "sha256:3a2f8446262062a9183aff6f219fd6c300ed447624355cb778bb050cdb14a2b9",
"live_effects": false,
"new_runtime_image": false,
"T06": "done_actual_approved_verified_rollout",
"T07": "wait_until_actual_RMGR19T06_cluster_publication_and_preservation_return",
"T08": "done_source_readiness_preserved",
"generic_chart_default_tag": "empty_required_explicit_pin_unchanged",
"declared_production_image": "main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015",
"preserve_preexisting_dirty": [
".custodian-brief.md",
"WORK-RECORDS.md",
"docs/evidence/legacy-meter-weekly-review-20261014.json",
"docs/evidence/legacy-meter-weekly-review-20261015.json",
"docs/evidence/legacy-meter-weekly-review-20261016.json",
"docs/evidence/legacy-meter-weekly-review-20261017.json"
]
}

View file

@ -0,0 +1,199 @@
--- a/workplans/STATE-WP-0095-historical-source-receiving.md
+++ b/workplans/STATE-WP-0095-historical-source-receiving.md
@@ -199,27 +199,37 @@
```task
id: STATE-WP-0095-T06
-status: wait
+status: done
priority: high
depends_on: [STATE-WP-0095-T05]
-needs_human: true
-intervention_note: "Review exact committed source, CI image and owner deployment proposal before rollout."
+needs_human: false
+intervention_note: "Exact revision 4 approved and verified; no pending rollout intervention."
state_hub_task_id: "6af973b4-1964-5d4d-a2f8-8dbd4c01fda4"
```
-Use the normal CI image and Helm owner path after exact revision/image pin,
-fresh headroom and reviewed dry-run/diff. API/MCP currently run main-7d99282;
-Helm revision75 is the observed rollback baseline, requiring freshness before
-execution. No deployment assent exists for this successor. Verify delivered
-module hash, health, both readiness checks and expected migrated schema
-c4d5e6f7a8b9; verify exact
-three parents/15 tasks and rejection behavior without committed probe changes.
-Only the verified return unblocks RMGR-WP-0019-T05. The full current image also carries inbox-feed migration c4d5e6f7a8b9,
-although this receiver patch adds none. Helm rollback leaves that migration in
-place; unrelated migration acceptance and old-image compatibility need normal
-owner evidence before rollout approval. Do not run destructive default local
-DB tests; use only exact disposable DB containment. Costs and monetary risk are
-unknown pending exact rollout proposal; no zero-cost or autonomy grant is inferred.
+Completed 2026-10-17 on the founder's approval of exact revision 4 of stable
+proposal STATE-WP-0095-T06-cluster-frozen-receiving-r1 (decision
+ e3798419-8aa9-5425-8c08-20df6bd82a19; approved digest
+ sha256:63a02e8296d126c0158b7eb2c4ce79c7eeb6f8d6f61298d473c1065c7557ba32).
+The ordinary owner Helm path delivered revision 76, runtime source 51bc70d and
+exact image manifest sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015.
+API/MCP Ready and primary/railiance01 health report expected schema
+c4d5e6f7a8b9; delivered historical module sha256:e3c4bf965764b32a6df5d61c71272ee22569df9a9ddae2a3fc3df97ec554e0b2 matches.
+All three parent and 15 task full DB row hashes/columns/UUIDs remained unchanged.
+Five actual native probes passed: preserve, actual foreign-parent holder move,
+nonterminal task, nonterminal parent and missing task. Every probe rolled back;
+a fresh transaction confirmed complete row restoration. The foreign-holder
+case precisely verified historical binding refusal, not the separate global
+UUID-holder creation branch. No committed probe writes were made.
+
+Evidence: [verified receiving rollout](../docs/evidence/2026-10-17-cluster-historical-receiving-rollout.json).
+This actual receiving return satisfies the prerequisite of RMGR-WP-0019-T05.
+Normal cluster publication and its immutable-source/row return remain T07;
+source readiness and this deployment do not count that later acceptance twice.
+The additive inbox migration is retained; Helm rollback does not downgrade
+schema or stop an active migration Job. Original failures and exact-UID recovery
+limits remain in the referenced approved packet. No TLS recovery/native private
+admission or new autonomy/cost grant is inferred.
## Verify ordinary cluster receiving and immutable historical rows
@@ -256,6 +266,6 @@
Command-fixture tests verify both requests and preserve other dry-run phases.
Evidence and original refusal remain in the Custodian cluster historical
publication packet, revisions/r2 and r3-readiness. This is completed source
-preparation; T06 remains the explicit native rollout gate. The runtime image
+preparation; T06 separately records the verified actual rollout. The runtime image
continues to be main-51bc70d; Makefile/workplan changes do not create a new
application image or native acceptance.
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,7 @@
RAILIANCE_STATE_HUB_NAMESPACE ?= state-hub
RAILIANCE_STATE_HUB_CHART ?= deploy/railiance/apps/charts/state-hub
RAILIANCE_STATE_HUB_VALUES ?= deploy/railiance/apps/helm/state-hub-values.yaml
-RAILIANCE_STATE_HUB_IMAGE_TAG ?= main-0747b4d
+RAILIANCE_STATE_HUB_IMAGE_TAG ?= main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015
RAILIANCE_STATE_HUB_PLATFORM_DIR ?= deploy/railiance/platform
RAILIANCE_STATE_HUB_APP_MANIFESTS ?= deploy/railiance/apps/manifests
# Codex/WSL non-login shells may not source ~/.profile; keep uv discoverable.
--- a/deploy/railiance/apps/charts/state-hub/Chart.yaml
+++ b/deploy/railiance/apps/charts/state-hub/Chart.yaml
@@ -3,4 +3,4 @@
description: State Hub API service for private Railiance operation
type: application
version: 0.1.0
-appVersion: "main-5dd3df9"
+appVersion: "main-51bc70d"
--- a/deploy/railiance/apps/helm/state-hub-values.yaml
+++ b/deploy/railiance/apps/helm/state-hub-values.yaml
@@ -6,7 +6,7 @@
image:
repository: forgejo.coulomb.social/coulomb/state-hub
- tag: "main-0747b4d"
+ tag: "main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015"
ingress:
enabled: false
--- /dev/null
+++ b/docs/evidence/2026-10-17-cluster-historical-receiving-rollout.json
@@ -0,0 +1,102 @@
+{
+ "schema": "statehub.cluster-historical-receiving-delivered.v1",
+ "status": "verified",
+ "proposal_id": "STATE-WP-0095-T06-cluster-frozen-receiving-r1",
+ "approved_revision": 4,
+ "approved_digest_sha256": "sha256:63a02e8296d126c0158b7eb2c4ce79c7eeb6f8d6f61298d473c1065c7557ba32",
+ "approval_decision_id": "e3798419-8aa9-5425-8c08-20df6bd82a19",
+ "source_revision": "51bc70d1ff8f25450a8650d8e50caf053093a02e",
+ "readiness_source_revision": "086d79a2736e34b916bd7de3c8a65e2ea38a3d64",
+ "helm_revision": 76,
+ "image_reference": "forgejo.coulomb.social/coulomb/state-hub:main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015",
+ "expected_schema": "c4d5e6f7a8b9",
+ "health": {
+ "status": "ok",
+ "instance_role": "primary",
+ "instance_label": "railiance01",
+ "schema": {
+ "status": "ok",
+ "applied": "c4d5e6f7a8b9",
+ "expected": "c4d5e6f7a8b9"
+ }
+ },
+ "pods_ready": true,
+ "historical_module_sha256": "sha256:e3c4bf965764b32a6df5d61c71272ee22569df9a9ddae2a3fc3df97ec554e0b2",
+ "historical_preservation_verified": true,
+ "parents": 3,
+ "tasks": 15,
+ "full_historical_rows_equal": true,
+ "native_rollback_probes": {
+ "foreign-holder": {
+ "full_historical_rows_restored": true,
+ "historical_binding_refused": true,
+ "native_outcome_status": "refused",
+ "rolled_back": true,
+ "status": "passed"
+ },
+ "missing-task": {
+ "full_historical_rows_restored": true,
+ "historical_binding_refused": true,
+ "native_outcome_status": "refused",
+ "rolled_back": true,
+ "status": "passed"
+ },
+ "nonterminal-parent": {
+ "full_historical_rows_restored": true,
+ "historical_binding_refused": true,
+ "native_outcome_status": "refused",
+ "rolled_back": true,
+ "status": "passed"
+ },
+ "nonterminal-task": {
+ "full_historical_rows_restored": true,
+ "historical_binding_refused": true,
+ "native_outcome_status": "refused",
+ "rolled_back": true,
+ "status": "passed"
+ },
+ "preserve": {
+ "full_historical_rows_equal": true,
+ "full_historical_rows_restored": true,
+ "native_outcome_status": "applied",
+ "rolled_back": true,
+ "status": "passed"
+ }
+ },
+ "native_probe_commits": 0,
+ "fresh_transaction_full_rows_equal": true,
+ "source_receiving_gate_delivered": true,
+ "normal_cluster_publication_verified": false,
+ "registrar_admission_or_cluster_publication_not_inferred": true,
+ "authority_limits": [
+ "Rollback probe SQL effects rolled back; foreign-holder case is historical-binding refusal, not separate global holder branch.",
+ "Actual historical native receiving is verified; owner-native TLS/private admission remains unresolved.",
+ "Migration schema remains additive; Helm rollback does not downgrade schema or stop an active hook Job."
+ ],
+ "evidence": [
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/approval-decision.json",
+ "sha256": "sha256:b51554c004daa882f3709f67bdae7f2e939d611120383e61be04ec6969878134"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/helm-return.json",
+ "sha256": "sha256:61727deacf22d37da2d28cbaf04f5a3ddee0f9ff7571c77f1e85dba97963b572"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/delivered-runtime.json",
+ "sha256": "sha256:89681066e8fe139456163ebfafa3efe77ffae8534ea167219230b0a74367f129"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/native-refusal-probes.json",
+ "sha256": "sha256:673ccef965c820704dc310a1bf7832b583c30c3edfeb814528cf892555199172"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/before-full-row-hashes.json",
+ "sha256": "sha256:75cdd87eef79059ff3f484ed24ca69e285899cdf8d851cc4e472653c417eb0ee"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/after-full-row-hashes.json",
+ "sha256": "sha256:2156aad18f190384838c27e792706a156a7423391408afbc6107e6c28c5872d8"
+ }
+ ]
+}

View file

@ -0,0 +1,21 @@
{
"schema": "statehub-followthrough-source-validation.v1",
"patch_apply_check_exit": 0,
"direct_local_helm_render_exit": 0,
"all3runtime_images_match_promoted_digest": true,
"appVersion": "main-51bc70d",
"cluster_contacted": false,
"production_mutations": false,
"normal_make_render": {
"status": "refused_preexisting_canon_copy_drift",
"guard_not_bypassed": true,
"chart_copy_version": "1.0",
"canon_version": "1.1",
"differences": [
"category publication",
"purpose history",
"purpose participation"
],
"scope": "No classification copy edit/guard relaxation included in followthrough."
}
}

View file

@ -0,0 +1,30 @@
{
"schema": "statehub.conditional-T07-completion-overlay.v1",
"status": "prepared_not_applied_no_completion_claim",
"source_preimage": {
"path": "workplans/STATE-WP-0095-historical-source-receiving.md",
"sha256": "921716ab00993a79917ae5b4750e5b61ae22af4c4fd410b9ccf2be9ec54560df"
},
"guards": [
"Actual normal cluster owner publication and central receiving status applied, with exact pushed commit receipt",
"RMGR-WP-0019-T06 actually done and source status/UUID projection verified",
"All three exact archived source SHA256 bytes remain equal to frozen manifest",
"All three historical parents/15 tasks full DB row hashes unchanged after normal receiving",
"No missing/unresolved/foreign-holder identity or retirement refusal hidden as success"
],
"edits_after_guards_only": [
{
"task_id": "STATE-WP-0095-T07",
"state_hub_task_id": "cfd1ea99-e75b-542e-a434-acefb62f3c7d",
"status": "done",
"evidence": "insert exact actual cluster commit/publication/receiving and preservation receipt refs at activation"
},
{
"workplan_id": "STATE-WP-0095",
"frontmatter_status": "finished",
"only_if": "all eight retained task IDs done and no unresolved retained work remains"
}
],
"preserve_all_IDs_UUIDs_history": true,
"normal_owner_commit_push_projection_required": true
}

View file

@ -0,0 +1,28 @@
--- prior-chart-copy
+++ generated-canon-copy
@@ -13,7 +13,7 @@
# to match the standard version. CUST-WP-0050 T01.
standard: "Repo Classification Standard"
-version: "1.0"
+version: "1.1"
canon_id: "canon-repo-classification"
# category — exactly 1 required (§5)
@@ -24,6 +24,7 @@
- tooling
- product
- business
+ - publication
# domain / secondary_domains — primary exactly 1; secondaries 0..n (§6)
domains:
@@ -85,6 +86,8 @@
- traceability
- documentation
- decision-support
+ - history
+ - participation
platform_and_operations:
- platform
- deployment

View file

@ -0,0 +1,61 @@
{
"schema": "statehub.source-owner-followthrough.v1",
"status": "isolated_six_path_source_refinement_not_applied",
"base_revision": "086d79a2736e34b916bd7de3c8a65e2ea38a3d64",
"files": [
{
"path": "workplans/STATE-WP-0095-historical-source-receiving.md",
"action": "modify",
"base_sha256": "ef10359cc5974283b5bdbfc602adf14e3c24362adf00640698d2937c6538483a",
"candidate_sha256": "921716ab00993a79917ae5b4750e5b61ae22af4c4fd410b9ccf2be9ec54560df"
},
{
"path": "Makefile",
"action": "modify",
"base_sha256": "5584ef94071044cca8215141ecd8aee8d6ae5cfe73e8f5719358c680eb8af0d4",
"candidate_sha256": "c5b70e8d4b8ae70380e0ade1c35f9e7fe80038de10080a4723b21ef07a98001e"
},
{
"path": "deploy/railiance/apps/charts/state-hub/Chart.yaml",
"action": "modify",
"base_sha256": "7fe02b390ffd2ba4ce6f82bd62df900826826401e31bdc2138dd5bf36b51a6ae",
"candidate_sha256": "61a989fcf277d05ffbececb7332ee3ca1d8f369c72f960ab1bc4dbc6815536b8"
},
{
"path": "deploy/railiance/apps/helm/state-hub-values.yaml",
"action": "modify",
"base_sha256": "f7cc0e9968598f6ce0faa0d56f62d6817845c3570c18b6ac39ed6561b952b197",
"candidate_sha256": "b51296fc937450f4ecf0ae43243262a631b551389c0d09c8bfe58fa10e2f3ee9"
},
{
"path": "docs/evidence/2026-10-17-cluster-historical-receiving-rollout.json",
"action": "add",
"base_sha256": null,
"candidate_sha256": "db6cd6931c5763ff390e54c6eb9430aad2c4c7c9f42dd6652f436e549e6f6297"
},
{
"path": "deploy/railiance/apps/charts/state-hub/files/repo-classification.allowed.yaml",
"action": "modify",
"base_sha256": "9c36fbd5308b19695f9f05d86f8e315bbeccdd267575ce4e2fccf6fb2ea82e32",
"candidate_sha256": "f53c651bb4329c09c04c66f906e2462fe4339ab51ef0ec3da9f942c3016f5bed"
}
],
"patch_sha256": "sha256:12ee2ab6f4cc45ff8cfcb4c03ef84a7e207eff13fdaa194a7281734f5cbc4021",
"live_effects": false,
"new_runtime_image": false,
"T06": "done_actual_approved_verified_rollout",
"T07": "wait_until_actual_RMGR19T06_cluster_publication_and_preservation_return",
"T08": "done_source_readiness_preserved",
"generic_chart_default_tag": "empty_required_explicit_pin_unchanged",
"declared_production_image": "main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015",
"preserve_preexisting_dirty": [
".custodian-brief.md",
"WORK-RECORDS.md",
"docs/evidence/legacy-meter-weekly-review-20261014.json",
"docs/evidence/legacy-meter-weekly-review-20261015.json",
"docs/evidence/legacy-meter-weekly-review-20261016.json",
"docs/evidence/legacy-meter-weekly-review-20261017.json"
],
"revision": 2,
"source_repair": "Existing generator refreshes chart classification copy to unchanged authoritative canon; no live ConfigMap apply or new runtime image."
}

View file

@ -0,0 +1,238 @@
--- a/workplans/STATE-WP-0095-historical-source-receiving.md
+++ b/workplans/STATE-WP-0095-historical-source-receiving.md
@@ -199,27 +199,37 @@
```task
id: STATE-WP-0095-T06
-status: wait
+status: done
priority: high
depends_on: [STATE-WP-0095-T05]
-needs_human: true
-intervention_note: "Review exact committed source, CI image and owner deployment proposal before rollout."
+needs_human: false
+intervention_note: "Exact revision 4 approved and verified; no pending rollout intervention."
state_hub_task_id: "6af973b4-1964-5d4d-a2f8-8dbd4c01fda4"
```
-Use the normal CI image and Helm owner path after exact revision/image pin,
-fresh headroom and reviewed dry-run/diff. API/MCP currently run main-7d99282;
-Helm revision75 is the observed rollback baseline, requiring freshness before
-execution. No deployment assent exists for this successor. Verify delivered
-module hash, health, both readiness checks and expected migrated schema
-c4d5e6f7a8b9; verify exact
-three parents/15 tasks and rejection behavior without committed probe changes.
-Only the verified return unblocks RMGR-WP-0019-T05. The full current image also carries inbox-feed migration c4d5e6f7a8b9,
-although this receiver patch adds none. Helm rollback leaves that migration in
-place; unrelated migration acceptance and old-image compatibility need normal
-owner evidence before rollout approval. Do not run destructive default local
-DB tests; use only exact disposable DB containment. Costs and monetary risk are
-unknown pending exact rollout proposal; no zero-cost or autonomy grant is inferred.
+Completed 2026-10-17 on the founder's approval of exact revision 4 of stable
+proposal STATE-WP-0095-T06-cluster-frozen-receiving-r1 (decision
+ e3798419-8aa9-5425-8c08-20df6bd82a19; approved digest
+ sha256:63a02e8296d126c0158b7eb2c4ce79c7eeb6f8d6f61298d473c1065c7557ba32).
+The ordinary owner Helm path delivered revision 76, runtime source 51bc70d and
+exact image manifest sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015.
+API/MCP Ready and primary/railiance01 health report expected schema
+c4d5e6f7a8b9; delivered historical module sha256:e3c4bf965764b32a6df5d61c71272ee22569df9a9ddae2a3fc3df97ec554e0b2 matches.
+All three parent and 15 task full DB row hashes/columns/UUIDs remained unchanged.
+Five actual native probes passed: preserve, actual foreign-parent holder move,
+nonterminal task, nonterminal parent and missing task. Every probe rolled back;
+a fresh transaction confirmed complete row restoration. The foreign-holder
+case precisely verified historical binding refusal, not the separate global
+UUID-holder creation branch. No committed probe writes were made.
+
+Evidence: [verified receiving rollout](../docs/evidence/2026-10-17-cluster-historical-receiving-rollout.json).
+This actual receiving return satisfies the prerequisite of RMGR-WP-0019-T05.
+Normal cluster publication and its immutable-source/row return remain T07;
+source readiness and this deployment do not count that later acceptance twice.
+The additive inbox migration is retained; Helm rollback does not downgrade
+schema or stop an active migration Job. Original failures and exact-UID recovery
+limits remain in the referenced approved packet. No TLS recovery/native private
+admission or new autonomy/cost grant is inferred.
## Verify ordinary cluster receiving and immutable historical rows
@@ -256,6 +266,17 @@
Command-fixture tests verify both requests and preserve other dry-run phases.
Evidence and original refusal remain in the Custodian cluster historical
publication packet, revisions/r2 and r3-readiness. This is completed source
-preparation; T06 remains the explicit native rollout gate. The runtime image
+preparation; T06 separately records the verified actual rollout. The runtime image
continues to be main-51bc70d; Makefile/workplan changes do not create a new
application image or native acceptance.
+
+
+T08 source repair refinement, 2026-10-17: the required normal Make render
+correctly refused a pre-existing generated chart classification copy at v1.0
+after authoritative canon reached v1.1. Regenerated only through the existing
+scripts/sync_classification_allowed.py, preserving canon and strict check.
+The exact source diff is version 1.0→1.1 plus publication/history/participation.
+Normal Make rendering and the focused classification check passed in an
+isolated owner-layout copy. The original refusal is retained in the Custodian
+owner-followthrough-r1 validation receipt. This is source repair only: no live
+ConfigMap apply, new runtime image, policy relaxation or T07 completion.
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,7 @@
RAILIANCE_STATE_HUB_NAMESPACE ?= state-hub
RAILIANCE_STATE_HUB_CHART ?= deploy/railiance/apps/charts/state-hub
RAILIANCE_STATE_HUB_VALUES ?= deploy/railiance/apps/helm/state-hub-values.yaml
-RAILIANCE_STATE_HUB_IMAGE_TAG ?= main-0747b4d
+RAILIANCE_STATE_HUB_IMAGE_TAG ?= main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015
RAILIANCE_STATE_HUB_PLATFORM_DIR ?= deploy/railiance/platform
RAILIANCE_STATE_HUB_APP_MANIFESTS ?= deploy/railiance/apps/manifests
# Codex/WSL non-login shells may not source ~/.profile; keep uv discoverable.
--- a/deploy/railiance/apps/charts/state-hub/Chart.yaml
+++ b/deploy/railiance/apps/charts/state-hub/Chart.yaml
@@ -3,4 +3,4 @@
description: State Hub API service for private Railiance operation
type: application
version: 0.1.0
-appVersion: "main-5dd3df9"
+appVersion: "main-51bc70d"
--- a/deploy/railiance/apps/helm/state-hub-values.yaml
+++ b/deploy/railiance/apps/helm/state-hub-values.yaml
@@ -6,7 +6,7 @@
image:
repository: forgejo.coulomb.social/coulomb/state-hub
- tag: "main-0747b4d"
+ tag: "main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015"
ingress:
enabled: false
--- /dev/null
+++ b/docs/evidence/2026-10-17-cluster-historical-receiving-rollout.json
@@ -0,0 +1,102 @@
+{
+ "schema": "statehub.cluster-historical-receiving-delivered.v1",
+ "status": "verified",
+ "proposal_id": "STATE-WP-0095-T06-cluster-frozen-receiving-r1",
+ "approved_revision": 4,
+ "approved_digest_sha256": "sha256:63a02e8296d126c0158b7eb2c4ce79c7eeb6f8d6f61298d473c1065c7557ba32",
+ "approval_decision_id": "e3798419-8aa9-5425-8c08-20df6bd82a19",
+ "source_revision": "51bc70d1ff8f25450a8650d8e50caf053093a02e",
+ "readiness_source_revision": "086d79a2736e34b916bd7de3c8a65e2ea38a3d64",
+ "helm_revision": 76,
+ "image_reference": "forgejo.coulomb.social/coulomb/state-hub:main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015",
+ "expected_schema": "c4d5e6f7a8b9",
+ "health": {
+ "status": "ok",
+ "instance_role": "primary",
+ "instance_label": "railiance01",
+ "schema": {
+ "status": "ok",
+ "applied": "c4d5e6f7a8b9",
+ "expected": "c4d5e6f7a8b9"
+ }
+ },
+ "pods_ready": true,
+ "historical_module_sha256": "sha256:e3c4bf965764b32a6df5d61c71272ee22569df9a9ddae2a3fc3df97ec554e0b2",
+ "historical_preservation_verified": true,
+ "parents": 3,
+ "tasks": 15,
+ "full_historical_rows_equal": true,
+ "native_rollback_probes": {
+ "foreign-holder": {
+ "full_historical_rows_restored": true,
+ "historical_binding_refused": true,
+ "native_outcome_status": "refused",
+ "rolled_back": true,
+ "status": "passed"
+ },
+ "missing-task": {
+ "full_historical_rows_restored": true,
+ "historical_binding_refused": true,
+ "native_outcome_status": "refused",
+ "rolled_back": true,
+ "status": "passed"
+ },
+ "nonterminal-parent": {
+ "full_historical_rows_restored": true,
+ "historical_binding_refused": true,
+ "native_outcome_status": "refused",
+ "rolled_back": true,
+ "status": "passed"
+ },
+ "nonterminal-task": {
+ "full_historical_rows_restored": true,
+ "historical_binding_refused": true,
+ "native_outcome_status": "refused",
+ "rolled_back": true,
+ "status": "passed"
+ },
+ "preserve": {
+ "full_historical_rows_equal": true,
+ "full_historical_rows_restored": true,
+ "native_outcome_status": "applied",
+ "rolled_back": true,
+ "status": "passed"
+ }
+ },
+ "native_probe_commits": 0,
+ "fresh_transaction_full_rows_equal": true,
+ "source_receiving_gate_delivered": true,
+ "normal_cluster_publication_verified": false,
+ "registrar_admission_or_cluster_publication_not_inferred": true,
+ "authority_limits": [
+ "Rollback probe SQL effects rolled back; foreign-holder case is historical-binding refusal, not separate global holder branch.",
+ "Actual historical native receiving is verified; owner-native TLS/private admission remains unresolved.",
+ "Migration schema remains additive; Helm rollback does not downgrade schema or stop an active hook Job."
+ ],
+ "evidence": [
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/approval-decision.json",
+ "sha256": "sha256:b51554c004daa882f3709f67bdae7f2e939d611120383e61be04ec6969878134"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/helm-return.json",
+ "sha256": "sha256:61727deacf22d37da2d28cbaf04f5a3ddee0f9ff7571c77f1e85dba97963b572"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/delivered-runtime.json",
+ "sha256": "sha256:89681066e8fe139456163ebfafa3efe77ffae8534ea167219230b0a74367f129"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/native-refusal-probes.json",
+ "sha256": "sha256:673ccef965c820704dc310a1bf7832b583c30c3edfeb814528cf892555199172"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/before-full-row-hashes.json",
+ "sha256": "sha256:75cdd87eef79059ff3f484ed24ca69e285899cdf8d851cc4e472653c417eb0ee"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/after-full-row-hashes.json",
+ "sha256": "sha256:2156aad18f190384838c27e792706a156a7423391408afbc6107e6c28c5872d8"
+ }
+ ]
+}
--- a/deploy/railiance/apps/charts/state-hub/files/repo-classification.allowed.yaml
+++ b/deploy/railiance/apps/charts/state-hub/files/repo-classification.allowed.yaml
@@ -13,7 +13,7 @@
# to match the standard version. CUST-WP-0050 T01.
standard: "Repo Classification Standard"
-version: "1.0"
+version: "1.1"
canon_id: "canon-repo-classification"
# category — exactly 1 required (§5)
@@ -24,6 +24,7 @@
- tooling
- product
- business
+ - publication
# domain / secondary_domains — primary exactly 1; secondaries 0..n (§6)
domains:
@@ -85,6 +86,8 @@
- traceability
- documentation
- decision-support
+ - history
+ - participation
platform_and_operations:
- platform
- deployment

View file

@ -0,0 +1,25 @@
{
"schema": "statehub-source-refinement-validation.v1",
"normal_owner_make_render_exit": 0,
"focused_classification_check_exit": 0,
"sync_script_sha256": "sha256:b1c06691843d15ac4e82c0eba59d566c754aa5163337ed4ee8644c3dc9283bcd",
"canon_sha256": "sha256:c0e0452bb08fe1700f2672b5b019da08c3d70737fa5b00bdfeab14ef32cb6477",
"exact_copy_diff": {
"removed": [
"version: \"1.0\""
],
"added": [
"version: \"1.1\"",
" - publication",
" - history",
" - participation"
]
},
"all3runtime_images_exact_promoted_digest": true,
"original_failed_render_receipt": "../owner-followthrough-r1/validation.json",
"canon_edited": false,
"guard_changed": false,
"cluster_contacted": false,
"new_runtime_image": false,
"canonical_mutations": false
}

View file

@ -0,0 +1,17 @@
# Immutable ordinary-binding repair, source revision 3
Canonical base: 086d79a2736e34b916bd7de3c8a65e2ea38a3d64. The ten-path patch includes the previously reviewed six-path followthrough and four guard/test paths. Originals and the actual failed publication receipt remain untouched.
The approved revision-4 receiving rollout preserved all 18 rows at its verification stage. A later ordinary Cluster publication changed exactly three parent full-row hashes; all 15 task hashes remained unchanged. The binding endpoint assigns a new backing sync timestamp unconditionally, providing a concrete mutation path. Hash-only snapshots do not establish the exact changed columns or supply original preimages. This patch does not restore them.
Only the three existing task-bound railiance-cluster registry entries receive this guard. The CLI requires all three scan members, validates their exact source bytes and metadata, and omits valid immutable bindings before contacting any receiver. A refused batch becomes C-44 FAIL and returns a nonzero exit even in remote-all mode. The API independently validates all request metadata before any assignment, locks and refreshes parent/task rows, checks actual repo ownership and existing terminal required-bound tasks, and skips every setter for valid frozen rows. Additional children retain existing Forge receiving semantics; no new child-set invariant is introduced. The ordinary binding response retains updated/received and adds preserved. Frozen-only requests perform no commit or cache invalidation. Existing get_session uses an async-session context manager, which closes the read transaction and releases locks on exit.
Validation: 40 isolated tests pass with --noconftest. Tests extract actual source functions/classes without importing owner app configuration or opening a DB. They verify all three exact sources, missing scan member/UUID, changed source and metadata, foreign names, complete mixed-batch refusal, API ownership and terminal-state refusal, no frozen setters/commit, ordinary compatibility, lock/refresh query intent and actual CLI exit criteria. The full tracked chart plus overlay passes normal Make render/classification check: eight objects, three unchanged digest-qualified images. No concurrency/native DB behavior is claimed by these source tests.
T06 remains done for the approved delivery at its verification stage; T07 remains wait for failed whole-pipeline preservation. T09 is progress until source publication. T10 is wait/needs_human for a separate exact new runtime approval. New task UUIDs are intentionally unallocated pending root review and normal managed allocation. Preserve the original dirty brief/index/weekly evidence and index state.
## Next runtime proposal preparation
After reviewed source integration and normal publication, obtain normal CI status and resolve the resulting exact immutable image manifest. Inspect selected delivered source hashes, including historical_projection.py, workstreams.py and the installed closure; do not reuse the prior main-51bc70d runtime for the new API guard. Prepare digest-qualified packaged-chart reuse-values server dry-run, compare intended object identities/spec hashes, collect fresh release/headroom/schema and full-row hash baseline, then record a new scoped proposal and supervisor disposition. Current known prior baseline was Helm 76/schema c4d5e6f7a8b9; refresh rather than assume it remains current. CI revision, new image digest, final chart package hash and exact execution digest remain unresolved until publication.
No new rollout is authorized by the prior revision-4 approval. A new rollout must retain migration closure and rollback limits: Helm rollback does not downgrade schema, and timeout/atomic does not necessarily stop an active hook Job. Cancellation may address only the exact owned trial Job UID/image after identity verification. Unknown migration recovery or connection/lock quiescence stops for attended owner handling; no credential grant or invented DB-session mapping. EUR cost and risk are unknown until reviewed; no bounded-outage assertion. New baseline preservation and rollback-only refusal probes prove the new stage, never restoration of the unavailable original parent preimages. T07/RMGR19T06 cannot close on source publication alone.

View file

@ -0,0 +1,77 @@
{
"schema": "statehub.source-owner-followthrough.v1",
"revision": 3,
"status": "isolated_source_candidate_not_published",
"base_revision": "086d79a2736e34b916bd7de3c8a65e2ea38a3d64",
"files": [
{
"path": "workplans/STATE-WP-0095-historical-source-receiving.md",
"action": "modify",
"base_sha256": "ef10359cc5974283b5bdbfc602adf14e3c24362adf00640698d2937c6538483a",
"candidate_sha256": "287001ec92d5a9b851b0d70e56747730a9bb150fcda09e97d6e6e9ddf39c7b5c"
},
{
"path": "Makefile",
"action": "modify",
"base_sha256": "5584ef94071044cca8215141ecd8aee8d6ae5cfe73e8f5719358c680eb8af0d4",
"candidate_sha256": "c5b70e8d4b8ae70380e0ade1c35f9e7fe80038de10080a4723b21ef07a98001e"
},
{
"path": "deploy/railiance/apps/charts/state-hub/Chart.yaml",
"action": "modify",
"base_sha256": "7fe02b390ffd2ba4ce6f82bd62df900826826401e31bdc2138dd5bf36b51a6ae",
"candidate_sha256": "61a989fcf277d05ffbececb7332ee3ca1d8f369c72f960ab1bc4dbc6815536b8"
},
{
"path": "deploy/railiance/apps/helm/state-hub-values.yaml",
"action": "modify",
"base_sha256": "f7cc0e9968598f6ce0faa0d56f62d6817845c3570c18b6ac39ed6561b952b197",
"candidate_sha256": "b51296fc937450f4ecf0ae43243262a631b551389c0d09c8bfe58fa10e2f3ee9"
},
{
"path": "docs/evidence/2026-10-17-cluster-historical-receiving-rollout.json",
"action": "add",
"base_sha256": null,
"candidate_sha256": "fb0b59ba3558e084fb4ae2c36c556f3cfbb2a889f6fd86d4e803c52f483b26fd"
},
{
"path": "deploy/railiance/apps/charts/state-hub/files/repo-classification.allowed.yaml",
"action": "modify",
"base_sha256": "9c36fbd5308b19695f9f05d86f8e315bbeccdd267575ce4e2fccf6fb2ea82e32",
"candidate_sha256": "f53c651bb4329c09c04c66f906e2462fe4339ab51ef0ec3da9f942c3016f5bed"
},
{
"path": "api/services/historical_projection.py",
"action": "modify",
"base_sha256": "e3c4bf965764b32a6df5d61c71272ee22569df9a9ddae2a3fc3df97ec554e0b2",
"candidate_sha256": "e24e57c58f4aaa4ec9d9bf088009ac69006e53f75b9477ee53dc26c4e850b975"
},
{
"path": "api/routers/workstreams.py",
"action": "modify",
"base_sha256": "61e7a0ab638b155e9e0b2a72fe8e0ddd2edf5bc6c2d3ca7f6c3ff51e8868910b",
"candidate_sha256": "9c9bf4376dd323b52109912bb3f615949e89ac02c8b88b4720f569f8565ad1d6"
},
{
"path": "scripts/consistency_check.py",
"action": "modify",
"base_sha256": "1827921c79b476bee823c2ddf9896aeb83f3efca6548427e17b3244216bbc6b5",
"candidate_sha256": "ae96d6260f6c88d35bc2d3f22f2a6061367621fd0416387d25d8683c8e804e06"
},
{
"path": "tests/test_frozen_binding_guard_isolated.py",
"action": "add",
"base_sha256": null,
"candidate_sha256": "78eb62178fdecf9b5f6c40d398f8c44e93ff740abaf86557ab51ceb708d2ed65"
}
],
"patch_sha256": "sha256:e9bc2ba9f81b6290945b6de6e0a00ba4c995478b17da786605f5a0466e300b3d",
"T06": "done_verified_approved_rollout_at_stage",
"T07": "wait_actual_later_parent_row_preservation_failure",
"T08": "done_source_repair_pending_publication",
"T09": "progress_until_source_published",
"T10": "wait_needs_human_new_runtime",
"live_effects": false,
"original_parent_preimages": "unknown_do_not_restore_guesses",
"managed_UUIDs": "T09_T10_not_allocated_pending_review"
}

View file

@ -0,0 +1,650 @@
--- a/workplans/STATE-WP-0095-historical-source-receiving.md
+++ b/workplans/STATE-WP-0095-historical-source-receiving.md
@@ -199,27 +199,37 @@
```task
id: STATE-WP-0095-T06
-status: wait
+status: done
priority: high
depends_on: [STATE-WP-0095-T05]
-needs_human: true
-intervention_note: "Review exact committed source, CI image and owner deployment proposal before rollout."
+needs_human: false
+intervention_note: "Exact revision 4 approved and verified; no pending rollout intervention."
state_hub_task_id: "6af973b4-1964-5d4d-a2f8-8dbd4c01fda4"
```
-Use the normal CI image and Helm owner path after exact revision/image pin,
-fresh headroom and reviewed dry-run/diff. API/MCP currently run main-7d99282;
-Helm revision75 is the observed rollback baseline, requiring freshness before
-execution. No deployment assent exists for this successor. Verify delivered
-module hash, health, both readiness checks and expected migrated schema
-c4d5e6f7a8b9; verify exact
-three parents/15 tasks and rejection behavior without committed probe changes.
-Only the verified return unblocks RMGR-WP-0019-T05. The full current image also carries inbox-feed migration c4d5e6f7a8b9,
-although this receiver patch adds none. Helm rollback leaves that migration in
-place; unrelated migration acceptance and old-image compatibility need normal
-owner evidence before rollout approval. Do not run destructive default local
-DB tests; use only exact disposable DB containment. Costs and monetary risk are
-unknown pending exact rollout proposal; no zero-cost or autonomy grant is inferred.
+Completed 2026-10-17 on the founder's approval of exact revision 4 of stable
+proposal STATE-WP-0095-T06-cluster-frozen-receiving-r1 (decision
+ e3798419-8aa9-5425-8c08-20df6bd82a19; approved digest
+ sha256:63a02e8296d126c0158b7eb2c4ce79c7eeb6f8d6f61298d473c1065c7557ba32).
+The ordinary owner Helm path delivered revision 76, runtime source 51bc70d and
+exact image manifest sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015.
+API/MCP Ready and primary/railiance01 health report expected schema
+c4d5e6f7a8b9; delivered historical module sha256:e3c4bf965764b32a6df5d61c71272ee22569df9a9ddae2a3fc3df97ec554e0b2 matches.
+All three parent and 15 task full DB row hashes/columns/UUIDs remained unchanged.
+Five actual native probes passed: preserve, actual foreign-parent holder move,
+nonterminal task, nonterminal parent and missing task. Every probe rolled back;
+a fresh transaction confirmed complete row restoration. The foreign-holder
+case precisely verified historical binding refusal, not the separate global
+UUID-holder creation branch. No committed probe writes were made.
+
+Evidence: [verified receiving rollout](../docs/evidence/2026-10-17-cluster-historical-receiving-rollout.json).
+This actual receiving return satisfies the prerequisite of RMGR-WP-0019-T05.
+Normal cluster publication and its immutable-source/row return remain T07;
+source readiness and this deployment do not count that later acceptance twice.
+The additive inbox migration is retained; Helm rollback does not downgrade
+schema or stop an active migration Job. Original failures and exact-UID recovery
+limits remain in the referenced approved packet. No TLS recovery/native private
+admission or new autonomy/cost grant is inferred.
## Verify ordinary cluster receiving and immutable historical rows
@@ -256,6 +266,70 @@
Command-fixture tests verify both requests and preserve other dry-run phases.
Evidence and original refusal remain in the Custodian cluster historical
publication packet, revisions/r2 and r3-readiness. This is completed source
-preparation; T06 remains the explicit native rollout gate. The runtime image
+preparation; T06 separately records the verified actual rollout. The runtime image
continues to be main-51bc70d; Makefile/workplan changes do not create a new
application image or native acceptance.
+
+
+T08 source repair refinement, 2026-10-17: the required normal Make render
+correctly refused a pre-existing generated chart classification copy at v1.0
+after authoritative canon reached v1.1. Regenerated only through the existing
+scripts/sync_classification_allowed.py, preserving canon and strict check.
+The exact source diff is version 1.0→1.1 plus publication/history/participation.
+Normal Make rendering and the focused classification check passed in an
+isolated owner-layout copy. The original refusal is retained in the Custodian
+owner-followthrough-r1 validation receipt. This is source repair only: no live
+ConfigMap apply, new runtime image, policy relaxation or T07 completion.
+
+
+T07 actual ordinary-publication return, 2026-10-17: cluster owner deep consistency
+and normal fast projection completed, exact archived source bytes and all 15
+historical task full-row hashes remained unchanged. The later full-row check
+FAILED for exactly the three parent rows. The earlier approved T06 rollout had
+preserved all 18 rows at its verification stage; that success is not the later
+whole-pipeline acceptance. The original snapshots retain hashes only, so exact
+changed columns and original field preimages are not established. No guessed
+DB repair or claim that all 18 original row hashes are restored is authorized.
+T07 and RMGR-WP-0019-T06 remain waiting pending T09/T10 and a reviewed return.
+
+## Guard immutable historical index bindings in CLI and receiving API
+
+```task
+id: STATE-WP-0095-T09
+status: progress
+priority: high
+depends_on: [STATE-WP-0095-T06]
+```
+
+Prepare and publish the narrowly scoped ordinary-binding repair for the three
+existing task-bound Cluster registry entries. The CLI verifies exact frozen
+source metadata/bytes and omits those bindings, including on old receivers.
+The API independently validates the complete batch before any mutation, checks
+actual repo ownership and the existing terminal parent/task closure, and
+preserves valid frozen rows without field or timestamp assignments. Invalid
+metadata, missing/foreign/nonterminal rows refuse the batch. Unknown ordinary
+bindings retain existing behavior; historical IDs, UUIDs and source bytes stay
+unchanged. Isolated regression tests and normal owner rendering are required.
+Mark done only after reviewed source publication and normal owner projection;
+source preparation does not establish delivered runtime or T07 acceptance.
+
+## Deliver and verify the ordinary-binding guard successor
+
+```task
+id: STATE-WP-0095-T10
+status: wait
+priority: high
+depends_on: [STATE-WP-0095-T09]
+needs_human: true
+intervention_note: "New API runtime requires its own exact image, reviewed preflight and scoped rollout approval; prior revision-4 approval does not cover it."
+```
+
+After source publication, resolve normal CI status and immutable image identity,
+verify delivered guard source closure, prepare exact chart/reuse-values dry-run,
+full closure/headroom/schema baseline and conditional recovery. Retain additive
+migration and active-Job cancellation limitations; no Helm schema downgrade or
+bounded-outage claim. Obtain scoped approval before live rollout. Then verify
+new full-row baseline preservation and actual rollback-only refusal probes,
+and only then return to T07 ordinary-pipeline acceptance. This prevents future
+binding mutation; it does not reconstruct or restore original parent hashes.
+No production changes, new autonomy or credential authority are granted here.
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,7 @@
RAILIANCE_STATE_HUB_NAMESPACE ?= state-hub
RAILIANCE_STATE_HUB_CHART ?= deploy/railiance/apps/charts/state-hub
RAILIANCE_STATE_HUB_VALUES ?= deploy/railiance/apps/helm/state-hub-values.yaml
-RAILIANCE_STATE_HUB_IMAGE_TAG ?= main-0747b4d
+RAILIANCE_STATE_HUB_IMAGE_TAG ?= main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015
RAILIANCE_STATE_HUB_PLATFORM_DIR ?= deploy/railiance/platform
RAILIANCE_STATE_HUB_APP_MANIFESTS ?= deploy/railiance/apps/manifests
# Codex/WSL non-login shells may not source ~/.profile; keep uv discoverable.
--- a/deploy/railiance/apps/charts/state-hub/Chart.yaml
+++ b/deploy/railiance/apps/charts/state-hub/Chart.yaml
@@ -3,4 +3,4 @@
description: State Hub API service for private Railiance operation
type: application
version: 0.1.0
-appVersion: "main-5dd3df9"
+appVersion: "main-51bc70d"
--- a/deploy/railiance/apps/helm/state-hub-values.yaml
+++ b/deploy/railiance/apps/helm/state-hub-values.yaml
@@ -6,7 +6,7 @@
image:
repository: forgejo.coulomb.social/coulomb/state-hub
- tag: "main-0747b4d"
+ tag: "main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015"
ingress:
enabled: false
--- /dev/null
+++ b/docs/evidence/2026-10-17-cluster-historical-receiving-rollout.json
@@ -0,0 +1,116 @@
+{
+ "schema": "statehub.cluster-historical-receiving-delivered.v1",
+ "status": "verified",
+ "proposal_id": "STATE-WP-0095-T06-cluster-frozen-receiving-r1",
+ "approved_revision": 4,
+ "approved_digest_sha256": "sha256:63a02e8296d126c0158b7eb2c4ce79c7eeb6f8d6f61298d473c1065c7557ba32",
+ "approval_decision_id": "e3798419-8aa9-5425-8c08-20df6bd82a19",
+ "source_revision": "51bc70d1ff8f25450a8650d8e50caf053093a02e",
+ "readiness_source_revision": "086d79a2736e34b916bd7de3c8a65e2ea38a3d64",
+ "helm_revision": 76,
+ "image_reference": "forgejo.coulomb.social/coulomb/state-hub:main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015",
+ "expected_schema": "c4d5e6f7a8b9",
+ "health": {
+ "status": "ok",
+ "instance_role": "primary",
+ "instance_label": "railiance01",
+ "schema": {
+ "status": "ok",
+ "applied": "c4d5e6f7a8b9",
+ "expected": "c4d5e6f7a8b9"
+ }
+ },
+ "pods_ready": true,
+ "historical_module_sha256": "sha256:e3c4bf965764b32a6df5d61c71272ee22569df9a9ddae2a3fc3df97ec554e0b2",
+ "historical_preservation_verified": true,
+ "parents": 3,
+ "tasks": 15,
+ "full_historical_rows_equal": true,
+ "native_rollback_probes": {
+ "foreign-holder": {
+ "full_historical_rows_restored": true,
+ "historical_binding_refused": true,
+ "native_outcome_status": "refused",
+ "rolled_back": true,
+ "status": "passed"
+ },
+ "missing-task": {
+ "full_historical_rows_restored": true,
+ "historical_binding_refused": true,
+ "native_outcome_status": "refused",
+ "rolled_back": true,
+ "status": "passed"
+ },
+ "nonterminal-parent": {
+ "full_historical_rows_restored": true,
+ "historical_binding_refused": true,
+ "native_outcome_status": "refused",
+ "rolled_back": true,
+ "status": "passed"
+ },
+ "nonterminal-task": {
+ "full_historical_rows_restored": true,
+ "historical_binding_refused": true,
+ "native_outcome_status": "refused",
+ "rolled_back": true,
+ "status": "passed"
+ },
+ "preserve": {
+ "full_historical_rows_equal": true,
+ "full_historical_rows_restored": true,
+ "native_outcome_status": "applied",
+ "rolled_back": true,
+ "status": "passed"
+ }
+ },
+ "native_probe_commits": 0,
+ "fresh_transaction_full_rows_equal": true,
+ "source_receiving_gate_delivered": true,
+ "normal_cluster_publication_verified": false,
+ "registrar_admission_or_cluster_publication_not_inferred": true,
+ "authority_limits": [
+ "Rollback probe SQL effects rolled back; foreign-holder case is historical-binding refusal, not separate global holder branch.",
+ "Actual historical native receiving is verified; owner-native TLS/private admission remains unresolved.",
+ "Migration schema remains additive; Helm rollback does not downgrade schema or stop an active hook Job."
+ ],
+ "evidence": [
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/approval-decision.json",
+ "sha256": "sha256:b51554c004daa882f3709f67bdae7f2e939d611120383e61be04ec6969878134"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/helm-return.json",
+ "sha256": "sha256:61727deacf22d37da2d28cbaf04f5a3ddee0f9ff7571c77f1e85dba97963b572"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/delivered-runtime.json",
+ "sha256": "sha256:89681066e8fe139456163ebfafa3efe77ffae8534ea167219230b0a74367f129"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/native-refusal-probes.json",
+ "sha256": "sha256:673ccef965c820704dc310a1bf7832b583c30c3edfeb814528cf892555199172"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/before-full-row-hashes.json",
+ "sha256": "sha256:75cdd87eef79059ff3f484ed24ca69e285899cdf8d851cc4e472653c417eb0ee"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/after-full-row-hashes.json",
+ "sha256": "sha256:2156aad18f190384838c27e792706a156a7423391408afbc6107e6c28c5872d8"
+ }
+ ],
+ "later_ordinary_cluster_publication": {
+ "status": "failed_full_parent_row_preservation",
+ "changed_parent_hashes": 3,
+ "unchanged_task_hashes": 15,
+ "exact_changed_columns": "unknown_hash_only_baseline",
+ "original_parent_preimages": "unavailable_no_guessed_restoration",
+ "receipt": "the-custodian/docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/post-publication-full-row-hashes-FAILED.json",
+ "T07": "wait",
+ "RMGR_WP_0019_T06": "wait",
+ "followup": [
+ "STATE-WP-0095-T09",
+ "STATE-WP-0095-T10"
+ ]
+ }
+}
--- a/deploy/railiance/apps/charts/state-hub/files/repo-classification.allowed.yaml
+++ b/deploy/railiance/apps/charts/state-hub/files/repo-classification.allowed.yaml
@@ -13,7 +13,7 @@
# to match the standard version. CUST-WP-0050 T01.
standard: "Repo Classification Standard"
-version: "1.0"
+version: "1.1"
canon_id: "canon-repo-classification"
# category — exactly 1 required (§5)
@@ -24,6 +24,7 @@
- tooling
- product
- business
+ - publication
# domain / secondary_domains — primary exactly 1; secondaries 0..n (§6)
domains:
@@ -85,6 +86,8 @@
- traceability
- documentation
- decision-support
+ - history
+ - participation
platform_and_operations:
- platform
- deployment
--- a/api/services/historical_projection.py
+++ b/api/services/historical_projection.py
@@ -193,3 +193,46 @@
raise ValueError("Historical source binding changed: " + entry["path"])
result.append((entry, data.decode("utf-8")))
return result
+
+
+# Only the three existing full-task Cluster bindings have immutable DB rows.
+# Other historical views retain their existing compatibility behavior.
+FROZEN_CLUSTER_BINDINGS = tuple(
+ entry for entry in BINDINGS
+ if entry["repo"] == "railiance-cluster" and "tasks" in entry
+)
+
+
+def frozen_cluster_binding(binding):
+ """Validate a claimed frozen binding; unrelated requests remain ordinary."""
+ matches = [entry for entry in FROZEN_CLUSTER_BINDINGS
+ if str(binding["workplan_id"]) == entry["uuid"]
+ or (binding["repo_slug"] == "railiance-cluster"
+ and (binding["relative_path"] == entry["path"]
+ or binding["filename"] == Path(entry["path"]).name))]
+ if not matches:
+ return None
+ if len(matches) != 1:
+ raise ValueError("frozen historical binding is ambiguous")
+ entry = matches[0]
+ expected = {
+ "workplan_id": entry["uuid"], "repo_slug": entry["repo"],
+ "filename": Path(entry["path"]).name, "relative_path": entry["path"],
+ "archived": True, "status": "finished",
+ }
+ if any((str(binding[key]) if key == "workplan_id" else binding[key]) != value
+ for key, value in expected.items()):
+ raise ValueError("frozen historical binding metadata changed")
+ return entry
+
+
+def validate_frozen_cluster_rows(entry, parent, tasks, repo_id):
+ """Receiving requires the exact existing terminal closure, never repair."""
+ if (parent is None or str(parent.id) != entry["uuid"]
+ or repo_id is None or parent.repo_id != repo_id
+ or parent.status not in {"done", "finished", "archived", "completed"}):
+ raise ValueError("frozen historical parent missing, foreign or nonterminal")
+ local = {str(task.id): task for task in tasks if task.workplan_id == parent.id}
+ if any(task["uuid"] not in local or local[task["uuid"]].status not in {"done", "cancel"}
+ for task in entry["tasks"]):
+ raise ValueError("frozen historical task missing, foreign or nonterminal")
--- a/api/routers/workstreams.py
+++ b/api/routers/workstreams.py
@@ -16,6 +16,8 @@
from api.events import EventEnvelope, publish_event
from api.models.managed_repo import ManagedRepo
from api.models.workplan import Workplan
+from api.models.task import Task
+from api.services.historical_projection import frozen_cluster_binding, validate_frozen_cluster_rows
from api.schemas.workplan import (
WorkplanBindingsSync,
WorkplanCreate,
@@ -435,12 +437,42 @@
session: AsyncSession = Depends(get_session),
) -> dict[str, int]:
"""Upsert workstation workplan file bindings for remote API index fallback."""
+ # Preflight the complete batch before assigning even an ordinary row.
+ try:
+ frozen = {entry.workplan_id: bound for entry in body.bindings
+ if (bound := frozen_cluster_binding(entry.model_dump())) is not None}
+ except ValueError as exc:
+ raise HTTPException(status_code=409, detail=str(exc)) from exc
+ requested_ids = {entry.workplan_id for entry in body.bindings}
+ rows = await session.execute(
+ select(Workplan).where(Workplan.id.in_(requested_ids))
+ .order_by(Workplan.id).with_for_update().execution_options(populate_existing=True)
+ )
+ workplans = {workplan.id: workplan for workplan in rows.scalars().all()}
+ if frozen:
+ repo_rows = await session.execute(
+ select(ManagedRepo).where(ManagedRepo.slug == "railiance-cluster")
+ )
+ repo = repo_rows.scalar_one_or_none()
+ task_rows = await session.execute(
+ select(Task).where(Task.workplan_id.in_(frozen))
+ .order_by(Task.id).with_for_update().execution_options(populate_existing=True)
+ )
+ tasks = task_rows.scalars().all()
+ try:
+ for workplan_id, bound in frozen.items():
+ validate_frozen_cluster_rows(
+ bound, workplans.get(workplan_id), tasks, repo.id if repo else None
+ )
+ except ValueError as exc:
+ raise HTTPException(status_code=409, detail=str(exc)) from exc
synced_at = datetime.now(timezone.utc)
updated = 0
- requested_ids = {entry.workplan_id for entry in body.bindings}
- rows = await session.execute(select(Workplan).where(Workplan.id.in_(requested_ids)))
- workplans = {workplan.id: workplan for workplan in rows.scalars().all()}
+ preserved = 0
for entry in body.bindings:
+ if entry.workplan_id in frozen:
+ preserved += 1
+ continue # No setters, including backing_synced_at/updated_at.
wp = workplans.get(entry.workplan_id)
if wp is None:
continue
@@ -449,9 +481,10 @@
wp.backing_archived = entry.archived
wp.backing_synced_at = synced_at
updated += 1
- await session.commit()
- _invalidate_workplan_index_cache()
- return {"updated": updated, "received": len(body.bindings)}
+ if updated:
+ await session.commit()
+ _invalidate_workplan_index_cache()
+ return {"updated": updated, "received": len(body.bindings), "preserved": preserved}
@router.post("/", status_code=status.HTTP_410_GONE)
--- a/scripts/consistency_check.py
+++ b/scripts/consistency_check.py
@@ -120,7 +120,9 @@
resolve_topic_domain_slug,
)
from api.services.lifecycle import should_activate_parent_for_active_tasks # noqa: E402
-from api.services.historical_projection import BINDINGS as _HISTORICAL_BINDINGS # noqa: E402
+from api.services.historical_projection import ( # noqa: E402
+ BINDINGS as _HISTORICAL_BINDINGS, FROZEN_CLUSTER_BINDINGS, frozen_cluster_binding,
+)
from api.task_status import ( # noqa: E402
CANONICAL_TASK_STATUSES,
OPEN_TASK_STATUSES,
@@ -318,7 +320,7 @@
# C-00 marks infrastructure/automation faults (API down, repo missing in DB).
# All other FAIL severities are assessment findings for follow-up.
-_AUTOMATION_ERROR_CHECKS: frozenset[str] = frozenset({"C-00"})
+_AUTOMATION_ERROR_CHECKS: frozenset[str] = frozenset({"C-00", "C-44"})
_NON_WORKPLAN_WORKPLAN_FILES: frozenset[str] = frozenset({"README.md"})
@@ -2496,23 +2498,44 @@
repo_dir: Path,
report: ConsistencyReport,
) -> None:
+ if repo_slug == "railiance-cluster":
+ scanned_paths = {workplan_display_path(repo_dir, path) for path, _, _ in workplan_infos}
+ missing = [entry["path"] for entry in FROZEN_CLUSTER_BINDINGS
+ if entry["path"] not in scanned_paths]
+ if missing:
+ report.add(severity="FAIL", check_id="C-44",
+ message="Frozen binding batch refused: bound source missing from scan",
+ fixable=False)
+ return
bindings: list[dict[str, Any]] = []
for wp_file, meta, _ in workplan_infos:
ws_id = str(meta.get("state_hub_workstream_id", "")).strip().strip('"')
+ archived = wp_file.parent.name == "archived"
+ file_status = normalise_workstream_status(str(meta.get("status", "")).strip())
+ binding = {
+ "workplan_id": ws_id, "filename": wp_file.name,
+ "relative_path": workplan_display_path(repo_dir, wp_file),
+ "repo_slug": repo_slug, "archived": archived,
+ "status": file_status or None,
+ }
+ try:
+ frozen = frozen_cluster_binding(binding)
+ if frozen is not None:
+ # Verify exact source before omitting the immutable binding.
+ # This is safe even against a receiver predating the API guard.
+ from hashlib import sha256
+ if (wp_file.is_symlink() or not wp_file.is_file()
+ or sha256(wp_file.read_bytes()).hexdigest() != frozen["sha256"]):
+ raise ValueError("frozen historical source missing or changed")
+ continue
+ except ValueError as exc:
+ report.add(severity="FAIL", check_id="C-44",
+ message=f"Frozen binding batch refused: {exc}",
+ file_path=str(wp_file), fixable=False)
+ return # Never send a partial batch after a frozen-source refusal.
if not ws_id:
continue
- archived = wp_file.parent.name == "archived"
- file_status = normalise_workstream_status(str(meta.get("status", "")).strip())
- bindings.append(
- {
- "workplan_id": ws_id,
- "filename": wp_file.name,
- "relative_path": workplan_display_path(repo_dir, wp_file),
- "repo_slug": repo_slug,
- "archived": archived,
- "status": file_status or None,
- }
- )
+ bindings.append(binding)
if not bindings:
return
result = _api_put(api_base, "/workplans/index/bindings", {"bindings": bindings})
--- /dev/null
+++ b/tests/test_frozen_binding_guard_isolated.py
@@ -0,0 +1,143 @@
+from pathlib import Path
+import ast, asyncio, importlib.util, types, uuid
+from dataclasses import dataclass, field
+from datetime import datetime, timezone
+import pytest
+
+ROOT = Path(__file__).resolve().parents[1]
+spec=importlib.util.spec_from_file_location('historical',ROOT/'api/services/historical_projection.py')
+h=importlib.util.module_from_spec(spec);spec.loader.exec_module(h)
+B=h.FROZEN_CLUSTER_BINDINGS[0]
+def binding(**changes):
+ d=dict(workplan_id=uuid.UUID(B['uuid']),repo_slug=B['repo'],filename=Path(B['path']).name,relative_path=B['path'],archived=True,status='finished');d.update(changes);return d
+class Obj:
+ def __init__(self,**kw):self.__dict__.update(kw);self.writes=[]
+ def __setattr__(self,k,v):
+ if 'writes' in self.__dict__:self.writes.append(k)
+ object.__setattr__(self,k,v)
+class Entry(Obj):
+ def model_dump(self):return {k:v for k,v in self.__dict__.items() if k!='writes'}
+class Col:
+ def in_(self,x):return self
+ def __eq__(self,x):return self
+class Query:
+ def __init__(self):self.locked=False;self.refreshed=False;self.ordered=False
+ def where(self,*a):return self
+ def order_by(self,*a):self.ordered=True;return self
+ def with_for_update(self):self.locked=True;return self
+ def execution_options(self,**kw):self.refreshed=kw.get("populate_existing") is True;return self
+class Model: id=Col();slug=Col();workplan_id=Col()
+class Result:
+ def __init__(self,values):self.values=values
+ def scalars(self):return self
+ def all(self):return self.values
+ def scalar_one_or_none(self):return self.values[0] if self.values else None
+class Session:
+ def __init__(self,rows):self.results=iter(rows);self.commits=0;self.queries=[]
+ async def execute(self,q):self.queries.append(q);return Result(next(self.results))
+ async def commit(self):self.commits+=1
+class HTTPException(Exception):
+ def __init__(self,status_code,detail):self.status_code=status_code;super().__init__(detail)
+def extracted(path,name,extra):
+ tree=ast.parse((ROOT/path).read_text());node=next(n for n in tree.body if isinstance(n,(ast.FunctionDef,ast.AsyncFunctionDef)) and n.name==name)
+ node.decorator_list=[];node.returns=None
+ for a in node.args.args + node.args.kwonlyargs:a.annotation=None
+ node.args.defaults=[]
+ ns=dict(Path=Path,datetime=datetime,timezone=timezone,**extra)
+ exec(compile(ast.Module(body=[node],type_ignores=[]),str(path),'exec'),ns);return ns[name]
+def actual_report():
+ tree=ast.parse((ROOT/'scripts/consistency_check.py').read_text())
+ nodes=[n for n in tree.body if (isinstance(n,ast.ClassDef) and n.name in {'Issue','ConsistencyReport'}) or (isinstance(n,ast.AnnAssign) and isinstance(n.target,ast.Name) and n.target.id=='_AUTOMATION_ERROR_CHECKS')]
+ ns=dict(dataclass=dataclass,field=field,_AUTOMATION_ERROR_CHECKS=frozenset({'C-00','C-44'}))
+ exec(compile(ast.Module(body=nodes,type_ignores=[]),'actual_report','exec'),ns)
+ return ns['ConsistencyReport']('railiance-cluster','/unused')
+
+def closure():
+ repo=Obj(id=uuid.uuid4());parent=Obj(id=uuid.UUID(B['uuid']),repo_id=repo.id,status='finished')
+ tasks=[Obj(id=uuid.UUID(t['uuid']),workplan_id=parent.id,status='done') for t in B['tasks']]
+ return repo,parent,tasks
+async def run_api(entries,repo,parent,tasks,ordinary=None):
+ rows=([parent] if parent else [])+([ordinary] if ordinary else [])
+ session=Session([rows,[repo] if repo else [],tasks])
+ fn=extracted('api/routers/workstreams.py','sync_workplan_bindings',dict(frozen_cluster_binding=h.frozen_cluster_binding,validate_frozen_cluster_rows=h.validate_frozen_cluster_rows,HTTPException=HTTPException,select=lambda *_:Query(),Workplan=Model,Task=Model,ManagedRepo=Model,_invalidate_workplan_index_cache=lambda:None))
+ result=await fn(Obj(bindings=[Entry(**e) for e in entries]),session)
+ return result,session
+@pytest.mark.parametrize('change',[dict(repo_slug='foreign'),dict(workplan_id=uuid.uuid4()),dict(relative_path='workplans/other.md'),dict(filename='other.md'),dict(archived=False),dict(status='active')])
+def test_metadata_refuses(change):
+ with pytest.raises(ValueError):h.frozen_cluster_binding(binding(**change))
+def test_unrelated_binding_unchanged():assert h.frozen_cluster_binding(binding(workplan_id=uuid.uuid4(),filename='ordinary.md',relative_path='workplans/ordinary.md',repo_slug='other')) is None
+@pytest.mark.parametrize('fault',['parent_missing','foreign_repo','parent_active','task_missing','task_active','task_foreign','repo_missing'])
+def test_mixed_batch_refusal_precedes_all_writes(fault):
+ repo,parent,tasks=closure();ordinary=Obj(id=uuid.uuid4())
+ if fault=='repo_missing':repo=None
+ if fault=='parent_missing':parent=None
+ if fault=='foreign_repo':parent.repo_id=uuid.uuid4()
+ if fault=='parent_active':parent.status='active'
+ if fault=='task_missing':tasks.pop()
+ if fault=='task_active':tasks[0].status='progress'
+ if fault=='task_foreign':tasks[0].workplan_id=uuid.uuid4()
+ if parent:parent.writes.clear()
+ for t in tasks:t.writes.clear()
+ normal=binding(workplan_id=ordinary.id,repo_slug='ordinary',filename='ordinary.md',relative_path='workplans/ordinary.md')
+ with pytest.raises(HTTPException):asyncio.run(run_api([normal,binding()],repo,parent,tasks,ordinary))
+ assert ordinary.writes==[]
+ if parent:assert parent.writes==[]
+ assert all(t.writes==[] for t in tasks)
+def test_exact_frozen_no_setters_or_commit():
+ repo,parent,tasks=closure();result,session=asyncio.run(run_api([binding()],repo,parent,tasks))
+ assert result==dict(updated=0,received=1,preserved=1);assert session.commits==0;assert parent.writes==[]
+ assert all(q.locked and q.refreshed and q.ordered for q in (session.queries[0],session.queries[2]))
+def test_mixed_valid_preserves_frozen_and_updates_ordinary():
+ repo,parent,tasks=closure();ordinary=Obj(id=uuid.uuid4())
+ normal=binding(workplan_id=ordinary.id,repo_slug='ordinary',filename='ordinary.md',relative_path='workplans/ordinary.md')
+ result,session=asyncio.run(run_api([normal,binding()],repo,parent,tasks,ordinary))
+ assert result==dict(updated=1,received=2,preserved=1);assert session.commits==1;assert parent.writes==[]
+ assert ordinary.writes==['backing_filename','backing_relative_path','backing_archived','backing_synced_at']
+@pytest.mark.parametrize('tamper',['metadata','bytes','symlink','missing_scan','missing_uuid','valid'])
+@pytest.mark.parametrize("bound_index", range(3))
+def test_cli_exact_source_omission_or_whole_batch_refusal(tmp_path,tamper,bound_index):
+ bound=h.FROZEN_CLUSTER_BINDINGS[bound_index]
+ import shutil
+ infos=[]
+ for entry in h.FROZEN_CLUSTER_BINDINGS:
+ target=tmp_path/entry['path'];target.parent.mkdir(parents=True,exist_ok=True)
+ shutil.copyfile(Path('/home/worsch/railiance-cluster')/entry['path'],target)
+ infos.append((target,dict(state_hub_workstream_id=entry['uuid'],status='finished'),''))
+ path=tmp_path/bound['path']
+ meta=dict(state_hub_workstream_id=bound['uuid'],status='finished')
+ if tamper=='metadata':meta['status']='active'
+ if tamper=='missing_uuid':meta.pop('state_hub_workstream_id')
+ if tamper=='bytes':path.write_text(path.read_text()+'\n')
+ if tamper=='symlink':path.unlink();path.symlink_to(Path('/home/worsch/railiance-cluster')/bound['path'])
+ ordinary=tmp_path/'workplans/ordinary.md';ordinary.write_text('ordinary')
+ if tamper=='missing_scan':infos.pop(bound_index)
+ else:infos[bound_index]=(path,meta,'')
+ calls=[];report=actual_report()
+ fn=extracted('scripts/consistency_check.py','_sync_workplan_bindings',dict(FROZEN_CLUSTER_BINDINGS=h.FROZEN_CLUSTER_BINDINGS,frozen_cluster_binding=h.frozen_cluster_binding,normalise_workstream_status=lambda s:s,workplan_display_path=lambda root,p:p.relative_to(root).as_posix(),_api_put=lambda *a:calls.append(a) or dict(updated=1,received=1)))
+ fn('http://unused','railiance-cluster',[(ordinary,dict(state_hub_workstream_id=str(uuid.uuid4()),status='active'),''),*infos],tmp_path,report)
+ if tamper=='valid':assert len(calls)==1 and len(calls[0][2]['bindings'])==1
+ else:assert calls==[] and report.failures[0].check_id=='C-44' and report.automation_errors
+
+
+def test_api_tampered_mixed_batch_refuses_before_queries_or_assignments():
+ repo,parent,tasks=closure();ordinary=Obj(id=uuid.uuid4())
+ with pytest.raises(HTTPException):
+ asyncio.run(run_api([binding(workplan_id=ordinary.id,repo_slug='ordinary',filename='ordinary.md',relative_path='workplans/ordinary.md'),binding(archived=False)],repo,parent,tasks,ordinary))
+ assert ordinary.writes==[] and parent.writes==[]
+
+
+def test_foreign_repo_same_filename_path_is_ordinary():
+ assert h.frozen_cluster_binding(binding(workplan_id=uuid.uuid4(),repo_slug='foreign')) is None
+
+
+@pytest.mark.parametrize('entry',h.FROZEN_CLUSTER_BINDINGS)
+def test_all_three_registry_bindings_are_guarded(entry):
+ good=dict(workplan_id=entry['uuid'],repo_slug=entry['repo'],filename=Path(entry['path']).name,relative_path=entry['path'],archived=True,status='finished')
+ assert h.frozen_cluster_binding(good) is entry
+ with pytest.raises(ValueError):h.frozen_cluster_binding(dict(good,status='active'))
+
+
+def test_real_cli_exit_criterion_is_nonzero_including_remote_all():
+ report=actual_report();report.add(severity='FAIL',check_id='C-44',message='refused')
+ fn=extracted('scripts/consistency_check.py','consistency_exit_code',{})
+ assert fn([report])==1 and fn([report],remote_all=True)==1

View file

@ -0,0 +1,17 @@
{
"isolated_regression_tests": 40,
"pytest_exit": 0,
"pytest_command": "python3 -m pytest --noconftest -q state-hub/tests/test_frozen_binding_guard_isolated.py",
"normal_make_render_exit": 0,
"render_complete_tracked_chart": true,
"rendered_objects": 8,
"runtime_images": [
"forgejo.coulomb.social/coulomb/state-hub:main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015",
"forgejo.coulomb.social/coulomb/state-hub:main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015",
"forgejo.coulomb.social/coulomb/state-hub:main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015"
],
"classification_check_exit": 0,
"no_DB_or_cluster_access": true,
"no_canonical_mutations": true,
"original_r1_r2_receipts_preserved": true
}

View file

@ -0,0 +1,43 @@
# Immutable ordinary-binding repair, source revision 4
Canonical base: 086d79a2736e34b916bd7de3c8a65e2ea38a3d64. The ten-path patch includes the previously reviewed six-path followthrough and four guard/test paths. Originals and the actual failed publication receipt remain untouched.
The approved revision-4 receiving rollout preserved all 18 rows at its verification stage. A later ordinary Cluster publication changed exactly three parent full-row hashes; all 15 task hashes remained unchanged. The binding endpoint assigns a new backing sync timestamp unconditionally, providing a concrete mutation path. Hash-only snapshots do not establish the exact changed columns or supply original preimages. This patch does not restore them.
Only the three existing task-bound railiance-cluster registry entries receive this guard. The CLI requires all three scan members, validates their exact source bytes and metadata, and omits valid immutable bindings before contacting any receiver. A refused batch becomes C-44 FAIL and returns a nonzero exit even in remote-all mode. The API independently validates all request metadata before any assignment, locks and refreshes parent/task rows, checks actual repo ownership and existing terminal required-bound tasks, and skips every setter for valid frozen rows. Additional children retain existing Forge receiving semantics; no new child-set invariant is introduced. The ordinary binding response retains updated/received and adds preserved. Frozen-only requests perform no commit or cache invalidation. Existing get_session uses an async-session context manager, which closes the read transaction and releases locks on exit.
Validation: 75 isolated tests pass with --noconftest. Tests extract actual source functions/classes without importing owner app configuration or opening a DB. They verify all three exact sources, missing scan member/UUID, changed source and metadata, foreign names, complete mixed-batch refusal, API ownership and terminal-state refusal, no frozen setters/commit, ordinary compatibility, lock/refresh query intent and actual CLI exit criteria. The full tracked chart plus overlay passes normal Make render/classification check: eight objects, three unchanged digest-qualified images. No concurrency/native DB behavior is claimed by these source tests.
T06 remains done for the approved delivery at its verification stage; T07 remains wait for failed whole-pipeline preservation. T09 is progress until source publication. T10 is wait/needs_human for a separate exact new runtime approval. New task UUIDs are intentionally unallocated pending root review and normal managed allocation. Preserve the original dirty brief/index/weekly evidence and index state.
## Next runtime proposal preparation
After reviewed source integration and normal publication, obtain normal CI status and resolve the resulting exact immutable image manifest. Inspect selected delivered source hashes, including historical_projection.py, workstreams.py and the installed closure; do not reuse the prior main-51bc70d runtime for the new API guard. Prepare digest-qualified packaged-chart reuse-values server dry-run, compare intended object identities/spec hashes, collect fresh release/headroom/schema and full-row hash baseline, then record a new scoped proposal and supervisor disposition. Current known prior baseline was Helm 76/schema c4d5e6f7a8b9; refresh rather than assume it remains current. CI revision, new image digest, final chart package hash and exact execution digest remain unresolved until publication.
No new rollout is authorized by the prior revision-4 approval. A new rollout must retain migration closure and rollback limits: Helm rollback does not downgrade schema, and timeout/atomic does not necessarily stop an active hook Job. Cancellation may address only the exact owned trial Job UID/image after identity verification. Unknown migration recovery or connection/lock quiescence stops for attended owner handling; no credential grant or invented DB-session mapping. EUR cost and risk are unknown until reviewed; no bounded-outage assertion. New baseline preservation and rollback-only refusal probes prove the new stage, never restoration of the unavailable original parent preimages. T07/RMGR19T06 cannot close on source publication alone.
Revision 4 retains frozen revision 3 and adds central consistency repair-batch
refusal. The same helper checks exact registered parent/task UUIDs and source
paths before check_repo can issue an index binding PUT and again before
fix_repo can invoke reconcile, push, metadata registration or individual fixes.
A protected repair finding refuses the complete batch with C-44, including
when an ordinary fix appears first. Existing C-44 refusal is not duplicated.
This prevents generic C-04/C-10/C-22 and related fixes from modifying frozen
rows through other APIs. It does not disable ordinary checks or silently
accept source/DB drift: owner review and the waiting live records remain.
Tests invoke the actual fix engine with mixed fix lists for all three parents,
exercise bound task/source contexts, and verify unchanged ordinary behavior.
API preservation/refusal simulations now exercise all three parent closures.
The post-publication client regression can compare fresh CURRENT full-row hashes
before/after the fixed normal Cluster deep consistency and fast projection.
That is a separate stage from the preserved original failure; equal fresh
hashes do not restore or accept the unavailable original parent preimages.
For T10 prefer the exact originally approved b5c chart archive with only the
newly verified immutable guard image changed, after artifact identity and
server dry-run comparison. Do not infer authority to roll out the source chart's
v1.1 classification ConfigMap or other chart metadata changes. Source Make
pins continue to declare the actually delivered 51bc70d@c802 artifact until a
new exact runtime is approved and verified.

View file

@ -0,0 +1,77 @@
{
"schema": "statehub.source-owner-followthrough.v1",
"revision": 4,
"status": "isolated_source_candidate_not_published",
"base_revision": "086d79a2736e34b916bd7de3c8a65e2ea38a3d64",
"files": [
{
"path": "workplans/STATE-WP-0095-historical-source-receiving.md",
"action": "modify",
"base_sha256": "ef10359cc5974283b5bdbfc602adf14e3c24362adf00640698d2937c6538483a",
"candidate_sha256": "5671189ee87f2d4dff08979f6243e5eb7888b140955899642a74d569496b71c7"
},
{
"path": "Makefile",
"action": "modify",
"base_sha256": "5584ef94071044cca8215141ecd8aee8d6ae5cfe73e8f5719358c680eb8af0d4",
"candidate_sha256": "c5b70e8d4b8ae70380e0ade1c35f9e7fe80038de10080a4723b21ef07a98001e"
},
{
"path": "deploy/railiance/apps/charts/state-hub/Chart.yaml",
"action": "modify",
"base_sha256": "7fe02b390ffd2ba4ce6f82bd62df900826826401e31bdc2138dd5bf36b51a6ae",
"candidate_sha256": "61a989fcf277d05ffbececb7332ee3ca1d8f369c72f960ab1bc4dbc6815536b8"
},
{
"path": "deploy/railiance/apps/helm/state-hub-values.yaml",
"action": "modify",
"base_sha256": "f7cc0e9968598f6ce0faa0d56f62d6817845c3570c18b6ac39ed6561b952b197",
"candidate_sha256": "b51296fc937450f4ecf0ae43243262a631b551389c0d09c8bfe58fa10e2f3ee9"
},
{
"path": "docs/evidence/2026-10-17-cluster-historical-receiving-rollout.json",
"action": "add",
"base_sha256": null,
"candidate_sha256": "fb0b59ba3558e084fb4ae2c36c556f3cfbb2a889f6fd86d4e803c52f483b26fd"
},
{
"path": "deploy/railiance/apps/charts/state-hub/files/repo-classification.allowed.yaml",
"action": "modify",
"base_sha256": "9c36fbd5308b19695f9f05d86f8e315bbeccdd267575ce4e2fccf6fb2ea82e32",
"candidate_sha256": "f53c651bb4329c09c04c66f906e2462fe4339ab51ef0ec3da9f942c3016f5bed"
},
{
"path": "api/services/historical_projection.py",
"action": "modify",
"base_sha256": "e3c4bf965764b32a6df5d61c71272ee22569df9a9ddae2a3fc3df97ec554e0b2",
"candidate_sha256": "e24e57c58f4aaa4ec9d9bf088009ac69006e53f75b9477ee53dc26c4e850b975"
},
{
"path": "api/routers/workstreams.py",
"action": "modify",
"base_sha256": "61e7a0ab638b155e9e0b2a72fe8e0ddd2edf5bc6c2d3ca7f6c3ff51e8868910b",
"candidate_sha256": "9c9bf4376dd323b52109912bb3f615949e89ac02c8b88b4720f569f8565ad1d6"
},
{
"path": "scripts/consistency_check.py",
"action": "modify",
"base_sha256": "1827921c79b476bee823c2ddf9896aeb83f3efca6548427e17b3244216bbc6b5",
"candidate_sha256": "c2dc6e1b930198ae8352de05573fc6d3b58709996a8499cec4e3f5217546c543"
},
{
"path": "tests/test_frozen_binding_guard_isolated.py",
"action": "add",
"base_sha256": null,
"candidate_sha256": "17dfc4733dce5da4195556d1b0b87eca4783f8760b40c74220c331edfcef32f5"
}
],
"patch_sha256": "sha256:c06bc0a3e667e9ae6407f60887ba8feddc66d19a2c21dfc86d1bbe3718361708",
"T06": "done_verified_approved_rollout_at_stage",
"T07": "wait_actual_later_parent_row_preservation_failure",
"T08": "done_source_repair_pending_publication",
"T09": "progress_until_source_published",
"T10": "wait_needs_human_new_runtime",
"live_effects": false,
"original_parent_preimages": "unknown_do_not_restore_guesses",
"managed_UUIDs": "T09_T10_not_allocated_pending_review"
}

View file

@ -0,0 +1,756 @@
--- a/workplans/STATE-WP-0095-historical-source-receiving.md
+++ b/workplans/STATE-WP-0095-historical-source-receiving.md
@@ -199,27 +199,37 @@
```task
id: STATE-WP-0095-T06
-status: wait
+status: done
priority: high
depends_on: [STATE-WP-0095-T05]
-needs_human: true
-intervention_note: "Review exact committed source, CI image and owner deployment proposal before rollout."
+needs_human: false
+intervention_note: "Exact revision 4 approved and verified; no pending rollout intervention."
state_hub_task_id: "6af973b4-1964-5d4d-a2f8-8dbd4c01fda4"
```
-Use the normal CI image and Helm owner path after exact revision/image pin,
-fresh headroom and reviewed dry-run/diff. API/MCP currently run main-7d99282;
-Helm revision75 is the observed rollback baseline, requiring freshness before
-execution. No deployment assent exists for this successor. Verify delivered
-module hash, health, both readiness checks and expected migrated schema
-c4d5e6f7a8b9; verify exact
-three parents/15 tasks and rejection behavior without committed probe changes.
-Only the verified return unblocks RMGR-WP-0019-T05. The full current image also carries inbox-feed migration c4d5e6f7a8b9,
-although this receiver patch adds none. Helm rollback leaves that migration in
-place; unrelated migration acceptance and old-image compatibility need normal
-owner evidence before rollout approval. Do not run destructive default local
-DB tests; use only exact disposable DB containment. Costs and monetary risk are
-unknown pending exact rollout proposal; no zero-cost or autonomy grant is inferred.
+Completed 2026-10-17 on the founder's approval of exact revision 4 of stable
+proposal STATE-WP-0095-T06-cluster-frozen-receiving-r1 (decision
+ e3798419-8aa9-5425-8c08-20df6bd82a19; approved digest
+ sha256:63a02e8296d126c0158b7eb2c4ce79c7eeb6f8d6f61298d473c1065c7557ba32).
+The ordinary owner Helm path delivered revision 76, runtime source 51bc70d and
+exact image manifest sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015.
+API/MCP Ready and primary/railiance01 health report expected schema
+c4d5e6f7a8b9; delivered historical module sha256:e3c4bf965764b32a6df5d61c71272ee22569df9a9ddae2a3fc3df97ec554e0b2 matches.
+All three parent and 15 task full DB row hashes/columns/UUIDs remained unchanged.
+Five actual native probes passed: preserve, actual foreign-parent holder move,
+nonterminal task, nonterminal parent and missing task. Every probe rolled back;
+a fresh transaction confirmed complete row restoration. The foreign-holder
+case precisely verified historical binding refusal, not the separate global
+UUID-holder creation branch. No committed probe writes were made.
+
+Evidence: [verified receiving rollout](../docs/evidence/2026-10-17-cluster-historical-receiving-rollout.json).
+This actual receiving return satisfies the prerequisite of RMGR-WP-0019-T05.
+Normal cluster publication and its immutable-source/row return remain T07;
+source readiness and this deployment do not count that later acceptance twice.
+The additive inbox migration is retained; Helm rollback does not downgrade
+schema or stop an active migration Job. Original failures and exact-UID recovery
+limits remain in the referenced approved packet. No TLS recovery/native private
+admission or new autonomy/cost grant is inferred.
## Verify ordinary cluster receiving and immutable historical rows
@@ -256,6 +266,74 @@
Command-fixture tests verify both requests and preserve other dry-run phases.
Evidence and original refusal remain in the Custodian cluster historical
publication packet, revisions/r2 and r3-readiness. This is completed source
-preparation; T06 remains the explicit native rollout gate. The runtime image
+preparation; T06 separately records the verified actual rollout. The runtime image
continues to be main-51bc70d; Makefile/workplan changes do not create a new
application image or native acceptance.
+
+
+T08 source repair refinement, 2026-10-17: the required normal Make render
+correctly refused a pre-existing generated chart classification copy at v1.0
+after authoritative canon reached v1.1. Regenerated only through the existing
+scripts/sync_classification_allowed.py, preserving canon and strict check.
+The exact source diff is version 1.0→1.1 plus publication/history/participation.
+Normal Make rendering and the focused classification check passed in an
+isolated owner-layout copy. The original refusal is retained in the Custodian
+owner-followthrough-r1 validation receipt. This is source repair only: no live
+ConfigMap apply, new runtime image, policy relaxation or T07 completion.
+
+
+T07 actual ordinary-publication return, 2026-10-17: cluster owner deep consistency
+and normal fast projection completed, exact archived source bytes and all 15
+historical task full-row hashes remained unchanged. The later full-row check
+FAILED for exactly the three parent rows. The earlier approved T06 rollout had
+preserved all 18 rows at its verification stage; that success is not the later
+whole-pipeline acceptance. The original snapshots retain hashes only, so exact
+changed columns and original field preimages are not established. No guessed
+DB repair or claim that all 18 original row hashes are restored is authorized.
+T07 and RMGR-WP-0019-T06 remain waiting pending T09/T10 and a reviewed return.
+
+## Guard immutable historical index bindings in CLI and receiving API
+
+```task
+id: STATE-WP-0095-T09
+status: progress
+priority: high
+depends_on: [STATE-WP-0095-T06]
+```
+
+Prepare and publish the narrowly scoped ordinary-binding repair for the three
+existing task-bound Cluster registry entries. The CLI verifies exact frozen
+source metadata/bytes and omits those bindings, including on old receivers.
+The API independently validates the complete batch before any mutation, checks
+actual repo ownership and the existing terminal parent/task closure, and
+preserves valid frozen rows without field or timestamp assignments. Invalid
+metadata, missing/foreign/nonterminal rows refuse the batch. Unknown ordinary
+bindings retain existing behavior; historical IDs, UUIDs and source bytes stay
+unchanged. Isolated regression tests and normal owner rendering are required.
+Mark done only after reviewed source publication and normal owner projection;
+source preparation does not establish delivered runtime or T07 acceptance.
+Central check/fix engines also refuse the complete repair batch if any fixable
+context touches an exact registered parent/task UUID or frozen source path.
+This runs before index bindings and before reconcile/push/individual fixes;
+ordinary earlier fixes cannot partially execute before a later frozen refusal.
+
+## Deliver and verify the ordinary-binding guard successor
+
+```task
+id: STATE-WP-0095-T10
+status: wait
+priority: high
+depends_on: [STATE-WP-0095-T09]
+needs_human: true
+intervention_note: "New API runtime requires its own exact image, reviewed preflight and scoped rollout approval; prior revision-4 approval does not cover it."
+```
+
+After source publication, resolve normal CI status and immutable image identity,
+verify delivered guard source closure, prepare exact chart/reuse-values dry-run,
+full closure/headroom/schema baseline and conditional recovery. Retain additive
+migration and active-Job cancellation limitations; no Helm schema downgrade or
+bounded-outage claim. Obtain scoped approval before live rollout. Then verify
+new full-row baseline preservation and actual rollback-only refusal probes,
+and only then return to T07 ordinary-pipeline acceptance. This prevents future
+binding mutation; it does not reconstruct or restore original parent hashes.
+No production changes, new autonomy or credential authority are granted here.
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,7 @@
RAILIANCE_STATE_HUB_NAMESPACE ?= state-hub
RAILIANCE_STATE_HUB_CHART ?= deploy/railiance/apps/charts/state-hub
RAILIANCE_STATE_HUB_VALUES ?= deploy/railiance/apps/helm/state-hub-values.yaml
-RAILIANCE_STATE_HUB_IMAGE_TAG ?= main-0747b4d
+RAILIANCE_STATE_HUB_IMAGE_TAG ?= main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015
RAILIANCE_STATE_HUB_PLATFORM_DIR ?= deploy/railiance/platform
RAILIANCE_STATE_HUB_APP_MANIFESTS ?= deploy/railiance/apps/manifests
# Codex/WSL non-login shells may not source ~/.profile; keep uv discoverable.
--- a/deploy/railiance/apps/charts/state-hub/Chart.yaml
+++ b/deploy/railiance/apps/charts/state-hub/Chart.yaml
@@ -3,4 +3,4 @@
description: State Hub API service for private Railiance operation
type: application
version: 0.1.0
-appVersion: "main-5dd3df9"
+appVersion: "main-51bc70d"
--- a/deploy/railiance/apps/helm/state-hub-values.yaml
+++ b/deploy/railiance/apps/helm/state-hub-values.yaml
@@ -6,7 +6,7 @@
image:
repository: forgejo.coulomb.social/coulomb/state-hub
- tag: "main-0747b4d"
+ tag: "main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015"
ingress:
enabled: false
--- /dev/null
+++ b/docs/evidence/2026-10-17-cluster-historical-receiving-rollout.json
@@ -0,0 +1,116 @@
+{
+ "schema": "statehub.cluster-historical-receiving-delivered.v1",
+ "status": "verified",
+ "proposal_id": "STATE-WP-0095-T06-cluster-frozen-receiving-r1",
+ "approved_revision": 4,
+ "approved_digest_sha256": "sha256:63a02e8296d126c0158b7eb2c4ce79c7eeb6f8d6f61298d473c1065c7557ba32",
+ "approval_decision_id": "e3798419-8aa9-5425-8c08-20df6bd82a19",
+ "source_revision": "51bc70d1ff8f25450a8650d8e50caf053093a02e",
+ "readiness_source_revision": "086d79a2736e34b916bd7de3c8a65e2ea38a3d64",
+ "helm_revision": 76,
+ "image_reference": "forgejo.coulomb.social/coulomb/state-hub:main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015",
+ "expected_schema": "c4d5e6f7a8b9",
+ "health": {
+ "status": "ok",
+ "instance_role": "primary",
+ "instance_label": "railiance01",
+ "schema": {
+ "status": "ok",
+ "applied": "c4d5e6f7a8b9",
+ "expected": "c4d5e6f7a8b9"
+ }
+ },
+ "pods_ready": true,
+ "historical_module_sha256": "sha256:e3c4bf965764b32a6df5d61c71272ee22569df9a9ddae2a3fc3df97ec554e0b2",
+ "historical_preservation_verified": true,
+ "parents": 3,
+ "tasks": 15,
+ "full_historical_rows_equal": true,
+ "native_rollback_probes": {
+ "foreign-holder": {
+ "full_historical_rows_restored": true,
+ "historical_binding_refused": true,
+ "native_outcome_status": "refused",
+ "rolled_back": true,
+ "status": "passed"
+ },
+ "missing-task": {
+ "full_historical_rows_restored": true,
+ "historical_binding_refused": true,
+ "native_outcome_status": "refused",
+ "rolled_back": true,
+ "status": "passed"
+ },
+ "nonterminal-parent": {
+ "full_historical_rows_restored": true,
+ "historical_binding_refused": true,
+ "native_outcome_status": "refused",
+ "rolled_back": true,
+ "status": "passed"
+ },
+ "nonterminal-task": {
+ "full_historical_rows_restored": true,
+ "historical_binding_refused": true,
+ "native_outcome_status": "refused",
+ "rolled_back": true,
+ "status": "passed"
+ },
+ "preserve": {
+ "full_historical_rows_equal": true,
+ "full_historical_rows_restored": true,
+ "native_outcome_status": "applied",
+ "rolled_back": true,
+ "status": "passed"
+ }
+ },
+ "native_probe_commits": 0,
+ "fresh_transaction_full_rows_equal": true,
+ "source_receiving_gate_delivered": true,
+ "normal_cluster_publication_verified": false,
+ "registrar_admission_or_cluster_publication_not_inferred": true,
+ "authority_limits": [
+ "Rollback probe SQL effects rolled back; foreign-holder case is historical-binding refusal, not separate global holder branch.",
+ "Actual historical native receiving is verified; owner-native TLS/private admission remains unresolved.",
+ "Migration schema remains additive; Helm rollback does not downgrade schema or stop an active hook Job."
+ ],
+ "evidence": [
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/approval-decision.json",
+ "sha256": "sha256:b51554c004daa882f3709f67bdae7f2e939d611120383e61be04ec6969878134"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/helm-return.json",
+ "sha256": "sha256:61727deacf22d37da2d28cbaf04f5a3ddee0f9ff7571c77f1e85dba97963b572"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/delivered-runtime.json",
+ "sha256": "sha256:89681066e8fe139456163ebfafa3efe77ffae8534ea167219230b0a74367f129"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/native-refusal-probes.json",
+ "sha256": "sha256:673ccef965c820704dc310a1bf7832b583c30c3edfeb814528cf892555199172"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/before-full-row-hashes.json",
+ "sha256": "sha256:75cdd87eef79059ff3f484ed24ca69e285899cdf8d851cc4e472653c417eb0ee"
+ },
+ {
+ "path": "docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/after-full-row-hashes.json",
+ "sha256": "sha256:2156aad18f190384838c27e792706a156a7423391408afbc6107e6c28c5872d8"
+ }
+ ],
+ "later_ordinary_cluster_publication": {
+ "status": "failed_full_parent_row_preservation",
+ "changed_parent_hashes": 3,
+ "unchanged_task_hashes": 15,
+ "exact_changed_columns": "unknown_hash_only_baseline",
+ "original_parent_preimages": "unavailable_no_guessed_restoration",
+ "receipt": "the-custodian/docs/changes/netkingdom-target-2026-10-17/receiver-rollout-r4/post-publication-full-row-hashes-FAILED.json",
+ "T07": "wait",
+ "RMGR_WP_0019_T06": "wait",
+ "followup": [
+ "STATE-WP-0095-T09",
+ "STATE-WP-0095-T10"
+ ]
+ }
+}
--- a/deploy/railiance/apps/charts/state-hub/files/repo-classification.allowed.yaml
+++ b/deploy/railiance/apps/charts/state-hub/files/repo-classification.allowed.yaml
@@ -13,7 +13,7 @@
# to match the standard version. CUST-WP-0050 T01.
standard: "Repo Classification Standard"
-version: "1.0"
+version: "1.1"
canon_id: "canon-repo-classification"
# category — exactly 1 required (§5)
@@ -24,6 +24,7 @@
- tooling
- product
- business
+ - publication
# domain / secondary_domains — primary exactly 1; secondaries 0..n (§6)
domains:
@@ -85,6 +86,8 @@
- traceability
- documentation
- decision-support
+ - history
+ - participation
platform_and_operations:
- platform
- deployment
--- a/api/services/historical_projection.py
+++ b/api/services/historical_projection.py
@@ -193,3 +193,46 @@
raise ValueError("Historical source binding changed: " + entry["path"])
result.append((entry, data.decode("utf-8")))
return result
+
+
+# Only the three existing full-task Cluster bindings have immutable DB rows.
+# Other historical views retain their existing compatibility behavior.
+FROZEN_CLUSTER_BINDINGS = tuple(
+ entry for entry in BINDINGS
+ if entry["repo"] == "railiance-cluster" and "tasks" in entry
+)
+
+
+def frozen_cluster_binding(binding):
+ """Validate a claimed frozen binding; unrelated requests remain ordinary."""
+ matches = [entry for entry in FROZEN_CLUSTER_BINDINGS
+ if str(binding["workplan_id"]) == entry["uuid"]
+ or (binding["repo_slug"] == "railiance-cluster"
+ and (binding["relative_path"] == entry["path"]
+ or binding["filename"] == Path(entry["path"]).name))]
+ if not matches:
+ return None
+ if len(matches) != 1:
+ raise ValueError("frozen historical binding is ambiguous")
+ entry = matches[0]
+ expected = {
+ "workplan_id": entry["uuid"], "repo_slug": entry["repo"],
+ "filename": Path(entry["path"]).name, "relative_path": entry["path"],
+ "archived": True, "status": "finished",
+ }
+ if any((str(binding[key]) if key == "workplan_id" else binding[key]) != value
+ for key, value in expected.items()):
+ raise ValueError("frozen historical binding metadata changed")
+ return entry
+
+
+def validate_frozen_cluster_rows(entry, parent, tasks, repo_id):
+ """Receiving requires the exact existing terminal closure, never repair."""
+ if (parent is None or str(parent.id) != entry["uuid"]
+ or repo_id is None or parent.repo_id != repo_id
+ or parent.status not in {"done", "finished", "archived", "completed"}):
+ raise ValueError("frozen historical parent missing, foreign or nonterminal")
+ local = {str(task.id): task for task in tasks if task.workplan_id == parent.id}
+ if any(task["uuid"] not in local or local[task["uuid"]].status not in {"done", "cancel"}
+ for task in entry["tasks"]):
+ raise ValueError("frozen historical task missing, foreign or nonterminal")
--- a/api/routers/workstreams.py
+++ b/api/routers/workstreams.py
@@ -16,6 +16,8 @@
from api.events import EventEnvelope, publish_event
from api.models.managed_repo import ManagedRepo
from api.models.workplan import Workplan
+from api.models.task import Task
+from api.services.historical_projection import frozen_cluster_binding, validate_frozen_cluster_rows
from api.schemas.workplan import (
WorkplanBindingsSync,
WorkplanCreate,
@@ -435,12 +437,42 @@
session: AsyncSession = Depends(get_session),
) -> dict[str, int]:
"""Upsert workstation workplan file bindings for remote API index fallback."""
+ # Preflight the complete batch before assigning even an ordinary row.
+ try:
+ frozen = {entry.workplan_id: bound for entry in body.bindings
+ if (bound := frozen_cluster_binding(entry.model_dump())) is not None}
+ except ValueError as exc:
+ raise HTTPException(status_code=409, detail=str(exc)) from exc
+ requested_ids = {entry.workplan_id for entry in body.bindings}
+ rows = await session.execute(
+ select(Workplan).where(Workplan.id.in_(requested_ids))
+ .order_by(Workplan.id).with_for_update().execution_options(populate_existing=True)
+ )
+ workplans = {workplan.id: workplan for workplan in rows.scalars().all()}
+ if frozen:
+ repo_rows = await session.execute(
+ select(ManagedRepo).where(ManagedRepo.slug == "railiance-cluster")
+ )
+ repo = repo_rows.scalar_one_or_none()
+ task_rows = await session.execute(
+ select(Task).where(Task.workplan_id.in_(frozen))
+ .order_by(Task.id).with_for_update().execution_options(populate_existing=True)
+ )
+ tasks = task_rows.scalars().all()
+ try:
+ for workplan_id, bound in frozen.items():
+ validate_frozen_cluster_rows(
+ bound, workplans.get(workplan_id), tasks, repo.id if repo else None
+ )
+ except ValueError as exc:
+ raise HTTPException(status_code=409, detail=str(exc)) from exc
synced_at = datetime.now(timezone.utc)
updated = 0
- requested_ids = {entry.workplan_id for entry in body.bindings}
- rows = await session.execute(select(Workplan).where(Workplan.id.in_(requested_ids)))
- workplans = {workplan.id: workplan for workplan in rows.scalars().all()}
+ preserved = 0
for entry in body.bindings:
+ if entry.workplan_id in frozen:
+ preserved += 1
+ continue # No setters, including backing_synced_at/updated_at.
wp = workplans.get(entry.workplan_id)
if wp is None:
continue
@@ -449,9 +481,10 @@
wp.backing_archived = entry.archived
wp.backing_synced_at = synced_at
updated += 1
- await session.commit()
- _invalidate_workplan_index_cache()
- return {"updated": updated, "received": len(body.bindings)}
+ if updated:
+ await session.commit()
+ _invalidate_workplan_index_cache()
+ return {"updated": updated, "received": len(body.bindings), "preserved": preserved}
@router.post("/", status_code=status.HTTP_410_GONE)
--- a/scripts/consistency_check.py
+++ b/scripts/consistency_check.py
@@ -120,7 +120,9 @@
resolve_topic_domain_slug,
)
from api.services.lifecycle import should_activate_parent_for_active_tasks # noqa: E402
-from api.services.historical_projection import BINDINGS as _HISTORICAL_BINDINGS # noqa: E402
+from api.services.historical_projection import ( # noqa: E402
+ BINDINGS as _HISTORICAL_BINDINGS, FROZEN_CLUSTER_BINDINGS, frozen_cluster_binding,
+)
from api.task_status import ( # noqa: E402
CANONICAL_TASK_STATUSES,
OPEN_TASK_STATUSES,
@@ -318,7 +320,7 @@
# C-00 marks infrastructure/automation faults (API down, repo missing in DB).
# All other FAIL severities are assessment findings for follow-up.
-_AUTOMATION_ERROR_CHECKS: frozenset[str] = frozenset({"C-00"})
+_AUTOMATION_ERROR_CHECKS: frozenset[str] = frozenset({"C-00", "C-44"})
_NON_WORKPLAN_WORKPLAN_FILES: frozenset[str] = frozenset({"README.md"})
@@ -2484,6 +2486,8 @@
_check_workplan_id_collisions(api_base, repo_slug, repo_dir, workplan_infos, report)
_check_scope_freshness(repo_dir, workplan_infos, report)
+ if _refuse_frozen_fix_batch(report, repo_slug):
+ return report
_sync_workplan_bindings(api_base, repo_slug, workplan_infos, repo_dir, report)
return report
@@ -2496,23 +2500,44 @@
repo_dir: Path,
report: ConsistencyReport,
) -> None:
+ if repo_slug == "railiance-cluster":
+ scanned_paths = {workplan_display_path(repo_dir, path) for path, _, _ in workplan_infos}
+ missing = [entry["path"] for entry in FROZEN_CLUSTER_BINDINGS
+ if entry["path"] not in scanned_paths]
+ if missing:
+ report.add(severity="FAIL", check_id="C-44",
+ message="Frozen binding batch refused: bound source missing from scan",
+ fixable=False)
+ return
bindings: list[dict[str, Any]] = []
for wp_file, meta, _ in workplan_infos:
ws_id = str(meta.get("state_hub_workstream_id", "")).strip().strip('"')
+ archived = wp_file.parent.name == "archived"
+ file_status = normalise_workstream_status(str(meta.get("status", "")).strip())
+ binding = {
+ "workplan_id": ws_id, "filename": wp_file.name,
+ "relative_path": workplan_display_path(repo_dir, wp_file),
+ "repo_slug": repo_slug, "archived": archived,
+ "status": file_status or None,
+ }
+ try:
+ frozen = frozen_cluster_binding(binding)
+ if frozen is not None:
+ # Verify exact source before omitting the immutable binding.
+ # This is safe even against a receiver predating the API guard.
+ from hashlib import sha256
+ if (wp_file.is_symlink() or not wp_file.is_file()
+ or sha256(wp_file.read_bytes()).hexdigest() != frozen["sha256"]):
+ raise ValueError("frozen historical source missing or changed")
+ continue
+ except ValueError as exc:
+ report.add(severity="FAIL", check_id="C-44",
+ message=f"Frozen binding batch refused: {exc}",
+ file_path=str(wp_file), fixable=False)
+ return # Never send a partial batch after a frozen-source refusal.
if not ws_id:
continue
- archived = wp_file.parent.name == "archived"
- file_status = normalise_workstream_status(str(meta.get("status", "")).strip())
- bindings.append(
- {
- "workplan_id": ws_id,
- "filename": wp_file.name,
- "relative_path": workplan_display_path(repo_dir, wp_file),
- "repo_slug": repo_slug,
- "archived": archived,
- "status": file_status or None,
- }
- )
+ bindings.append(binding)
if not bindings:
return
result = _api_put(api_base, "/workplans/index/bindings", {"bindings": bindings})
@@ -3610,6 +3635,44 @@
# Fix engine
# ---------------------------------------------------------------------------
+def _refuse_frozen_fix_batch(report: ConsistencyReport, repo_slug: str) -> bool:
+ """Refuse a complete repair batch touching the exact bound histories."""
+ if any(issue.check_id == "C-44" and issue.severity == "FAIL" for issue in report.issues):
+ return True
+ protected_ids = {entry["uuid"] for entry in FROZEN_CLUSTER_BINDINGS}
+ protected_ids.update(task["uuid"] for entry in FROZEN_CLUSTER_BINDINGS
+ for task in entry["tasks"])
+ paths = {entry["path"] for entry in FROZEN_CLUSTER_BINDINGS}
+ root = Path(report.repo_path).absolute() if report.repo_path else None
+
+ def protected(value, key=""):
+ if isinstance(value, dict):
+ return any(protected(item, name) for name, item in value.items())
+ if isinstance(value, (list, tuple)):
+ return any(protected(item, key) for item in value)
+ text = str(value)
+ if (key in {"id", "db_id", "ws_id", "task_id"}
+ or key.endswith(("_id", "_uuid"))):
+ if text in protected_ids:
+ return True
+ if key in {"file_path", "wp_file", "md_path", "path"} and text:
+ candidate = Path(text)
+ if candidate.is_absolute():
+ return root is not None and any(candidate.absolute() == root / path for path in paths)
+ return repo_slug == "railiance-cluster" and candidate.as_posix() in paths
+ return False
+
+ for issue in report.issues:
+ if issue.fixable and (protected(issue.db_id, "db_id")
+ or protected(issue.file_path, "file_path")
+ or protected(issue._fix_context)):
+ report.add(severity="FAIL", check_id="C-44",
+ message="Frozen historical repair batch refused; owner review required",
+ fixable=False)
+ return True
+ return False
+
+
def fix_repo(
api_base: str,
repo_slug: str,
@@ -3624,7 +3687,9 @@
repo_path_override,
bootstrap_empty_projection=bootstrap_empty_projection,
)
- if any(i.check_id in {"C-00", "C-36"} for i in report.failures):
+ if _refuse_frozen_fix_batch(report, repo_slug):
+ return report
+ if any(i.check_id in {"C-00", "C-36", "C-44"} for i in report.failures):
return report
# RMGR-WP-0002: optional repo-manager reconcile proxy (index rebuild) for pilots
--- /dev/null
+++ b/tests/test_frozen_binding_guard_isolated.py
@@ -0,0 +1,180 @@
+from pathlib import Path
+import ast, asyncio, importlib.util, types, uuid
+from dataclasses import dataclass, field
+from datetime import datetime, timezone
+import pytest
+
+ROOT = Path(__file__).resolve().parents[1]
+spec=importlib.util.spec_from_file_location('historical',ROOT/'api/services/historical_projection.py')
+h=importlib.util.module_from_spec(spec);spec.loader.exec_module(h)
+B=h.FROZEN_CLUSTER_BINDINGS[0]
+def binding(_bound=B,**changes):
+ B=_bound
+ d=dict(workplan_id=uuid.UUID(B['uuid']),repo_slug=B['repo'],filename=Path(B['path']).name,relative_path=B['path'],archived=True,status='finished');d.update(changes);return d
+class Obj:
+ def __init__(self,**kw):self.__dict__.update(kw);self.writes=[]
+ def __setattr__(self,k,v):
+ if 'writes' in self.__dict__:self.writes.append(k)
+ object.__setattr__(self,k,v)
+class Entry(Obj):
+ def model_dump(self):return {k:v for k,v in self.__dict__.items() if k!='writes'}
+class Col:
+ def in_(self,x):return self
+ def __eq__(self,x):return self
+class Query:
+ def __init__(self):self.locked=False;self.refreshed=False;self.ordered=False
+ def where(self,*a):return self
+ def order_by(self,*a):self.ordered=True;return self
+ def with_for_update(self):self.locked=True;return self
+ def execution_options(self,**kw):self.refreshed=kw.get("populate_existing") is True;return self
+class Model: id=Col();slug=Col();workplan_id=Col()
+class Result:
+ def __init__(self,values):self.values=values
+ def scalars(self):return self
+ def all(self):return self.values
+ def scalar_one_or_none(self):return self.values[0] if self.values else None
+class Session:
+ def __init__(self,rows):self.results=iter(rows);self.commits=0;self.queries=[]
+ async def execute(self,q):self.queries.append(q);return Result(next(self.results))
+ async def commit(self):self.commits+=1
+class HTTPException(Exception):
+ def __init__(self,status_code,detail):self.status_code=status_code;super().__init__(detail)
+def extracted(path,name,extra):
+ tree=ast.parse((ROOT/path).read_text());node=next(n for n in tree.body if isinstance(n,(ast.FunctionDef,ast.AsyncFunctionDef)) and n.name==name)
+ node.decorator_list=[];node.returns=None
+ for a in node.args.args + node.args.kwonlyargs:a.annotation=None
+ node.args.defaults=[]
+ ns=dict(Path=Path,datetime=datetime,timezone=timezone,**extra)
+ exec(compile(ast.Module(body=[node],type_ignores=[]),str(path),'exec'),ns);return ns[name]
+def actual_report():
+ tree=ast.parse((ROOT/'scripts/consistency_check.py').read_text())
+ nodes=[n for n in tree.body if (isinstance(n,ast.ClassDef) and n.name in {'Issue','ConsistencyReport'}) or (isinstance(n,ast.AnnAssign) and isinstance(n.target,ast.Name) and n.target.id=='_AUTOMATION_ERROR_CHECKS')]
+ ns=dict(dataclass=dataclass,field=field,_AUTOMATION_ERROR_CHECKS=frozenset({'C-00','C-44'}))
+ exec(compile(ast.Module(body=nodes,type_ignores=[]),'actual_report','exec'),ns)
+ return ns['ConsistencyReport']('railiance-cluster','/unused')
+
+def closure(B=B):
+ repo=Obj(id=uuid.uuid4());parent=Obj(id=uuid.UUID(B['uuid']),repo_id=repo.id,status='finished')
+ tasks=[Obj(id=uuid.UUID(t['uuid']),workplan_id=parent.id,status='done') for t in B['tasks']]
+ return repo,parent,tasks
+async def run_api(entries,repo,parent,tasks,ordinary=None):
+ rows=([parent] if parent else [])+([ordinary] if ordinary else [])
+ session=Session([rows,[repo] if repo else [],tasks])
+ fn=extracted('api/routers/workstreams.py','sync_workplan_bindings',dict(frozen_cluster_binding=h.frozen_cluster_binding,validate_frozen_cluster_rows=h.validate_frozen_cluster_rows,HTTPException=HTTPException,select=lambda *_:Query(),Workplan=Model,Task=Model,ManagedRepo=Model,_invalidate_workplan_index_cache=lambda:None))
+ result=await fn(Obj(bindings=[Entry(**e) for e in entries]),session)
+ return result,session
+@pytest.mark.parametrize('change',[dict(repo_slug='foreign'),dict(workplan_id=uuid.uuid4()),dict(relative_path='workplans/other.md'),dict(filename='other.md'),dict(archived=False),dict(status='active')])
+def test_metadata_refuses(change):
+ with pytest.raises(ValueError):h.frozen_cluster_binding(binding(**change))
+def test_unrelated_binding_unchanged():assert h.frozen_cluster_binding(binding(workplan_id=uuid.uuid4(),filename='ordinary.md',relative_path='workplans/ordinary.md',repo_slug='other')) is None
+@pytest.mark.parametrize('fault',['parent_missing','foreign_repo','parent_active','task_missing','task_active','task_foreign','repo_missing'])
+@pytest.mark.parametrize("bound",h.FROZEN_CLUSTER_BINDINGS)
+def test_mixed_batch_refusal_precedes_all_writes(fault,bound):
+ repo,parent,tasks=closure(bound);ordinary=Obj(id=uuid.uuid4())
+ if fault=='repo_missing':repo=None
+ if fault=='parent_missing':parent=None
+ if fault=='foreign_repo':parent.repo_id=uuid.uuid4()
+ if fault=='parent_active':parent.status='active'
+ if fault=='task_missing':tasks.pop()
+ if fault=='task_active':tasks[0].status='progress'
+ if fault=='task_foreign':tasks[0].workplan_id=uuid.uuid4()
+ if parent:parent.writes.clear()
+ for t in tasks:t.writes.clear()
+ normal=binding(workplan_id=ordinary.id,repo_slug='ordinary',filename='ordinary.md',relative_path='workplans/ordinary.md')
+ with pytest.raises(HTTPException):asyncio.run(run_api([normal,binding(_bound=bound)],repo,parent,tasks,ordinary))
+ assert ordinary.writes==[]
+ if parent:assert parent.writes==[]
+ assert all(t.writes==[] for t in tasks)
+@pytest.mark.parametrize("bound",h.FROZEN_CLUSTER_BINDINGS)
+def test_exact_frozen_no_setters_or_commit(bound):
+ repo,parent,tasks=closure(bound);result,session=asyncio.run(run_api([binding(_bound=bound)],repo,parent,tasks))
+ assert result==dict(updated=0,received=1,preserved=1);assert session.commits==0;assert parent.writes==[]
+ assert all(q.locked and q.refreshed and q.ordered for q in (session.queries[0],session.queries[2]))
+@pytest.mark.parametrize("bound",h.FROZEN_CLUSTER_BINDINGS)
+def test_mixed_valid_preserves_frozen_and_updates_ordinary(bound):
+ repo,parent,tasks=closure(bound);ordinary=Obj(id=uuid.uuid4())
+ normal=binding(workplan_id=ordinary.id,repo_slug='ordinary',filename='ordinary.md',relative_path='workplans/ordinary.md')
+ result,session=asyncio.run(run_api([normal,binding(_bound=bound)],repo,parent,tasks,ordinary))
+ assert result==dict(updated=1,received=2,preserved=1);assert session.commits==1;assert parent.writes==[]
+ assert ordinary.writes==['backing_filename','backing_relative_path','backing_archived','backing_synced_at']
+@pytest.mark.parametrize('tamper',['metadata','bytes','symlink','missing_scan','missing_uuid','valid'])
+@pytest.mark.parametrize("bound_index", range(3))
+def test_cli_exact_source_omission_or_whole_batch_refusal(tmp_path,tamper,bound_index):
+ bound=h.FROZEN_CLUSTER_BINDINGS[bound_index]
+ import shutil
+ infos=[]
+ for entry in h.FROZEN_CLUSTER_BINDINGS:
+ target=tmp_path/entry['path'];target.parent.mkdir(parents=True,exist_ok=True)
+ shutil.copyfile(Path('/home/worsch/railiance-cluster')/entry['path'],target)
+ infos.append((target,dict(state_hub_workstream_id=entry['uuid'],status='finished'),''))
+ path=tmp_path/bound['path']
+ meta=dict(state_hub_workstream_id=bound['uuid'],status='finished')
+ if tamper=='metadata':meta['status']='active'
+ if tamper=='missing_uuid':meta.pop('state_hub_workstream_id')
+ if tamper=='bytes':path.write_text(path.read_text()+'\n')
+ if tamper=='symlink':path.unlink();path.symlink_to(Path('/home/worsch/railiance-cluster')/bound['path'])
+ ordinary=tmp_path/'workplans/ordinary.md';ordinary.write_text('ordinary')
+ if tamper=='missing_scan':infos.pop(bound_index)
+ else:infos[bound_index]=(path,meta,'')
+ calls=[];report=actual_report()
+ fn=extracted('scripts/consistency_check.py','_sync_workplan_bindings',dict(FROZEN_CLUSTER_BINDINGS=h.FROZEN_CLUSTER_BINDINGS,frozen_cluster_binding=h.frozen_cluster_binding,normalise_workstream_status=lambda s:s,workplan_display_path=lambda root,p:p.relative_to(root).as_posix(),_api_put=lambda *a:calls.append(a) or dict(updated=1,received=1)))
+ fn('http://unused','railiance-cluster',[(ordinary,dict(state_hub_workstream_id=str(uuid.uuid4()),status='active'),''),*infos],tmp_path,report)
+ if tamper=='valid':assert len(calls)==1 and len(calls[0][2]['bindings'])==1
+ else:assert calls==[] and report.failures[0].check_id=='C-44' and report.automation_errors
+
+
+def test_api_tampered_mixed_batch_refuses_before_queries_or_assignments():
+ repo,parent,tasks=closure();ordinary=Obj(id=uuid.uuid4())
+ with pytest.raises(HTTPException):
+ asyncio.run(run_api([binding(workplan_id=ordinary.id,repo_slug='ordinary',filename='ordinary.md',relative_path='workplans/ordinary.md'),binding(archived=False)],repo,parent,tasks,ordinary))
+ assert ordinary.writes==[] and parent.writes==[]
+
+
+def test_foreign_repo_same_filename_path_is_ordinary():
+ assert h.frozen_cluster_binding(binding(workplan_id=uuid.uuid4(),repo_slug='foreign')) is None
+
+
+@pytest.mark.parametrize('entry',h.FROZEN_CLUSTER_BINDINGS)
+def test_all_three_registry_bindings_are_guarded(entry):
+ good=dict(workplan_id=entry['uuid'],repo_slug=entry['repo'],filename=Path(entry['path']).name,relative_path=entry['path'],archived=True,status='finished')
+ assert h.frozen_cluster_binding(good) is entry
+ with pytest.raises(ValueError):h.frozen_cluster_binding(dict(good,status='active'))
+
+
+def test_real_cli_exit_criterion_is_nonzero_including_remote_all():
+ report=actual_report();report.add(severity='FAIL',check_id='C-44',message='refused')
+ fn=extracted('scripts/consistency_check.py','consistency_exit_code',{})
+ assert fn([report])==1 and fn([report],remote_all=True)==1
+
+
+@pytest.mark.parametrize('check,key,protected_kind',[
+ ('C-04','ws_id','parent'),('C-10','task_id','task'),('C-22','task_id','task'),
+ ('C-09','db_id','parent'),('C-23','wp_file','path'),
+])
+@pytest.mark.parametrize('bound',h.FROZEN_CLUSTER_BINDINGS)
+def test_complete_fix_batch_preflight_before_ordinary_writes(check,key,protected_kind,bound):
+ report=actual_report();report.repo_path='/tmp/fixture-cluster'
+ report.add(severity='WARN',check_id='C-05',message='ordinary first',fixable=True,_fix_context={'ws_id':str(uuid.uuid4()),'field':'title','value':'ordinary'})
+ value=bound['uuid'] if protected_kind=='parent' else bound['tasks'][0]['uuid'] if protected_kind=='task' else str(Path(report.repo_path)/bound['path'])
+ report.add(severity='WARN',check_id=check,message='protected later',fixable=True,db_id=value if key=='db_id' else '',_fix_context={key:value})
+ guard=extracted('scripts/consistency_check.py','_refuse_frozen_fix_batch',dict(FROZEN_CLUSTER_BINDINGS=h.FROZEN_CLUSTER_BINDINGS))
+ assert guard(report,'railiance-cluster') and report.automation_errors
+ calls=[]
+ fn=extracted('scripts/consistency_check.py','fix_repo',dict(check_repo=lambda *a,**kw:report,_refuse_frozen_fix_batch=guard,_api_post=lambda *a:calls.append(a),_api_patch=lambda *a:calls.append(a)))
+ assert fn('http://unused','railiance-cluster',None,False,False) is report and calls==[]
+
+def test_unrelated_fix_context_not_refused():
+ report=actual_report();report.add(severity='WARN',check_id='C-05',message='ordinary',fixable=True,_fix_context={'ws_id':str(uuid.uuid4())})
+ guard=extracted('scripts/consistency_check.py','_refuse_frozen_fix_batch',dict(FROZEN_CLUSTER_BINDINGS=h.FROZEN_CLUSTER_BINDINGS))
+ assert not guard(report,'other')
+
+
+def test_check_repo_guard_precedes_binding_sync_and_does_not_duplicate():
+ tree=ast.parse((ROOT/'scripts/consistency_check.py').read_text())
+ node=next(n for n in tree.body if isinstance(n,ast.FunctionDef) and n.name=='check_repo')
+ guard_lines=[n.lineno for n in ast.walk(node) if isinstance(n,ast.Call) and isinstance(n.func,ast.Name) and n.func.id=='_refuse_frozen_fix_batch']
+ sync_lines=[n.lineno for n in ast.walk(node) if isinstance(n,ast.Call) and isinstance(n.func,ast.Name) and n.func.id=='_sync_workplan_bindings']
+ assert len(guard_lines)==len(sync_lines)==1 and guard_lines[0]<sync_lines[0]
+ report=actual_report();report.add(severity='FAIL',check_id='C-44',message='already refused')
+ guard=extracted('scripts/consistency_check.py','_refuse_frozen_fix_batch',dict(FROZEN_CLUSTER_BINDINGS=h.FROZEN_CLUSTER_BINDINGS))
+ assert guard(report,'railiance-cluster') and len(report.issues)==1

View file

@ -0,0 +1,17 @@
{
"isolated_regression_tests": 75,
"pytest_exit": 0,
"pytest_command": "python3 -m pytest --noconftest -q state-hub/tests/test_frozen_binding_guard_isolated.py",
"normal_make_render_exit": 0,
"render_complete_tracked_chart": true,
"rendered_objects": 8,
"runtime_images": [
"forgejo.coulomb.social/coulomb/state-hub:main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015",
"forgejo.coulomb.social/coulomb/state-hub:main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015",
"forgejo.coulomb.social/coulomb/state-hub:main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015"
],
"classification_check_exit": 0,
"no_DB_or_cluster_access": true,
"no_canonical_mutations": true,
"original_r1_r2_receipts_preserved": true
}

View file

@ -0,0 +1,31 @@
# Prepared publication sequence (do not execute until root go)
The publisher refuses until publication-manifest.json exists with the reviewed
exact ten path hashes, canonical base and publication_stage
root_reviewed_UUIDs_and_T09_completion. Root must first review/integrate R5,
assign only the two normal managed T09/T10 UUID fields, and review the T09 done
source-proof overlay. Do not copy the preparation manifest to satisfy this gate.
T07 remains wait; T10 remains wait/needs_human. No new image or live apply.
Run the canonical normal Make render in a private temporary output directory,
emit only object count/identity/image hashes and reject other changes. Then:
python3 publish_followthrough_source.py
python3 publish_followthrough_source.py --execute-source-commit
The helper hashes/preserves unrelated index entries and dirty files, stages only
the ten reviewed paths, runs the normal scanner, commits with --only and normal
hooks, verifies exact committed paths; normal sync below publishes remote main. It never stashes/resets
or adds unrelated dirty brief/index/weekly evidence. Scanner/hook/push refusal
stops; no bypass or blind retry.
After source publication, use the required repo-local normal fast sync:
make sync SYNC_PATH=. SYNC_PUSH=1 STATE_HUB_API_BASE=http://127.0.0.1:8000
Verify explicit applied primary/railiance01 receipt, exact expected/derived
revision and read back all ten STATE95 tasks with exact UUIDs/status. Retain
original failure and compare fresh CURRENT historical full-row hashes around
fixed normal Cluster consistency/projection separately; never claim original
preimages restored. Root performs this authorized owner sync, not this helper.
New runtime CI/image inspection and scoped approval remain T10.

View file

@ -0,0 +1,53 @@
# Immutable ordinary-binding repair, source revision 5
Canonical base: 086d79a2736e34b916bd7de3c8a65e2ea38a3d64. The ten-path patch includes the previously reviewed six-path followthrough and four guard/test paths. Originals and the actual failed publication receipt remain untouched.
The approved revision-4 receiving rollout preserved all 18 rows at its verification stage. A later ordinary Cluster publication changed exactly three parent full-row hashes; all 15 task hashes remained unchanged. The binding endpoint assigns a new backing sync timestamp unconditionally, providing a concrete mutation path. Hash-only snapshots do not establish the exact changed columns or supply original preimages. This patch does not restore them.
Only the three existing task-bound railiance-cluster registry entries receive this guard. The CLI requires all three scan members, validates their exact source bytes and metadata, and omits valid immutable bindings before contacting any receiver. A refused batch becomes C-44 FAIL and returns a nonzero exit even in remote-all mode. The API independently validates all request metadata before any assignment, locks and refreshes parent/task rows, checks actual repo ownership and existing terminal required-bound tasks, and skips every setter for valid frozen rows. Additional children retain existing Forge receiving semantics; no new child-set invariant is introduced. The ordinary binding response retains updated/received and adds preserved. Frozen-only requests perform no commit or cache invalidation. Existing get_session uses an async-session context manager, which closes the read transaction and releases locks on exit.
Validation: 102 isolated tests pass with --noconftest. Tests extract actual source functions/classes without importing owner app configuration or opening a DB. They verify all three exact sources, missing scan member/UUID, changed source and metadata, foreign names, complete mixed-batch refusal, API ownership and terminal-state refusal, no frozen setters/commit, ordinary compatibility, lock/refresh query intent and actual CLI exit criteria. The full tracked chart plus overlay passes normal Make render/classification check: eight objects, three unchanged digest-qualified images. No concurrency/native DB behavior is claimed by these source tests.
T06 remains done for the approved delivery at its verification stage; T07 remains wait for failed whole-pipeline preservation. T09 is progress until source publication. T10 is wait/needs_human for a separate exact new runtime approval. New task UUIDs are intentionally unallocated pending root review and normal managed allocation. Preserve the original dirty brief/index/weekly evidence and index state.
## Next runtime proposal preparation
After reviewed source integration and normal publication, obtain normal CI status and resolve the resulting exact immutable image manifest. Inspect selected delivered source hashes, including historical_projection.py, workstreams.py and the installed closure; do not reuse the prior main-51bc70d runtime for the new API guard. Prepare digest-qualified packaged-chart reuse-values server dry-run, compare intended object identities/spec hashes, collect fresh release/headroom/schema and full-row hash baseline, then record a new scoped proposal and supervisor disposition. Current known prior baseline was Helm 76/schema c4d5e6f7a8b9; refresh rather than assume it remains current. CI revision, new image digest, final chart package hash and exact execution digest remain unresolved until publication.
No new rollout is authorized by the prior revision-4 approval. A new rollout must retain migration closure and rollback limits: Helm rollback does not downgrade schema, and timeout/atomic does not necessarily stop an active hook Job. Cancellation may address only the exact owned trial Job UID/image after identity verification. Unknown migration recovery or connection/lock quiescence stops for attended owner handling; no credential grant or invented DB-session mapping. EUR cost and risk are unknown until reviewed; no bounded-outage assertion. New baseline preservation and rollback-only refusal probes prove the new stage, never restoration of the unavailable original parent preimages. T07/RMGR19T06 cannot close on source publication alone.
Revision 4 retains frozen revision 3 and adds central consistency repair-batch
refusal. The same helper checks exact registered parent/task UUIDs and source
paths before check_repo can issue an index binding PUT and again before
fix_repo can invoke reconcile, push, metadata registration or individual fixes.
A protected repair finding refuses the complete batch with C-44, including
when an ordinary fix appears first. Existing C-44 refusal is not duplicated.
This prevents generic C-04/C-10/C-22 and related fixes from modifying frozen
rows through other APIs. It does not disable ordinary checks or silently
accept source/DB drift: owner review and the waiting live records remain.
Tests invoke the actual fix engine with mixed fix lists for all three parents,
exercise bound task/source contexts, and verify unchanged ordinary behavior.
API preservation/refusal simulations now exercise all three parent closures.
The post-publication client regression can compare fresh CURRENT full-row hashes
before/after the fixed normal Cluster deep consistency and fast projection.
That is a separate stage from the preserved original failure; equal fresh
hashes do not restore or accept the unavailable original parent preimages.
For T10 prefer the exact originally approved b5c chart archive with only the
newly verified immutable guard image changed, after artifact identity and
server dry-run comparison. Do not infer authority to roll out the source chart's
v1.1 classification ConfigMap or other chart metadata changes. Source Make
pins continue to declare the actually delivered 51bc70d@c802 artifact until a
new exact runtime is approved and verified.
Revision 5 retains revision 4 and narrows scalar identity detection to actual
mutation targets: issue.db_id, ws_id/task_id, from-workplan/workstream/task,
desired creation identities and nested state_hub bindings. Incoming dependency
targets to_workstream_id/to_workplan_id/to_task_id remain legitimate references;
they do not mutate a protected row. Exact protected source paths still refuse.
The real C-20 engine posts against from_workstream_id and uses its to IDs only
as dependency body references. Tests cover incoming ordinary edges and protected
origins for all three histories, including nested binding metadata.

View file

@ -0,0 +1,344 @@
---
id: STATE-WP-0095
type: workplan
title: "Preserve exact closed historical source bindings in Forge receiving"
domain: infotech
repo: state-hub
status: active
owner: codex
created: "2026-10-04"
updated: "2026-10-17"
flavor: implementation
depends_on: []
state_hub_workstream_id: "79579572-8120-5d3b-aca5-dd4aff60d058"
---
## Goal
Normal Forge receiving must preserve the seven bound Activity legacy source
views and the Custodian retired ADHOC source without new identities, source
rewrites, task movement, deletion, relabeling or unretirement. This implements
receiving support before RMGR-WP-0019 registrar admission. CUST-WP-0077 owns
cross-owner coordination; deployment and replay remain explicit owner returns.
## Verify source bindings and shared views
```task
id: STATE-WP-0095-T01
status: done
priority: high
state_hub_task_id: "6a5f87bb-e63d-5e20-b9fc-c1b5cd403ff7"
```
Reviewed seven exact Activity files: four existing workplans and 64 UUID-bound
closed tasks. The archived custodian-WP-0003 parent and three split_from views
share one existing parent UUID; every repeated task binding/content agrees.
Custodian ADHOC-2026-08-25-retired retains parent
1c53d3db-6633-5e2d-987f-10706d73be9e and task
d8af3e6b-26ae-580c-bbeb-caa8ad419bec. No identity migration is authorized.
## Implement preservation receiving and global collision refusal
```task
id: STATE-WP-0095-T02
status: done
priority: high
state_hub_task_id: "ebb24194-e4cc-5130-aaa6-663f2ea2c822"
```
Prepared exact source digest manifest, existing-parent/task verification,
immutable receiving and UUID-based historical diff. Ordinary canonical sources
continue deriving their identities. Missing/changed/symlinked source, absent
bound rows or nonterminal hub histories refuse. Check task UUID holders globally,
including retired and acknowledged-stale parent workplans, before INSERT.
Offline preview tests and snapshot proof are evidence of source behavior only;
finish after applying reviewed source and its focused tests to the owning repo.
## Deliver receiving through normal deployment and verify owner return
```task
id: STATE-WP-0095-T03
status: done
priority: high
depends_on: [STATE-WP-0095-T02]
state_hub_task_id: "e8566341-cf6e-5b9c-ad80-8564d14cd52a"
decision_id: "36772026-f43d-4331-b466-5f4b1afe4eca"
```
No direct live code patch. This task is a deployment return, not policy authority
or permission to resolve identity decisions. Exact receiving fixture acceptance
and global collision rejection must hold in the delivered version.
## Verify normal historical replay preserves existing identities
```task
id: STATE-WP-0095-T04
status: done
priority: high
depends_on: [STATE-WP-0095-T03, RMGR-WP-0019-T03]
state_hub_task_id: "3bbb311f-194e-5862-8aed-358a580f22b4"
```
Verify Activity four historical parent UUIDs/64 task UUIDs and Custodian retired
parent/task unchanged; no historical row creation, movement, deletion,
relabeling or unretirement. Unrelated stale projections remain separate decisions.
2026-10-05: Source exact-set retirement support now exists. Repository projection
reconcile accepts `expected_retirement_uuids`; that set must equal the stale
projections, and any task or dependency edge on a stale row refuses the call.
Live apply still needs a reviewed rollout. On 2026-10-05 the founder accepted
review `review-1ece4f3e-d0e3-4fd6-abc8-272f1b1876f4`, which names only the three
empty projections and preserves parent `b4eb45a9-69e3-4ab0-b00c-67a53c3117c5`.
That acceptance is not a live retirement and does not resolve
`91b7c633-a66f-5d43-9f4b-6565aefb9356`.
## Offline evidence, 2026-10-04
Prepared receiver suite: Forge projection regression plus historical preservation,
missing/changed source, terminality, immutable UUID diff, and global holder query.
Real-source offline snapshot proof preserves four Activity parents/64 tasks and
one Custodian parent/task without mutations; no API writes or deployment occurred.
Exact original bindings are maintained in api/services/historical_projection.py.
2026-10-04 actual owning-source validation: 155 focused receiver/qualifier/planner
cases passed; the independent offline current-source/current-hub-snapshot proof
retained five existing parents and 65 task UUIDs, no mutations, clean historical
diff. T02 is source-complete; T03/T04 remain wait for reviewed deployment and
normal registrar/replay receiving, respectively. Evidence: docs/evidence/2026-10-04-historical-receiving-source.json.
## Verified receiving rollout and remaining replay gate, 2026-10-04
T03 is done on actual founder approval of proposal
STATE-WP-0095-T03-historical-receiving revision 1, SHA256
`d6789359b3fe7988a6201fff6f367cb46948aa197e1cc0ca66c31f45e1ea9981`,
approval receipt reference `user_reply:call_o87uEfFs3AtXfxO5YqUnSpcg:0`.
The normal owner Helm path delivered `main-5dd3df9`, commit
`5dd3df9c69eb564af10ea5ea01e349786212501a`, revision 72; API and MCP are Ready,
primary/railiance01 health and schema a0b1c2d3e4f5 verified, running source
hashes match. Rollback remains revision 71; no receipt-table downgrade is needed.
Actual historical preservation and global task-holder refusal were verified in
the delivered version. Durable receipt:
[verified historical receiving rollout](../docs/evidence/2026-10-04-historical-receiving-rollout.json).
Custodian immutable replay applied while preserving the retired historical
parent/task UUIDs. Activity immutable replay retained all four historical parent
and 64 task UUIDs and historical row contents, but refused transactionally on
three unrelated stale projections: custodian-wp-0003 (archived),
custodian-wp-0003a (done), custodian-wp-0003b (done). No retirement acknowledgement
was issued and that refusal is not represented as normal replay success.
Registrar source `c41d36c2` has 38 actual passing tests and Repo Manager's own
normal sync succeeded; registrar admission does not authorize retiring Activity
projections. T04 therefore remains wait, retaining RMGR-WP-0019-T03 and awaiting
Activity Core's precise reviewed disposition of those three stale rows. No
rename, identity migration, new paid native trial or further live action is
included in this source status update.
## Activity Core return, 2026-10-06
Activity Core returned the exact disposition (message
`cf2d5847-8b3f-4653-9d9f-d5fcfbe3f1e4`, ACTIVITY-WP-0045, activity-core commit
`941fac0`). `expected_retirement_uuids`:
`683724d9-4249-5e0e-88e0-7baff4330d58` (custodian-wp-0003),
`644017c7-c5f2-5a64-81c3-d8ee806f8251` (custodian-wp-0003a),
`a5806df9-5b9a-50da-81c1-7e606076bed9` (custodian-wp-0003b). Each has zero
tasks and zero dependency edges. None is preserved parent
`b4eb45a9-69e3-4ab0-b00c-67a53c3117c5`. Repo Manager independently verified the
set (message `916841f6`, repo-manager commit `498682c`). Nothing was retired.
T04 now waits on decision `91b7c633` and a reviewed live rollout. After that,
Repo Manager re-runs the Activity owner sync and closes RMGR-WP-0019-T03.
## Retirement rollout and T04 verification, 2026-10-06
The founder resolved decision `91b7c633` (retire exactly the three projections)
and approved the rollout. The founder ran the normal Helm upgrade to
`main-968a811` (revision 74; rollback revision 73, `main-fad9b3f`). API and MCP
are Ready, health is ok on primary/railiance01, and schema `b3c4d5e6f7a8` is
unchanged; no migrations. The activity-core reconcile at commit
`67a92d2ff8e033baf2c36998d24187fe83edec84` with `expected_retirement_uuids` set
to the three UUIDs applied: 0 created, 3 retired (tombstoned with an
`@retired-20261006` slug; UUIDs and statuses kept), 0 refused, 0 tasks
cancelled. All 7 bound Activity source files still match their digests. The four
historical parents and 64 task UUIDs are present under their bound parents with
unchanged fields. The Custodian retired parent and task are unchanged. Activity
had 54 workplans and 288 tasks before and after. Evidence:
[activity stale retirement rollout](../docs/evidence/2026-10-06-activity-stale-retirement-rollout.json).
Repo Manager's re-run of the Activity owner sync (RMGR-WP-0019-T03) and
ACTIVITY-WP-0045-T02 remain with their owners.
## Cluster exact historical receiving successor, 2026-10-17
This scoped extension retains the completed Activity/Custodian outcomes above.
Three frozen cluster archives contain three existing parent UUIDs and 15 existing
closed task UUIDs. Extend the existing exact historical receiving path before
registrar admission; never derive replacement identities or modify those
sources. Cross-owner observation attaches to existing SHR-WP-0001, with
RMGR-WP-0019 as obligor/beneficiary and NK-WP-0049-T35 benefiting indirectly.
The local derived case is waiting; no live worker injection is authorized.
## Prepare exact cluster source preservation and refusal cases
```task
id: STATE-WP-0095-T05
status: done
priority: high
state_hub_task_id: "339b9a37-181c-5599-9a89-0393e199bd4b"
```
Prepared the same full-file SHA256 and 15 task binding manifest as Repo Manager.
Candidate receiving tests pass 26: existing rows remain entirely unchanged;
missing/foreign/nonterminal rows and unknown, changed, missing or symlinked
source refuse. Read-only public metadata confirms all 18 existing rows have the
expected ownership and terminal status. These are source and metadata checks,
not delivered receiving or a committed native trial.
## Deliver reviewed cluster receiving successor before registrar admission
```task
id: STATE-WP-0095-T06
status: done
priority: high
depends_on: [STATE-WP-0095-T05]
needs_human: false
intervention_note: "Exact revision 4 approved and verified; no pending rollout intervention."
state_hub_task_id: "6af973b4-1964-5d4d-a2f8-8dbd4c01fda4"
```
Completed 2026-10-17 on the founder's approval of exact revision 4 of stable
proposal STATE-WP-0095-T06-cluster-frozen-receiving-r1 (decision
e3798419-8aa9-5425-8c08-20df6bd82a19; approved digest
sha256:63a02e8296d126c0158b7eb2c4ce79c7eeb6f8d6f61298d473c1065c7557ba32).
The ordinary owner Helm path delivered revision 76, runtime source 51bc70d and
exact image manifest sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015.
API/MCP Ready and primary/railiance01 health report expected schema
c4d5e6f7a8b9; delivered historical module sha256:e3c4bf965764b32a6df5d61c71272ee22569df9a9ddae2a3fc3df97ec554e0b2 matches.
All three parent and 15 task full DB row hashes/columns/UUIDs remained unchanged.
Five actual native probes passed: preserve, actual foreign-parent holder move,
nonterminal task, nonterminal parent and missing task. Every probe rolled back;
a fresh transaction confirmed complete row restoration. The foreign-holder
case precisely verified historical binding refusal, not the separate global
UUID-holder creation branch. No committed probe writes were made.
Evidence: [verified receiving rollout](../docs/evidence/2026-10-17-cluster-historical-receiving-rollout.json).
This actual receiving return satisfies the prerequisite of RMGR-WP-0019-T05.
Normal cluster publication and its immutable-source/row return remain T07;
source readiness and this deployment do not count that later acceptance twice.
The additive inbox migration is retained; Helm rollback does not downgrade
schema or stop an active migration Job. Original failures and exact-UID recovery
limits remain in the referenced approved packet. No TLS recovery/native private
admission or new autonomy/cost grant is inferred.
## Verify ordinary cluster receiving and immutable historical rows
```task
id: STATE-WP-0095-T07
status: wait
priority: high
depends_on: [STATE-WP-0095-T06, RMGR-WP-0019-T06]
state_hub_task_id: "cfd1ea99-e75b-542e-a434-acefb62f3c7d"
```
Capture normal cluster owner publication/receiving receipts and compare entire
existing row snapshots and exact source bytes. No creation, movement, relabel,
unretirement or retirement is authorized for these histories. A rollback first
fences cluster sync and restores registrar refusal before removing receiving
support; it never rewrites archived IDs or UUIDs. This work does not establish
original TLS recovery or owner-native admission.
## Fix policy-compatible Secret-template server dry-run preparation
```task
id: STATE-WP-0095-T08
status: done
priority: high
state_hub_task_id: "2dd9b04f-2250-5a8a-b5bc-c8a009872858"
```
Prepared the narrow Makefile successor after the existing readiness command
refused its first Secret-template phase. Ordinary client-side apply adds the
last-applied annotation, correctly denied by the existing Platform
reject-secret-last-applied policy. Only the two Secret-template server
dry-run requests now use server-side apply with dry-run; no force-conflicts,
Secret value change, policy relaxation or actual Secret write is introduced.
Command-fixture tests verify both requests and preserve other dry-run phases.
Evidence and original refusal remain in the Custodian cluster historical
publication packet, revisions/r2 and r3-readiness. This is completed source
preparation; T06 separately records the verified actual rollout. The runtime image
continues to be main-51bc70d; Makefile/workplan changes do not create a new
application image or native acceptance.
T08 source repair refinement, 2026-10-17: the required normal Make render
correctly refused a pre-existing generated chart classification copy at v1.0
after authoritative canon reached v1.1. Regenerated only through the existing
scripts/sync_classification_allowed.py, preserving canon and strict check.
The exact source diff is version 1.0→1.1 plus publication/history/participation.
Normal Make rendering and the focused classification check passed in an
isolated owner-layout copy. The original refusal is retained in the Custodian
owner-followthrough-r1 validation receipt. This is source repair only: no live
ConfigMap apply, new runtime image, policy relaxation or T07 completion.
T07 actual ordinary-publication return, 2026-10-17: cluster owner deep consistency
and normal fast projection completed, exact archived source bytes and all 15
historical task full-row hashes remained unchanged. The later full-row check
FAILED for exactly the three parent rows. The earlier approved T06 rollout had
preserved all 18 rows at its verification stage; that success is not the later
whole-pipeline acceptance. The original snapshots retain hashes only, so exact
changed columns and original field preimages are not established. No guessed
DB repair or claim that all 18 original row hashes are restored is authorized.
T07 and RMGR-WP-0019-T06 remain waiting pending T09/T10 and a reviewed return.
## Guard immutable historical index bindings in CLI and receiving API
```task
id: STATE-WP-0095-T09
status: done
priority: high
depends_on: [STATE-WP-0095-T06]
state_hub_task_id: "8be8f13b-6352-5295-9755-de6fbfb13593"
```
Prepare and publish the narrowly scoped ordinary-binding repair for the three
existing task-bound Cluster registry entries. The CLI verifies exact frozen
source metadata/bytes and omits those bindings, including on old receivers.
The API independently validates the complete batch before any mutation, checks
actual repo ownership and the existing terminal parent/task closure, and
preserves valid frozen rows without field or timestamp assignments. Invalid
metadata, missing/foreign/nonterminal rows refuse the batch. Unknown ordinary
bindings retain existing behavior; historical IDs, UUIDs and source bytes stay
unchanged. Isolated regression tests and normal owner rendering are required.
Reviewed source preparation and canonical regression proof completed on
2026-10-17: 102 isolated tests pass and normal Make render/classification
check passes (eight objects, three unchanged delivered runtime images).
This completion record accompanies the same source publication/projection;
source preparation does not establish delivered runtime or T07 acceptance.
Central check/fix engines also refuse the complete repair batch if any fixable
context touches an exact registered parent/task UUID or frozen source path.
This runs before index bindings and before reconcile/push/individual fixes;
ordinary earlier fixes cannot partially execute before a later frozen refusal.
## Deliver and verify the ordinary-binding guard successor
```task
id: STATE-WP-0095-T10
status: wait
priority: high
depends_on: [STATE-WP-0095-T09]
needs_human: true
intervention_note: "New API runtime requires its own exact image, reviewed preflight and scoped rollout approval; prior revision-4 approval does not cover it."
state_hub_task_id: "88585c69-d9b9-5e1f-a256-6416ff71e824"
```
After source publication, resolve normal CI status and immutable image identity,
verify delivered guard source closure, prepare exact chart/reuse-values dry-run,
full closure/headroom/schema baseline and conditional recovery. Retain additive
migration and active-Job cancellation limitations; no Helm schema downgrade or
bounded-outage claim. Obtain scoped approval before live rollout. Then verify
new full-row baseline preservation and actual rollback-only refusal probes,
and only then return to T07 ordinary-pipeline acceptance. This prevents future
binding mutation; it does not reconstruct or restore original parent hashes.
No production changes, new autonomy or credential authority are granted here.

View file

@ -0,0 +1,23 @@
--- a/workplans/STATE-WP-0095-historical-source-receiving.md
+++ b/workplans/STATE-WP-0095-historical-source-receiving.md
@@ -296,7 +296,7 @@
```task
id: STATE-WP-0095-T09
-status: progress
+status: done
priority: high
depends_on: [STATE-WP-0095-T06]
state_hub_task_id: "8be8f13b-6352-5295-9755-de6fbfb13593"
@@ -311,7 +311,10 @@
metadata, missing/foreign/nonterminal rows refuse the batch. Unknown ordinary
bindings retain existing behavior; historical IDs, UUIDs and source bytes stay
unchanged. Isolated regression tests and normal owner rendering are required.
-Mark done only after reviewed source publication and normal owner projection;
+Reviewed source preparation and canonical regression proof completed on
+2026-10-17: 102 isolated tests pass and normal Make render/classification
+check passes (eight objects, three unchanged delivered runtime images).
+This completion record accompanies the same source publication/projection;
source preparation does not establish delivered runtime or T07 acceptance.
Central check/fix engines also refuse the complete repair batch if any fixable
context touches an exact registered parent/task UUID or frozen source path.

View file

@ -0,0 +1,12 @@
from pathlib import Path
import hashlib,json
p=Path("/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-16/cluster-historical-publication-r1/owner-followthrough-r5")
wp=Path("/home/worsch/state-hub/workplans/STATE-WP-0095-historical-source-receiving.md")
assert hashlib.sha256(wp.read_bytes()).hexdigest()=="5d250bcf89c7bc3b3dd81a8b24f57dfbeef69a04f5722f3e76cc3527f143c77c"
new=(p/"STATE95-final-reviewed-overlay.md").read_bytes()
assert hashlib.sha256(new).hexdigest()=="a9aa50f5772ac985dcee9b1b65228e5f909ab25209f80dea9d6f0248029855ca"
wp.write_bytes(new)
d=json.loads((p/"publication-manifest.json").read_text());assert d["publication_stage"]=="pending_root_review_UUIDs_and_T09_completion"
d["publication_stage"]="root_reviewed_UUIDs_and_T09_completion"
(p/"publication-manifest.json").write_text(json.dumps(d,indent=2)+"\n")
print("Reviewed T09 completion overlay applied; no commit yet")

View file

@ -0,0 +1,13 @@
{
"canonical_base": "086d79a2736e34b916bd7de3c8a65e2ea38a3d64",
"tests_passed": 102,
"tests_seconds": 9.49,
"pytest_warning": "system pytest lacks asyncio plugin option; isolated tests use asyncio.run and passed",
"normal_make_render_exit": 0,
"objects": 8,
"existing_digest_images": 3,
"whole_unrelated_index_and_dirty_state_preserved": true,
"canonical_commit": false,
"Hub_projection": false,
"runtime_changed": false
}

View file

@ -0,0 +1,42 @@
#!/usr/bin/env python3
"""Exact reviewed local source integration and two normal managed UUIDs only."""
import argparse, hashlib, importlib.util, json, sys
from pathlib import Path
BASE=Path(__file__).resolve().parent
OWNER=Path('/home/worsch/state-hub')
EXPECTED_PATCH='ad3d49b83435dd026118ee1e4a25779b5a7739473619bbda256f160c4eb0e56a'
def sha(b):return hashlib.sha256(b).hexdigest()
def main():
ap=argparse.ArgumentParser();ap.add_argument('--execute',action='store_true');args=ap.parse_args()
spec=importlib.util.spec_from_file_location('publisher',BASE/'publish_followthrough_source.py');pub=importlib.util.module_from_spec(spec);spec.loader.exec_module(pub)
expected=json.loads((BASE/'manifest.json').read_text());names={x['path'] for x in expected['files']}
assert len(names)==10 and sha((BASE/'state-hub-followthrough.patch').read_bytes())==EXPECTED_PATCH
assert pub.git(OWNER,'rev-parse','HEAD').stdout.decode().strip()==expected['base_revision']
for entry in expected['files']:
target=OWNER/entry['path'];source=BASE/'state-hub'/entry['path']
assert not target.is_symlink() and not source.is_symlink()
assert sha(source.read_bytes())==entry['candidate_sha256']
assert (sha(target.read_bytes()) if target.exists() else None)==entry['base_sha256']
assert entry['path'] not in pub.index_entries(OWNER) or not pub.git(OWNER,'diff','--cached','--',entry['path']).stdout
before=pub.preserved_state(OWNER,names)
receipt=dict(schema='state95.local-source-integration.v1',status='preflight_only',canonical_revision=expected['base_revision'],patch_sha256='sha256:'+EXPECTED_PATCH,preserved_before=before,live_effects=False)
if args.execute:
for entry in expected['files']:
target=OWNER/entry['path'];target.parent.mkdir(parents=True,exist_ok=True);target.write_bytes((BASE/'state-hub'/entry['path']).read_bytes())
sys.path.insert(0,'/home/worsch/repo-manager/src')
from repo_manager.identifiers import ensure_missing_work_record_identifiers
preview=ensure_missing_work_record_identifiers(OWNER,execute=False)
allowed={'STATE-WP-0095-T09','STATE-WP-0095-T10'}
assert len(preview['assignments'])==2 and {a['record_id'] for a in preview['assignments']}==allowed
assert all(a['path']=='workplans/STATE-WP-0095-historical-source-receiving.md' and a['kind']=='task' for a in preview['assignments'])
assigned=ensure_missing_work_record_identifiers(OWNER,execute=True)
assert assigned['assignments']==preview['assignments']
wp='workplans/STATE-WP-0095-historical-source-receiving.md'
source=(BASE/'state-hub'/wp).read_text();actual=(OWNER/wp).read_text()
for a in assigned['assignments']:actual=actual.replace('state_hub_task_id: "'+a['uuid']+'"\n','')
assert actual==source
assert pub.preserved_state(OWNER,names)==before
receipt.update(status='integrated_managed_UUIDs_not_committed',managed_assignment=assigned,preserved_after=before)
(BASE/'local-integration-result.json').write_text(json.dumps(receipt,indent=2)+'\n')
print(json.dumps({k:v for k,v in receipt.items() if k not in {'preserved_before','preserved_after'}}))
if __name__=='__main__':main()