Admit reviewed receiver evidence batch 1
This commit is contained in:
parent
f8c7dfa2a9
commit
4b647dc32f
24 changed files with 2857 additions and 0 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
@ -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"
|
||||
+ }
|
||||
+ ]
|
||||
+}
|
||||
|
|
@ -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."
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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."
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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")
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue