docs: preserve reviewed NetKingdom session artifacts
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Reject generated-only commits / generated-commit-guard (push) Successful in 9s

This commit is contained in:
codex 2026-11-01 22:51:41 +01:00
parent ccf5a95ce3
commit a0d684116a
43 changed files with 21999 additions and 0 deletions

View file

@ -0,0 +1,143 @@
from pathlib import Path
import ast, asyncio, importlib.util, types, uuid
from dataclasses import dataclass, field
from datetime import datetime, timezone
import pytest
ROOT = Path(__file__).resolve().parents[1]
spec=importlib.util.spec_from_file_location('historical',ROOT/'api/services/historical_projection.py')
h=importlib.util.module_from_spec(spec);spec.loader.exec_module(h)
B=h.FROZEN_CLUSTER_BINDINGS[0]
def binding(**changes):
d=dict(workplan_id=uuid.UUID(B['uuid']),repo_slug=B['repo'],filename=Path(B['path']).name,relative_path=B['path'],archived=True,status='finished');d.update(changes);return d
class Obj:
def __init__(self,**kw):self.__dict__.update(kw);self.writes=[]
def __setattr__(self,k,v):
if 'writes' in self.__dict__:self.writes.append(k)
object.__setattr__(self,k,v)
class Entry(Obj):
def model_dump(self):return {k:v for k,v in self.__dict__.items() if k!='writes'}
class Col:
def in_(self,x):return self
def __eq__(self,x):return self
class Query:
def __init__(self):self.locked=False;self.refreshed=False;self.ordered=False
def where(self,*a):return self
def order_by(self,*a):self.ordered=True;return self
def with_for_update(self):self.locked=True;return self
def execution_options(self,**kw):self.refreshed=kw.get("populate_existing") is True;return self
class Model: id=Col();slug=Col();workplan_id=Col()
class Result:
def __init__(self,values):self.values=values
def scalars(self):return self
def all(self):return self.values
def scalar_one_or_none(self):return self.values[0] if self.values else None
class Session:
def __init__(self,rows):self.results=iter(rows);self.commits=0;self.queries=[]
async def execute(self,q):self.queries.append(q);return Result(next(self.results))
async def commit(self):self.commits+=1
class HTTPException(Exception):
def __init__(self,status_code,detail):self.status_code=status_code;super().__init__(detail)
def extracted(path,name,extra):
tree=ast.parse((ROOT/path).read_text());node=next(n for n in tree.body if isinstance(n,(ast.FunctionDef,ast.AsyncFunctionDef)) and n.name==name)
node.decorator_list=[];node.returns=None
for a in node.args.args + node.args.kwonlyargs:a.annotation=None
node.args.defaults=[]
ns=dict(Path=Path,datetime=datetime,timezone=timezone,**extra)
exec(compile(ast.Module(body=[node],type_ignores=[]),str(path),'exec'),ns);return ns[name]
def actual_report():
tree=ast.parse((ROOT/'scripts/consistency_check.py').read_text())
nodes=[n for n in tree.body if (isinstance(n,ast.ClassDef) and n.name in {'Issue','ConsistencyReport'}) or (isinstance(n,ast.AnnAssign) and isinstance(n.target,ast.Name) and n.target.id=='_AUTOMATION_ERROR_CHECKS')]
ns=dict(dataclass=dataclass,field=field,_AUTOMATION_ERROR_CHECKS=frozenset({'C-00','C-44'}))
exec(compile(ast.Module(body=nodes,type_ignores=[]),'actual_report','exec'),ns)
return ns['ConsistencyReport']('railiance-cluster','/unused')
def closure():
repo=Obj(id=uuid.uuid4());parent=Obj(id=uuid.UUID(B['uuid']),repo_id=repo.id,status='finished')
tasks=[Obj(id=uuid.UUID(t['uuid']),workplan_id=parent.id,status='done') for t in B['tasks']]
return repo,parent,tasks
async def run_api(entries,repo,parent,tasks,ordinary=None):
rows=([parent] if parent else [])+([ordinary] if ordinary else [])
session=Session([rows,[repo] if repo else [],tasks])
fn=extracted('api/routers/workstreams.py','sync_workplan_bindings',dict(frozen_cluster_binding=h.frozen_cluster_binding,validate_frozen_cluster_rows=h.validate_frozen_cluster_rows,HTTPException=HTTPException,select=lambda *_:Query(),Workplan=Model,Task=Model,ManagedRepo=Model,_invalidate_workplan_index_cache=lambda:None))
result=await fn(Obj(bindings=[Entry(**e) for e in entries]),session)
return result,session
@pytest.mark.parametrize('change',[dict(repo_slug='foreign'),dict(workplan_id=uuid.uuid4()),dict(relative_path='workplans/other.md'),dict(filename='other.md'),dict(archived=False),dict(status='active')])
def test_metadata_refuses(change):
with pytest.raises(ValueError):h.frozen_cluster_binding(binding(**change))
def test_unrelated_binding_unchanged():assert h.frozen_cluster_binding(binding(workplan_id=uuid.uuid4(),filename='ordinary.md',relative_path='workplans/ordinary.md',repo_slug='other')) is None
@pytest.mark.parametrize('fault',['parent_missing','foreign_repo','parent_active','task_missing','task_active','task_foreign','repo_missing'])
def test_mixed_batch_refusal_precedes_all_writes(fault):
repo,parent,tasks=closure();ordinary=Obj(id=uuid.uuid4())
if fault=='repo_missing':repo=None
if fault=='parent_missing':parent=None
if fault=='foreign_repo':parent.repo_id=uuid.uuid4()
if fault=='parent_active':parent.status='active'
if fault=='task_missing':tasks.pop()
if fault=='task_active':tasks[0].status='progress'
if fault=='task_foreign':tasks[0].workplan_id=uuid.uuid4()
if parent:parent.writes.clear()
for t in tasks:t.writes.clear()
normal=binding(workplan_id=ordinary.id,repo_slug='ordinary',filename='ordinary.md',relative_path='workplans/ordinary.md')
with pytest.raises(HTTPException):asyncio.run(run_api([normal,binding()],repo,parent,tasks,ordinary))
assert ordinary.writes==[]
if parent:assert parent.writes==[]
assert all(t.writes==[] for t in tasks)
def test_exact_frozen_no_setters_or_commit():
repo,parent,tasks=closure();result,session=asyncio.run(run_api([binding()],repo,parent,tasks))
assert result==dict(updated=0,received=1,preserved=1);assert session.commits==0;assert parent.writes==[]
assert all(q.locked and q.refreshed and q.ordered for q in (session.queries[0],session.queries[2]))
def test_mixed_valid_preserves_frozen_and_updates_ordinary():
repo,parent,tasks=closure();ordinary=Obj(id=uuid.uuid4())
normal=binding(workplan_id=ordinary.id,repo_slug='ordinary',filename='ordinary.md',relative_path='workplans/ordinary.md')
result,session=asyncio.run(run_api([normal,binding()],repo,parent,tasks,ordinary))
assert result==dict(updated=1,received=2,preserved=1);assert session.commits==1;assert parent.writes==[]
assert ordinary.writes==['backing_filename','backing_relative_path','backing_archived','backing_synced_at']
@pytest.mark.parametrize('tamper',['metadata','bytes','symlink','missing_scan','missing_uuid','valid'])
@pytest.mark.parametrize("bound_index", range(3))
def test_cli_exact_source_omission_or_whole_batch_refusal(tmp_path,tamper,bound_index):
bound=h.FROZEN_CLUSTER_BINDINGS[bound_index]
import shutil
infos=[]
for entry in h.FROZEN_CLUSTER_BINDINGS:
target=tmp_path/entry['path'];target.parent.mkdir(parents=True,exist_ok=True)
shutil.copyfile(Path('/home/worsch/railiance-cluster')/entry['path'],target)
infos.append((target,dict(state_hub_workstream_id=entry['uuid'],status='finished'),''))
path=tmp_path/bound['path']
meta=dict(state_hub_workstream_id=bound['uuid'],status='finished')
if tamper=='metadata':meta['status']='active'
if tamper=='missing_uuid':meta.pop('state_hub_workstream_id')
if tamper=='bytes':path.write_text(path.read_text()+'\n')
if tamper=='symlink':path.unlink();path.symlink_to(Path('/home/worsch/railiance-cluster')/bound['path'])
ordinary=tmp_path/'workplans/ordinary.md';ordinary.write_text('ordinary')
if tamper=='missing_scan':infos.pop(bound_index)
else:infos[bound_index]=(path,meta,'')
calls=[];report=actual_report()
fn=extracted('scripts/consistency_check.py','_sync_workplan_bindings',dict(FROZEN_CLUSTER_BINDINGS=h.FROZEN_CLUSTER_BINDINGS,frozen_cluster_binding=h.frozen_cluster_binding,normalise_workstream_status=lambda s:s,workplan_display_path=lambda root,p:p.relative_to(root).as_posix(),_api_put=lambda *a:calls.append(a) or dict(updated=1,received=1)))
fn('http://unused','railiance-cluster',[(ordinary,dict(state_hub_workstream_id=str(uuid.uuid4()),status='active'),''),*infos],tmp_path,report)
if tamper=='valid':assert len(calls)==1 and len(calls[0][2]['bindings'])==1
else:assert calls==[] and report.failures[0].check_id=='C-44' and report.automation_errors
def test_api_tampered_mixed_batch_refuses_before_queries_or_assignments():
repo,parent,tasks=closure();ordinary=Obj(id=uuid.uuid4())
with pytest.raises(HTTPException):
asyncio.run(run_api([binding(workplan_id=ordinary.id,repo_slug='ordinary',filename='ordinary.md',relative_path='workplans/ordinary.md'),binding(archived=False)],repo,parent,tasks,ordinary))
assert ordinary.writes==[] and parent.writes==[]
def test_foreign_repo_same_filename_path_is_ordinary():
assert h.frozen_cluster_binding(binding(workplan_id=uuid.uuid4(),repo_slug='foreign')) is None
@pytest.mark.parametrize('entry',h.FROZEN_CLUSTER_BINDINGS)
def test_all_three_registry_bindings_are_guarded(entry):
good=dict(workplan_id=entry['uuid'],repo_slug=entry['repo'],filename=Path(entry['path']).name,relative_path=entry['path'],archived=True,status='finished')
assert h.frozen_cluster_binding(good) is entry
with pytest.raises(ValueError):h.frozen_cluster_binding(dict(good,status='active'))
def test_real_cli_exit_criterion_is_nonzero_including_remote_all():
report=actual_report();report.add(severity='FAIL',check_id='C-44',message='refused')
fn=extracted('scripts/consistency_check.py','consistency_exit_code',{})
assert fn([report])==1 and fn([report],remote_all=True)==1

View file

@ -0,0 +1,335 @@
---
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: 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.

View file

@ -0,0 +1,517 @@
.PHONY: start install install-cli dashboard-install dashboard-check db db-tools migrate seed api dashboard dashboard-local sync check-primary primary-port-clear check check-local test test-python clean register-project register-codex-project register-mcp configure-codex bootstrap-env dev-hub edge-relay mcp-profile validate-adr add-domain rename-domain add-repo list-repos register-path register-from-classification register-from-classification-all cleanup-stale tunnels-up tunnels-status tunnels-check bridges install-hooks install-hooks-all gitea-inventory token-reconcile railiance-state-hub-render railiance-state-hub-client-dry-run railiance-state-hub-server-dry-run railiance-state-hub-headroom
COMPOSE = docker compose -f infra/docker-compose.yml --env-file .env
PYTHON ?= python3
HELM ?= $(shell command -v helm 2>/dev/null || if [ -x "$$HOME/.local/bin/helm" ]; then printf "%s" "$$HOME/.local/bin/helm"; else printf "%s" "helm"; fi)
KUBECTL ?= $(shell command -v kubectl 2>/dev/null || if [ -x "$$HOME/.local/bin/kubectl" ]; then printf "%s" "$$HOME/.local/bin/kubectl"; else printf "%s" "kubectl"; fi)
RAILIANCE_STATE_HUB_RELEASE ?= state-hub
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-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.
UV ?= $(shell command -v uv 2>/dev/null || if [ -x "$$HOME/.local/bin/uv" ]; then printf "%s" "$$HOME/.local/bin/uv"; else printf "%s" "uv"; fi)
RMGR ?= $(shell command -v rmgr 2>/dev/null || if [ -x "$$HOME/repo-manager/.venv/bin/rmgr" ]; then printf "%s" "$$HOME/repo-manager/.venv/bin/rmgr"; else printf "%s" "rmgr"; fi)
STATE_HUB_API_BASE ?= http://127.0.0.1:8000
SYNC_PATH ?= .
SYNC_PUSH ?= 1
start:
@echo "# Normal production UI access (run in order)"
@echo "make bridges # connect local :8000 to the railiance01 primary"
@echo "make dashboard # verify the primary, then serve the UI on :3000"
@echo "make sync # push file-backed records and reconcile the exact forge commit"
@echo ""
@echo "# Optional local MCP adapter"
@echo "make mcp-http # local SSE adapter on :8001; not needed by the UI"
@echo ""
@echo "# Local fallback/development only"
@echo "make api # local Postgres + API; conflicts with the production :8000 tunnel"
@echo "make dashboard-local # dashboard against the deliberate local API"
install:
$(UV) sync
dashboard/node_modules/.bin/observable: dashboard/package.json dashboard/package-lock.json
cd dashboard && npm ci
dashboard-install: dashboard/node_modules/.bin/observable
dashboard-check: dashboard-install
cd dashboard && npm run build
## Symlink the custodian CLI into ~/.local/bin so it's on PATH system-wide
install-cli: install
mkdir -p ~/.local/bin
ln -sf "$(shell pwd)/.venv/bin/custodian" ~/.local/bin/custodian
ln -sf "$(shell pwd)/.venv/bin/statehub" ~/.local/bin/statehub
@echo "Installed: custodian → $$(readlink -f ~/.local/bin/custodian)"
@echo "Installed: statehub → $$(readlink -f ~/.local/bin/statehub)"
@echo "Make sure ~/.local/bin is on your PATH:"
@echo " echo 'export PATH=\"\$$HOME/.local/bin:\$$PATH\"' >> ~/.bashrc && source ~/.bashrc"
db:
$(COMPOSE) up -d postgres
db-tools:
$(COMPOSE) --profile tools up -d
migrate:
$(UV) run alembic upgrade head
seed:
$(UV) run python scripts/seed.py
## Start (or restart) the MCP SSE server on :8001 — primary transport for Claude Code.
## Remote clients (e.g. COULOMBCORE) connect via the ops-bridge tunnel (port 18001).
## Registration: claude mcp add-json -s user state-hub '{"type":"sse","url":"http://127.0.0.1:8001/sse"}'
mcp-http:
@fuser -k 8001/tcp 2>/dev/null && echo "Stopped running MCP server" || true
MCP_TRANSPORT=sse MCP_PORT=8001 $(UV) run python mcp_server/server.py
## Require the production identity before serving the normal dashboard. This
## prevents an accidental local `make api` from presenting an empty fallback DB
## as the live State Hub.
check-primary:
@health="$$(curl -fsS --max-time 5 http://127.0.0.1:8000/state/health 2>/dev/null)" || { \
echo "ERROR: State Hub primary is not reachable on 127.0.0.1:8000." >&2; \
echo "Run 'make bridges' first." >&2; \
exit 1; \
}; \
identity="$$(printf '%s' "$$health" | $(PYTHON) -c 'import json, sys; d=json.load(sys.stdin); print("{}/{}".format(d.get("instance_role", ""), d.get("instance_label", "")))')"; \
if [ "$$identity" != "primary/railiance01" ]; then \
echo "ERROR: 127.0.0.1:8000 is '$$identity', not the primary/railiance01 State Hub." >&2; \
echo "Stop the local API, then run 'make bridges'. Use 'make dashboard-local' only for intentional local development." >&2; \
exit 1; \
fi
## Refuse to start the production tunnel when a non-primary API already owns
## port 8000. With no listener, bridge is free to establish the tunnel.
primary-port-clear:
@if health="$$(curl -fsS --max-time 2 http://127.0.0.1:8000/state/health 2>/dev/null)"; then \
identity="$$(printf '%s' "$$health" | $(PYTHON) -c 'import json, sys; d=json.load(sys.stdin); print("{}/{}".format(d.get("instance_role", ""), d.get("instance_label", "")))')"; \
if [ "$$identity" != "primary/railiance01" ]; then \
echo "ERROR: port 8000 is occupied by '$$identity'." >&2; \
echo "Stop the local 'make api' process before starting the production tunnel." >&2; \
exit 1; \
fi; \
fi
dashboard: check-primary
@fuser -k 3000/tcp 2>/dev/null && echo "Stopped running dashboard" || true
$(MAKE) dashboard-install
cd dashboard && npm run dev
## Deliberate local-development dashboard; bypasses the production identity gate.
dashboard-local:
@echo "WARNING: serving the dashboard against the local/fallback API on :8000."
@fuser -k 3000/tcp 2>/dev/null && echo "Stopped running dashboard" || true
$(MAKE) dashboard-install
cd dashboard && npm run dev
## Fast work-record path. Repo Manager assigns only missing deterministic IDs,
## verifies the primary/railliance01 bridge, and asks central to derive the
## exact pushed Forgejo commit in one transactional request.
sync:
$(RMGR) sync --path "$(SYNC_PATH)" --api-base "$(STATE_HUB_API_BASE)" $(if $(filter 1 true yes,$(SYNC_PUSH)),--push,)
check: check-primary
@echo "State Hub primary/railiance01 is healthy."
check-local:
curl -sf http://127.0.0.1:8000/state/health | python3 -m json.tool
# CUST-WP-0067-T09. The chart ships a copy of the-custodian canon allowed-values
# because a container has no checkout to read it from. Regenerate after any
# canon vocabulary change; the -check target fails on drift so a release cannot
# validate classification against a stale vocabulary.
sync-classification-allowed:
python3 scripts/sync_classification_allowed.py
check-classification-allowed:
python3 scripts/sync_classification_allowed.py --check
# STATE-WP-0091: read-only surge-headroom check. Does not start Helm.
# Live: make railiance-state-hub-headroom
# Fixtures: make railiance-state-hub-headroom HEADROOM_FIXTURE=tests/fixtures/release_headroom/rev63-105m.json
HEADROOM_FIXTURE ?=
railiance-state-hub-headroom:
@if [ -n "$(HEADROOM_FIXTURE)" ]; then \
$(PYTHON) scripts/release_headroom_preflight.py --fixture "$(HEADROOM_FIXTURE)"; \
else \
$(PYTHON) scripts/release_headroom_preflight.py --kubectl "$(KUBECTL)"; \
fi
railiance-state-hub-render: check-classification-allowed
$(HELM) template $(RAILIANCE_STATE_HUB_RELEASE) $(RAILIANCE_STATE_HUB_CHART) \
--namespace $(RAILIANCE_STATE_HUB_NAMESPACE) \
-f $(RAILIANCE_STATE_HUB_VALUES) \
--set image.tag=$(RAILIANCE_STATE_HUB_IMAGE_TAG)
railiance-state-hub-client-dry-run:
@set -e; \
tmpdir="$$(mktemp -d)"; \
trap 'rm -rf "$$tmpdir"' EXIT; \
$(HELM) template $(RAILIANCE_STATE_HUB_RELEASE) $(RAILIANCE_STATE_HUB_CHART) \
--namespace $(RAILIANCE_STATE_HUB_NAMESPACE) \
-f $(RAILIANCE_STATE_HUB_VALUES) \
--set image.tag=$(RAILIANCE_STATE_HUB_IMAGE_TAG) > "$$tmpdir/state-hub.yaml"; \
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-credentials.sops.yaml.template; \
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-cluster.yaml; \
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-networkpolicies.yaml; \
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_APP_MANIFESTS)/state-hub-namespace.yaml; \
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_APP_MANIFESTS)/state-hub-env.secret.sops.yaml.template; \
$(KUBECTL) apply --dry-run=client -n $(RAILIANCE_STATE_HUB_NAMESPACE) -f "$$tmpdir/state-hub.yaml"
railiance-state-hub-server-dry-run:
@set -e; \
tmpdir="$$(mktemp -d)"; \
trap 'rm -rf "$$tmpdir"' EXIT; \
$(HELM) template $(RAILIANCE_STATE_HUB_RELEASE) $(RAILIANCE_STATE_HUB_CHART) \
--namespace $(RAILIANCE_STATE_HUB_NAMESPACE) \
-f $(RAILIANCE_STATE_HUB_VALUES) \
--set image.tag=$(RAILIANCE_STATE_HUB_IMAGE_TAG) > "$$tmpdir/state-hub.yaml"; \
$(KUBECTL) apply --server-side --dry-run=server -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-credentials.sops.yaml.template; \
$(KUBECTL) apply --dry-run=server -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-cluster.yaml; \
$(KUBECTL) apply --dry-run=server -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-networkpolicies.yaml; \
$(KUBECTL) apply --dry-run=server -f $(RAILIANCE_STATE_HUB_APP_MANIFESTS)/state-hub-namespace.yaml; \
if $(KUBECTL) get namespace $(RAILIANCE_STATE_HUB_NAMESPACE) >/dev/null 2>&1; then \
$(KUBECTL) apply --server-side --dry-run=server -f $(RAILIANCE_STATE_HUB_APP_MANIFESTS)/state-hub-env.secret.sops.yaml.template; \
$(KUBECTL) apply --dry-run=server -n $(RAILIANCE_STATE_HUB_NAMESPACE) -f "$$tmpdir/state-hub.yaml"; \
else \
echo "Namespace $(RAILIANCE_STATE_HUB_NAMESPACE) does not exist; validating namespaced app manifests with client dry-run."; \
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_APP_MANIFESTS)/state-hub-namespace.yaml; \
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_APP_MANIFESTS)/state-hub-env.secret.sops.yaml.template; \
$(KUBECTL) apply --dry-run=client -n $(RAILIANCE_STATE_HUB_NAMESPACE) -f "$$tmpdir/state-hub.yaml"; \
fi
test: test-python dashboard-check
test-python:
TEST_DATABASE_URL=postgresql+asyncpg://custodian:changeme@127.0.0.1:5432/custodian_test \
$(UV) run pytest -x -q
## Benchmark /state/summary revision cache (API must be running on :8000)
benchmark-summary-cache:
$(UV) run python scripts/benchmark_summary_cache.py
## ops-bridge managed tunnels
## Requires ops-bridge: bridge is at /home/worsch/.local/bin/bridge
tunnels-up: primary-port-clear
bridge up
tunnels-status:
bridge status
## End-to-end check: verifies SSH process alive + remote port listening on COULOMBCORE.
## Exits non-zero if any tunnel is not fully operational.
tunnels-check:
bridge check
## Ensure all ops-bridge tunnels are up and healthy.
## Brings up any stopped/stale tunnels, shows final status, exits non-zero if anything is still down.
bridges: primary-port-clear
@echo "==> Bringing up all tunnels..."
bridge up
@echo ""
@echo "==> Tunnel status:"
bridge status
@echo ""
@echo "==> Checking tunnel health..."
bridge check
## Start (or restart) the LOCAL FALLBACK backend — db + migrate + uvicorn.
## This replaces anything on :8000, including the production State Hub tunnel.
api: db
@echo "WARNING: starting the local fallback API; this is not the railiance01 primary."
@echo "Waiting for postgres..."; \
for i in 1 2 3 4 5 6 7 8 9 10; do \
nc -z 127.0.0.1 5432 2>/dev/null && break; \
sleep 1; \
done
$(MAKE) migrate
@fuser -k 8000/tcp 2>/dev/null && echo "Stopped running API" || true
$(UV) run uvicorn api.main:app --reload --reload-dir api --reload-dir mcp_server --reload-dir task_flow_engine --host 127.0.0.1 --port 8000
## Register a project (Claude Code): make register-project DOMAIN=railiance PROJECT_PATH=/home/worsch/railiance
register-project:
@test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN is required. Usage: make register-project DOMAIN=<domain> PROJECT_PATH=<path>"; exit 1)
@test -n "$(PROJECT_PATH)" || (echo "ERROR: PROJECT_PATH is required."; exit 1)
scripts/register_project.sh "$(DOMAIN)" "$(PROJECT_PATH)"
## Register a Codex project (AGENTS.md + HTTP API): make register-codex-project DOMAIN=capabilities PROJECT_PATH=/home/worsch/my-repo
register-codex-project:
@test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN is required. Usage: make register-codex-project DOMAIN=<domain> PROJECT_PATH=<path>"; exit 1)
@test -n "$(PROJECT_PATH)" || (echo "ERROR: PROJECT_PATH is required."; exit 1)
scripts/register_project.sh "$(DOMAIN)" "$(PROJECT_PATH)" --codex
## Register State Hub MCP for Claude Code. Optional: make register-mcp MCP_URL=http://127.0.0.1:18001/sse
register-mcp:
scripts/register-mcp.sh \
$(if $(MCP_URL),--url "$(MCP_URL)",) \
$(if $(API_BASE),--api-base "$(API_BASE)",) \
$(if $(DRY_RUN),--dry-run,)
## Configure Codex workspace-write networking for local State Hub access.
configure-codex:
scripts/configure-codex.sh \
$(if $(CODEX_HOME),--codex-home "$(CODEX_HOME)",) \
$(if $(API_BASE),--api-base "$(API_BASE)",) \
$(if $(WITH_MCP),--with-mcp,) \
$(if $(DRY_RUN),--dry-run,)
## Bootstrap a new operator/collaborator environment. Optional: make bootstrap-env ARGS="--install-missing"
bootstrap-env:
scripts/bootstrap-env.sh $(ARGS)
## Files-first local dev hub (CUST-WP-0054-T07): postgres + migrate + seed + API + repo registration.
## Optional: WITH_EDGE=1 WITH_MCP=1 make dev-hub
## Fleet tunnel when reachable: make dev-hub PROFILE=fleet
dev-hub:
bash scripts/dev_hub_up.sh $(if $(PROFILE),$(PROFILE),dev)
## Local edge relay for offline write buffering (STATE-WP-0068).
edge-relay:
@fuser -k 18080/tcp 2>/dev/null && echo "Stopped running edge relay" || true
STATEHUB_UPSTREAM_URL=$${STATEHUB_UPSTREAM_URL:-http://127.0.0.1:8000} \
$(UV) run uvicorn api.edge.relay:app --host 127.0.0.1 --port 18080
## Switch MCP/API profile: make mcp-profile PROFILE=dev|fleet|status
mcp-profile:
@test -n "$(PROFILE)" || (echo "ERROR: PROFILE is required (dev|fleet|status)."; exit 1)
bash scripts/mcp_hub_profile.sh "$(PROFILE)"
## Add a second repo to an existing domain: make add-repo DOMAIN=railiance REPO_PATH=/home/worsch/railiance-infra
add-repo:
@test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN is required."; exit 1)
@test -n "$(REPO_PATH)" || (echo "ERROR: REPO_PATH is required."; exit 1)
scripts/register_project.sh "$(DOMAIN)" "$(REPO_PATH)" --additional
## Create a new domain: make add-domain DOMAIN=my_domain NAME="My Domain"
add-domain:
@test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN is required (slug)."; exit 1)
@test -n "$(NAME)" || (echo "ERROR: NAME is required (display name)."; exit 1)
curl -sf -X POST http://127.0.0.1:8000/domains/ \
-H "Content-Type: application/json" \
-d "{\"slug\": \"$(DOMAIN)\", \"name\": \"$(NAME)\"}" | python3 -m json.tool
## Rename a domain: make rename-domain DOMAIN=old_slug NEW_SLUG=new_slug NEW_NAME="New Name"
rename-domain:
@test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN (old slug) is required."; exit 1)
@test -n "$(NEW_SLUG)" || (echo "ERROR: NEW_SLUG is required."; exit 1)
@test -n "$(NEW_NAME)" || (echo "ERROR: NEW_NAME is required."; exit 1)
curl -sf -X PATCH http://127.0.0.1:8000/domains/$(DOMAIN)/rename \
-H "Content-Type: application/json" \
-d "{\"new_slug\": \"$(NEW_SLUG)\", \"new_name\": \"$(NEW_NAME)\"}" | python3 -m json.tool
## Register this machine's local path for a repo: make register-path REPO=marki-docx PATH=/home/tegwick/marki-docx
register-path:
@test -n "$(REPO)" || (echo "ERROR: REPO is required. Usage: make register-path REPO=<slug> PATH=<path>"; exit 1)
@test -n "$(PATH)" || (echo "ERROR: PATH is required. Usage: make register-path REPO=<slug> PATH=<path>"; exit 1)
curl -sf -X POST "http://127.0.0.1:8000/repos/$(REPO)/paths" \
-H "Content-Type: application/json" \
-d "{\"host\": \"$$(hostname)\", \"path\": \"$(PATH)\"}" | python3 -m json.tool
## List repos for a domain: make list-repos DOMAIN=railiance
list-repos:
@test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN is required."; exit 1)
curl -sf "http://127.0.0.1:8000/repos/?domain=$(DOMAIN)" | python3 -m json.tool
## Ingest SBOM data for a repo (all mechanisms: lockfiles + ansible + sbom-tools.yaml).
## Auto-detect all sources: make ingest-sbom REPO=the-custodian REPO_PATH=/home/worsch/the-custodian
## Single lockfile (explicit): make ingest-sbom REPO=the-custodian LOCKFILE=/path/to/uv.lock
## Dry-run (no submit): make ingest-sbom REPO=the-custodian REPO_PATH=... DRY_RUN=1
## Tip: run capture-tools first for repos with system-level tool dependencies.
ingest-sbom:
@test -n "$(REPO)" || (echo "ERROR: REPO is required."; exit 1)
$(UV) run python scripts/ingest_sbom.py --repo "$(REPO)" \
$(if $(LOCKFILE),--lockfile "$(LOCKFILE)") \
$(if $(REPO_PATH),--repo-path "$(REPO_PATH)") \
$(if $(DRY_RUN),--dry-run)
## Ingest capability declarations from SCOPE.md into the catalog.
## Usage: make ingest-capabilities REPO=the-custodian [REPO_PATH=/home/worsch/the-custodian]
## Or: make ingest-capabilities-all
## Add DRY_RUN=1 to preview without writing.
ingest-capabilities:
@test -n "$(REPO)" || (echo "ERROR: REPO is required."; exit 1)
$(UV) run python scripts/ingest_capabilities.py --repo "$(REPO)" \
$(if $(REPO_PATH),--repo-path "$(REPO_PATH)") \
$(if $(DRY_RUN),--dry-run)
ingest-capabilities-all:
$(UV) run python scripts/ingest_capabilities.py --all \
$(if $(DRY_RUN),--dry-run)
## Check Repository Definition of Integrated (DoI) criteria for a repo.
## Usage: make check-doi REPO=llm-connect
## Or: make check-doi-all
## Add JSON=1 for machine-readable output.
check-doi:
@test -n "$(REPO)" || (echo "ERROR: REPO is required."; exit 1)
$(UV) run python scripts/check_doi.py --repo "$(REPO)" $(if $(JSON),--json)
check-doi-all:
$(UV) run python scripts/check_doi.py --all $(if $(JSON),--json)
## Ingest tpsc.yaml service declarations from a repo into the TPSC catalog.
## Usage: make ingest-tpsc REPO=llm-connect
## Or: make ingest-tpsc-all
## Add DRY_RUN=1 to preview without writing.
ingest-tpsc:
@test -n "$(REPO)" || (echo "ERROR: REPO is required."; exit 1)
$(UV) run python scripts/ingest_tpsc.py --repo "$(REPO)" \
$(if $(DRY_RUN),--dry-run)
ingest-tpsc-all:
$(UV) run python scripts/ingest_tpsc.py --all \
$(if $(DRY_RUN),--dry-run)
## Run SBOM capture agent for a repo — generates/updates sbom-tools.yaml.
## Usage: make capture-tools REPO=railiance-infra [REPO_PATH=/home/worsch/railiance-infra]
## Add DRY_RUN=1 to preview without writing.
capture-tools:
@test -n "$(REPO)" || (echo "ERROR: REPO is required."; exit 1)
$(UV) run python scripts/capture_sbom_tools.py --repo "$(REPO)" \
$(if $(REPO_PATH),--repo-path "$(REPO_PATH)") \
$(if $(DRY_RUN),--dry-run)
## Check a repo for ADR-001 compliance: make validate-adr REPO=/path/to/repo [DOMAIN=custodian]
validate-adr:
@test -n "$(REPO)" || (echo "ERROR: REPO is required. Usage: make validate-adr REPO=<path> [DOMAIN=<slug>]"; exit 1)
$(UV) run python scripts/validate_repo_adr.py "$(REPO)" $(if $(DOMAIN),--domain "$(DOMAIN)",)
## Consistency exit contract:
## - Direct scripts/consistency_check.py: 0 clean, 2 warnings-only, 1 failures.
## - Agent/operator Make wrappers below normalize warning-only 2 to shell success
## while preserving visible WARN output and keeping real failures non-zero.
## Check a single repo for ADR-001 consistency: make check-consistency REPO=the-custodian [REPO_PATH=/override]
## Exit 0 = clean or warnings-only (warnings stay visible), exit 1 = failures
check-consistency:
@test -n "$(REPO)" || (echo "ERROR: REPO is required. Usage: make check-consistency REPO=<slug>"; exit 1)
$(UV) run python scripts/consistency_check.py --repo "$(REPO)" \
$(if $(API_BASE),--api-base "$(API_BASE)",) \
$(if $(REPO_PATH),--repo-path "$(REPO_PATH)",); \
e=$$?; [ $$e -eq 2 ] && exit 0 || exit $$e
## Check and auto-fix a single repo: make fix-consistency REPO=the-custodian [REPO_PATH=/override]
## Exit 0 = clean or warnings-only (warnings stay visible), exit 1 = failures
fix-consistency:
@test -n "$(REPO)" || (echo "ERROR: REPO is required. Usage: make fix-consistency REPO=<slug>"; exit 1)
$(UV) run python scripts/consistency_check.py --repo "$(REPO)" --fix \
$(if $(API_BASE),--api-base "$(API_BASE)",) \
$(if $(REPO_PATH),--repo-path "$(REPO_PATH)",); \
e=$$?; [ $$e -eq 2 ] && exit 0 || exit $$e
## Normalize workplan frontmatter and task status literals in attached repos.
## Usage: make normalize-attached-workplans REPO=artifact-store
## make normalize-attached-workplans DIRTY=1
normalize-attached-workplans:
$(UV) run python scripts/normalize_attached_repo_workplans.py \
$(if $(REPO),--repo "$(REPO)",) \
$(if $(DIRTY),--dirty,) \
$(if $(DRY_RUN),--dry-run,)
@test -n "$(REPO)$(DIRTY)" || (echo "ERROR: set REPO=<slug> or DIRTY=1"; exit 1)
## Regenerate AGENTS.md / CLAUDE.md / .claude/rules from templates.
## Usage: make update-agent-instructions REPO=artifact-store
## make update-agent-instructions DIRTY=1
update-agent-instructions:
$(UV) run python scripts/update_agent_instruction_files.py \
$(if $(REPO),--repo "$(REPO)",) \
$(if $(DIRTY),--dirty,)
@test -n "$(REPO)$(DIRTY)" || (echo "ERROR: set REPO=<slug> or DIRTY=1"; exit 1)
## Reconcile measured token sources against State Hub.
## Usage: make token-reconcile [SINCE=2026-05-19] [APPLY=1] [ZERO_FALLBACKS=1]
token-reconcile:
$(PYTHON) scripts/token_reconcile.py \
$(if $(SINCE),--since "$(SINCE)",) \
$(if $(API_BASE),--api-base "$(API_BASE)",) \
$(if $(CODEX_HOME),--codex-home "$(CODEX_HOME)",) \
$(if $(CLAUDE_HOME),--claude-home "$(CLAUDE_HOME)",) \
$(if $(APPLY),--apply,) \
$(if $(ZERO_FALLBACKS),--zero-superseded-fallbacks,)
## Pull then fix: single repo or all repos if REPO omitted
## make fix-consistency-remote — smart pull+fix all repos that need it
## make fix-consistency-remote REPO=slug — pull+fix one repo
fix-consistency-remote:
$(UV) run python scripts/consistency_check.py \
$(if $(REPO),--repo "$(REPO)",--all) \
--remote \
$(if $(API_BASE),--api-base "$(API_BASE)",) \
$(if $(NO_WRITEBACK),--no-writeback,); \
e=$$?; [ $$e -eq 2 ] && exit 0 || exit $$e
## Infer repo slug from git remote URL and check: make check-consistency-here [REPO_PATH=/path/to/repo]
## Omit REPO_PATH to use the Python script's CWD (i.e. pass an empty --here flag).
check-consistency-here:
$(UV) run python scripts/consistency_check.py \
--here $(if $(REPO_PATH),"$(REPO_PATH)",) \
$(if $(API_BASE),--api-base "$(API_BASE)",); \
e=$$?; [ $$e -eq 2 ] && exit 0 || exit $$e
## Infer repo slug from git remote URL and fix: make fix-consistency-here [REPO_PATH=/path/to/repo]
fix-consistency-here:
$(UV) run python scripts/consistency_check.py \
--here $(if $(REPO_PATH),"$(REPO_PATH)",) \
--fix \
$(if $(API_BASE),--api-base "$(API_BASE)",); \
e=$$?; [ $$e -eq 2 ] && exit 0 || exit $$e
## Check all registered repos for ADR-001 consistency
check-consistency-all:
$(UV) run python scripts/consistency_check.py --all $(if $(API_BASE),--api-base "$(API_BASE)",); \
e=$$?; [ $$e -eq 2 ] && exit 0 || exit $$e
## Check and auto-fix all registered repos
fix-consistency-all:
$(UV) run python scripts/consistency_check.py --all --fix $(if $(API_BASE),--api-base "$(API_BASE)",); \
e=$$?; [ $$e -eq 2 ] && exit 0 || exit $$e
## Cancel open tasks belonging to completed/archived workstreams.
## Safe to run at any time; also suitable for a daily cron job.
## Cron example: 0 3 * * * cd ~/state-hub && make cleanup-stale
cleanup-stale:
$(UV) run python scripts/cleanup_stale_tasks.py
## Install custodian post-commit sync hook into one repo: make install-hooks REPO=marki-docx
install-hooks:
@test -n "$(REPO)" || (echo "ERROR: REPO is required. Usage: make install-hooks REPO=<slug>"; exit 1)
bash scripts/install_hooks.sh --repo "$(REPO)"
## Install custodian post-commit sync hook into all active registered repos
install-hooks-all:
bash scripts/install_hooks.sh --all
## Remove custodian post-commit sync hook from one repo: make remove-hooks REPO=marki-docx
remove-hooks:
@test -n "$(REPO)" || (echo "ERROR: REPO is required. Usage: make remove-hooks REPO=<slug>"; exit 1)
bash scripts/install_hooks.sh --repo "$(REPO)" --remove
## Compare Gitea coulomb org repos against state-hub registered repos
## Requires GITEA_TOKEN in env or .env: make gitea-inventory GITEA_TOKEN=<token>
gitea-inventory:
$(UV) run python scripts/gitea_inventory.py $(if $(JSON),--json)
## Register/update one repo from .repo-classification.yaml:
## make register-from-classification REPO=state-hub
## make register-from-classification PATH=/path/to/repo
## Optional: DRY_RUN=1
register-from-classification:
@test -n "$(REPO)" -o -n "$(PATH)" || (echo "ERROR: REPO or PATH is required."; exit 1)
$(UV) run python scripts/register_from_classification.py \
$(if $(PATH),--repo-path "$(PATH)",--slug "$(REPO)") \
$(if $(DRY_RUN),--dry-run,)
## Bulk register/update all active repos with accessible local paths
register-from-classification-all:
$(UV) run python scripts/register_from_classification.py --bulk \
$(if $(DRY_RUN),--dry-run,)
clean:
$(COMPOSE) down -v

View file

@ -0,0 +1,591 @@
import asyncio
import logging
import uuid
import socket
import time
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
import yaml
from fastapi import APIRouter, Depends, HTTPException, Query, Request, Response, status
from sqlalchemy import or_, select
from sqlalchemy.ext.asyncio import AsyncSession
from api.database import get_session
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,
WorkplanRead,
WorkplanUpdate,
)
from api.services.lifecycle import transition_workplan_status
from api.services.legacy_compat import retire_legacy_route
from api.services.wait_kind import annotate_workplans
from api.work_record_flavor import (
RESIDUAL_FLAVOR,
WORK_RECORD_FLAVORS,
normalize_flavor,
)
from api.workplan_status import (
is_supported_workplan_status,
normalize_workplan_status,
ready_review_status,
)
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/workstreams", tags=["workstreams"])
workplan_router = APIRouter(prefix="/workplans", tags=["workplans"])
_INDEX_CACHE: dict[str, Any] | None = None
_INDEX_CACHE_AT: float = 0.0
_INDEX_TTL = 30.0
_INDEX_REFRESH_TASK: asyncio.Task | None = None
_INDEX_LAST_ERROR: str | None = None
_COMPLETED_WORKPLAN_EVENT = "org.statehub.workplan.completed"
def _repo_path(repo: ManagedRepo) -> Path | None:
hostname = socket.gethostname()
candidates = []
host_paths = repo.host_paths or {}
if host_paths.get(hostname):
candidates.append(host_paths[hostname])
if repo.local_path:
candidates.append(repo.local_path)
for raw in candidates:
path = Path(raw).expanduser()
if path.is_dir():
return path
return None
def _frontmatter(path: Path) -> dict[str, Any]:
try:
text = path.read_text(encoding="utf-8")
except OSError:
return {}
if not text.startswith("---\n"):
return {}
end = text.find("\n---", 4)
if end == -1:
return {}
try:
return yaml.safe_load(text[4:end].strip()) or {}
except yaml.YAMLError:
return {}
def _legacy_key(method: str, route: str) -> str:
return f"rest_api:{method} {route}"
async def _list_workplans(
*,
topic_id: uuid.UUID | None,
repo_id: uuid.UUID | None,
repo_goal_id: uuid.UUID | None,
status_filter: str | None,
owner: str | None,
slug: str | None,
flavor: str | None = None,
include_residuals: bool = True,
session: AsyncSession,
) -> list[Workplan]:
q = select(Workplan)
if topic_id:
q = q.where(Workplan.topic_id == topic_id)
if repo_id:
q = q.where(Workplan.repo_id == repo_id)
if repo_goal_id:
q = q.where(Workplan.repo_goal_id == repo_goal_id)
if status_filter:
normalised_status = normalize_workplan_status(status_filter)
if not is_supported_workplan_status(status_filter):
raise HTTPException(status_code=422, detail=f"Unsupported workplan status '{status_filter}'")
q = q.where(Workplan.status == normalised_status)
if owner:
q = q.where(Workplan.owner == owner)
if slug:
q = q.where(Workplan.slug == slug)
wanted = normalize_flavor(flavor)
if wanted is not None:
if wanted not in WORK_RECORD_FLAVORS:
raise HTTPException(
status_code=422,
detail=(
f"Unknown work-record flavor {wanted!r}; "
f"expected one of {', '.join(WORK_RECORD_FLAVORS)}"
),
)
q = q.where(Workplan.flavor == wanted)
elif not include_residuals:
q = q.where(or_(Workplan.flavor.is_(None), Workplan.flavor != RESIDUAL_FLAVOR))
q = q.order_by(
Workplan.planning_priority.asc().nullslast(),
Workplan.planning_order.asc().nullslast(),
Workplan.updated_at.desc(),
)
result = await session.execute(q)
return list(result.scalars().all())
async def _build_workplan_index(session: AsyncSession) -> dict[str, Any]:
result = await session.execute(
select(ManagedRepo).where(ManagedRepo.status == "active").order_by(ManagedRepo.slug)
)
index: dict[str, Any] = {}
for repo in result.scalars().all():
root = _repo_path(repo)
if root is None:
continue
for directory, archived in (
(root / "workplans", False),
(root / "workplans" / "archived", True),
):
if not directory.is_dir():
continue
for path in sorted(directory.glob("*.md")):
data = _frontmatter(path)
workplan_id = data.get("state_hub_workstream_id") or data.get("state_hub_workplan_id")
if not workplan_id:
continue
file_status = normalize_workplan_status(data.get("status", ""))
review = (
ready_review_status(
root,
data.get("reviewed_against_commit"),
data.get("context_paths"),
)
if file_status == "ready"
else None
)
index[str(workplan_id)] = {
"filename": path.name,
"relative_path": str(path.relative_to(root)),
"repo_slug": repo.slug,
"archived": archived,
"status": file_status or None,
"needs_review": bool(review and review.needs_review),
"health_labels": ["needs_review"] if review and review.needs_review else [],
}
await _merge_db_backing_index(session, index)
return {"workplans": index, "workstreams": index}
async def _merge_db_backing_index(session: AsyncSession, index: dict[str, Any]) -> None:
"""Fill index gaps from DB-backed file bindings synced by fix-consistency."""
result = await session.execute(
select(Workplan, ManagedRepo.slug)
.join(ManagedRepo, Workplan.repo_id == ManagedRepo.id)
.where(Workplan.backing_filename.isnot(None))
)
for wp, repo_slug in result.all():
key = str(wp.id)
if key in index:
continue
index[key] = {
"filename": wp.backing_filename,
"relative_path": wp.backing_relative_path,
"repo_slug": repo_slug,
"archived": bool(wp.backing_archived),
"status": normalize_workplan_status(wp.status) if wp.status else None,
"needs_review": False,
"health_labels": [],
}
def _invalidate_workplan_index_cache() -> None:
global _INDEX_CACHE, _INDEX_CACHE_AT
_INDEX_CACHE = None
_INDEX_CACHE_AT = 0.0
def _index_with_meta(*, stale: bool, refresh_in_progress: bool) -> dict[str, Any]:
age = time.monotonic() - _INDEX_CACHE_AT if _INDEX_CACHE_AT else None
return {
**(_INDEX_CACHE or {"workplans": {}, "workstreams": {}}),
"_meta": {
"generated_at": _INDEX_CACHE.get("_meta", {}).get("generated_at") if _INDEX_CACHE else None,
"stale": stale,
"cache_age_seconds": round(age, 3) if age is not None else None,
"refresh_in_progress": refresh_in_progress,
"last_error": _INDEX_LAST_ERROR,
},
}
async def _refresh_workplan_index_background() -> None:
global _INDEX_CACHE, _INDEX_CACHE_AT, _INDEX_LAST_ERROR
from api.database import async_session_factory
try:
async with async_session_factory() as session:
index = await _build_workplan_index(session)
index["_meta"] = {
"generated_at": datetime.now(timezone.utc).isoformat(),
"stale": False,
"cache_age_seconds": 0.0,
"refresh_in_progress": False,
"last_error": None,
}
_INDEX_CACHE = index
_INDEX_CACHE_AT = time.monotonic()
_INDEX_LAST_ERROR = None
except Exception as exc:
_INDEX_LAST_ERROR = str(exc)
def _ensure_index_refresh_started() -> None:
global _INDEX_REFRESH_TASK
if _INDEX_REFRESH_TASK is not None and not _INDEX_REFRESH_TASK.done():
return
_INDEX_REFRESH_TASK = asyncio.create_task(_refresh_workplan_index_background())
async def _workplan_index(
*,
refresh: bool,
session: AsyncSession,
) -> dict[str, Any]:
"""Map file-backed workplan ids to their local workplan filenames."""
global _INDEX_CACHE, _INDEX_CACHE_AT, _INDEX_LAST_ERROR
cache_age = time.monotonic() - _INDEX_CACHE_AT if _INDEX_CACHE_AT else None
if not refresh and _INDEX_CACHE is not None and cache_age is not None and cache_age < _INDEX_TTL:
refresh_running = _INDEX_REFRESH_TASK is not None and not _INDEX_REFRESH_TASK.done()
return _index_with_meta(stale=False, refresh_in_progress=refresh_running)
if not refresh and _INDEX_CACHE is not None:
_ensure_index_refresh_started()
return _index_with_meta(stale=True, refresh_in_progress=True)
index = await _build_workplan_index(session)
index["_meta"] = {
"generated_at": datetime.now(timezone.utc).isoformat(),
"stale": False,
"cache_age_seconds": 0.0,
"refresh_in_progress": False,
"last_error": None,
}
_INDEX_CACHE = index
_INDEX_CACHE_AT = time.monotonic()
_INDEX_LAST_ERROR = None
return _INDEX_CACHE
async def _create_workplan(
*,
body: WorkplanCreate,
session: AsyncSession,
) -> Workplan:
wp = Workplan(**body.model_dump())
session.add(wp)
await session.commit()
await session.refresh(wp)
return wp
async def _get_workplan(
*,
workplan_id: uuid.UUID,
session: AsyncSession,
) -> Workplan:
wp = await session.get(Workplan, workplan_id)
if wp is None:
raise HTTPException(status_code=404, detail="Workplan not found")
return wp
async def _update_workplan(
*,
workplan_id: uuid.UUID,
body: WorkplanUpdate,
session: AsyncSession,
) -> Workplan:
wp = await session.get(Workplan, workplan_id)
if wp is None:
raise HTTPException(status_code=404, detail="Workplan not found")
update_data = body.model_dump(exclude_unset=True)
status_update = update_data.pop("status", None)
prev_status = wp.status
for field, value in update_data.items():
setattr(wp, field, value)
if status_update is not None:
transition_workplan_status(wp, status_update)
await session.commit()
await session.refresh(wp)
if normalize_workplan_status(prev_status) != "finished" and wp.status == "finished":
await _publish_completion_events(wp, session)
return wp
async def _archive_workplan(
*,
workplan_id: uuid.UUID,
session: AsyncSession,
) -> Workplan:
wp = await session.get(Workplan, workplan_id)
if wp is None:
raise HTTPException(status_code=404, detail="Workplan not found")
transition_workplan_status(wp, "archived")
await session.commit()
await session.refresh(wp)
return wp
async def _publish_completion_events(wp: Workplan, session: AsyncSession) -> None:
workplan_envelope = EventEnvelope.new(
_COMPLETED_WORKPLAN_EVENT,
attributes={
"workplan_id": str(wp.id),
"slug": wp.slug,
"title": wp.title,
"topic_id": str(wp.topic_id) if wp.topic_id else None,
"repo_id": str(wp.repo_id) if wp.repo_id else None,
"repo_goal_id": str(wp.repo_goal_id) if wp.repo_goal_id else None,
},
)
asyncio.create_task(publish_event(_COMPLETED_WORKPLAN_EVENT, workplan_envelope))
@router.get("/", status_code=status.HTTP_410_GONE)
async def list_workstreams(
request: Request,
response: Response,
topic_id: uuid.UUID | None = None,
repo_id: uuid.UUID | None = None,
repo_goal_id: uuid.UUID | None = None,
status: str | None = None,
owner: str | None = None,
slug: str | None = None,
session: AsyncSession = Depends(get_session),
) -> None:
await retire_legacy_route(
session=session,
request=request,
response=response,
interface_key=_legacy_key("GET", "/workstreams/"),
replacement_ref="/workplans/",
detail="Legacy GET /workstreams/ retired; use GET /workplans/",
)
@workplan_router.get("/", response_model=list[WorkplanRead])
async def list_workplans(
topic_id: uuid.UUID | None = None,
repo_id: uuid.UUID | None = None,
repo_goal_id: uuid.UUID | None = None,
status: str | None = None,
owner: str | None = None,
slug: str | None = None,
flavor: str | None = Query(None),
include_residuals: bool = Query(True),
session: AsyncSession = Depends(get_session),
) -> list[Workplan]:
workplans = await _list_workplans(
topic_id=topic_id,
repo_id=repo_id,
repo_goal_id=repo_goal_id,
status_filter=status,
owner=owner,
slug=slug,
flavor=flavor,
include_residuals=include_residuals,
session=session,
)
return list(await annotate_workplans(session, workplans))
@router.get("/workplan-index", status_code=status.HTTP_410_GONE)
async def workplan_index(
request: Request,
response: Response,
refresh: bool = Query(False, description="Force cache invalidation"),
session: AsyncSession = Depends(get_session),
) -> None:
await retire_legacy_route(
session=session,
request=request,
response=response,
interface_key=_legacy_key("GET", "/workstreams/workplan-index"),
replacement_ref="/workplans/index",
detail="Legacy GET /workstreams/workplan-index retired; use GET /workplans/index",
)
@workplan_router.get("/index")
async def workplan_index_preferred(
refresh: bool = Query(False, description="Force cache invalidation"),
session: AsyncSession = Depends(get_session),
) -> dict[str, Any]:
return await _workplan_index(refresh=refresh, session=session)
@workplan_router.put("/index/bindings")
async def sync_workplan_bindings(
body: WorkplanBindingsSync,
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
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
wp.backing_filename = entry.filename
wp.backing_relative_path = entry.relative_path
wp.backing_archived = entry.archived
wp.backing_synced_at = synced_at
updated += 1
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)
async def create_workstream(
request: Request,
response: Response,
body: WorkplanCreate,
session: AsyncSession = Depends(get_session),
) -> None:
await retire_legacy_route(
session=session,
request=request,
response=response,
interface_key=_legacy_key("POST", "/workstreams/"),
replacement_ref="/workplans/",
detail="Legacy POST /workstreams/ retired; use POST /workplans/",
)
@workplan_router.post("/", response_model=WorkplanRead, status_code=status.HTTP_201_CREATED)
async def create_workplan(
body: WorkplanCreate,
session: AsyncSession = Depends(get_session),
) -> Workplan:
return await _create_workplan(body=body, session=session)
@router.get("/{workstream_id}", status_code=status.HTTP_410_GONE)
async def get_workstream(
request: Request,
response: Response,
workstream_id: uuid.UUID,
session: AsyncSession = Depends(get_session),
) -> None:
await retire_legacy_route(
session=session,
request=request,
response=response,
interface_key=_legacy_key("GET", "/workstreams/{workstream_id}"),
replacement_ref="/workplans/{workplan_id}",
detail="Legacy GET /workstreams/{workstream_id} retired; use GET /workplans/{workplan_id}",
)
@workplan_router.get("/{workplan_id}", response_model=WorkplanRead)
async def get_workplan(
workplan_id: uuid.UUID,
session: AsyncSession = Depends(get_session),
) -> Workplan:
wp = await _get_workplan(workplan_id=workplan_id, session=session)
await annotate_workplans(session, [wp])
return wp
@router.patch("/{workstream_id}", status_code=status.HTTP_410_GONE)
async def update_workstream(
request: Request,
response: Response,
workstream_id: uuid.UUID,
body: WorkplanUpdate,
session: AsyncSession = Depends(get_session),
) -> None:
await retire_legacy_route(
session=session,
request=request,
response=response,
interface_key=_legacy_key("PATCH", "/workstreams/{workstream_id}"),
replacement_ref="/workplans/{workplan_id}",
detail="Legacy PATCH /workstreams/{workstream_id} retired; use PATCH /workplans/{workplan_id}",
)
@workplan_router.patch("/{workplan_id}", response_model=WorkplanRead)
async def update_workplan(
workplan_id: uuid.UUID,
body: WorkplanUpdate,
session: AsyncSession = Depends(get_session),
) -> Workplan:
return await _update_workplan(workplan_id=workplan_id, body=body, session=session)
@router.delete("/{workstream_id}", status_code=status.HTTP_410_GONE)
async def archive_workstream(
request: Request,
response: Response,
workstream_id: uuid.UUID,
session: AsyncSession = Depends(get_session),
) -> None:
await retire_legacy_route(
session=session,
request=request,
response=response,
interface_key=_legacy_key("DELETE", "/workstreams/{workstream_id}"),
replacement_ref="DELETE /workplans/{workplan_id}",
detail="Legacy DELETE /workstreams/{workstream_id} retired; use DELETE /workplans/{workplan_id}",
)
@workplan_router.delete("/{workplan_id}", response_model=WorkplanRead)
async def archive_workplan(
workplan_id: uuid.UUID,
session: AsyncSession = Depends(get_session),
) -> Workplan:
return await _archive_workplan(workplan_id=workplan_id, session=session)

View file

@ -0,0 +1,238 @@
"""Exact closed source bindings predating canonical Forge identity.
These are frozen historical source views, not aliases or UUID allocation rules.
An altered/missing source requires explicit owner review; generic source UUIDs
remain untrusted. Shared Activity views represent the one archived parent.
"""
from pathlib import Path
from hashlib import sha256
import json
BINDINGS = json.loads(r'''[
{
"repo": "activity-core",
"path": "workplans/archived/260707-custodian-WP-0003-event-bridge.md",
"sha256": "83ea8bcb151e003419b73c59dbd028f4a1ce3d1120a82e4a128a772503ff3b76",
"record_id": "custodian-WP-0003",
"uuid": "b4eb45a9-69e3-4ab0-b00c-67a53c3117c5",
"parent_record_id": "custodian-WP-0003"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0001-temporal-backbone.md",
"sha256": "020e2eac973f2daa41c1687f8e867da9d521f945547e6aa82c87993b3ff6fc64",
"record_id": "custodian-WP-0001",
"uuid": "e22516cc-4fdf-4dad-a928-a9e04a9fa7c7",
"parent_record_id": "custodian-WP-0001"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0002-triggers-ops.md",
"sha256": "d8cbc61631c01b6f11a03a63f2fb6275fc853ff7f7459ce65973ed1a233daa01",
"record_id": "custodian-WP-0002",
"uuid": "3a4f47d9-8bc1-434e-acb4-bed5d4dacda0",
"parent_record_id": "custodian-WP-0002"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0003a-event-bridge-model-rules-registry.md",
"sha256": "ffa6682e9ba3aaf559bd44203d561c90dea69bcf1b1e778209a7b209e6e5cdd3",
"record_id": "custodian-WP-0003a",
"uuid": "b4eb45a9-69e3-4ab0-b00c-67a53c3117c5",
"parent_record_id": "custodian-WP-0003"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0003b-event-bridge-parser-workflow-triggers.md",
"sha256": "4fc34e0f821e2df51e5c08c7ab34da35b65690381d01f0cfdf55d00c970b414c",
"record_id": "custodian-WP-0003b",
"uuid": "b4eb45a9-69e3-4ab0-b00c-67a53c3117c5",
"parent_record_id": "custodian-WP-0003"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0003c-event-bridge-context-integration.md",
"sha256": "b6cd62b26515e509670ca39736b6bcec9633beedd46c9bd8f7598c06bac7276b",
"record_id": "custodian-WP-0003c",
"uuid": "b4eb45a9-69e3-4ab0-b00c-67a53c3117c5",
"parent_record_id": "custodian-WP-0003"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0004-railiance-ops.md",
"sha256": "20df02eea740e109c937130e7f8801a636fc0990ed8c6d2ced55659f8957adb1",
"record_id": "custodian-WP-0004",
"uuid": "759b1255-aa78-42b5-8fab-c5fcf168f5f4",
"parent_record_id": "custodian-WP-0004"
},
{
"repo": "the-custodian",
"path": "workplans/ADHOC-2026-08-25-retired.md",
"sha256": "344a1293dbb3a67a944eb34ca4a0337487d8f047226027a2107922346fbab2dd",
"record_id": "CUST-WP-ADHOC-2026-08-25-RETIRED",
"uuid": "1c53d3db-6633-5e2d-987f-10706d73be9e",
"parent_record_id": "CUST-WP-ADHOC-2026-08-25-RETIRED"
},
{
"repo": "railiance-cluster",
"path": "workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md",
"sha256": "8d6f8533c119117686bc0d5bb9ed5349726aa6c3189ff98b53409c68ac685ca9",
"record_id": "RAIL-BS-WP-0002",
"uuid": "4c63dfc6-9eac-4e79-9f77-8f644ad7147d",
"parent_record_id": "RAIL-BS-WP-0002",
"tasks": [
{
"label": "T01",
"uuid": "3f042630-eab0-4c6a-9167-e2b28ff20e40",
"status": "done"
},
{
"label": "T02",
"uuid": "e8510646-46ed-4697-a345-f3d3009eea78",
"status": "done"
},
{
"label": "T03",
"uuid": "dab2c07f-8aa0-4635-8df6-857e87e93fc5",
"status": "done"
},
{
"label": "T04",
"uuid": "5c3d40e4-239b-488e-9519-6f7a38d2325f",
"status": "done"
},
{
"label": "T05",
"uuid": "7f9e0e58-a130-467a-a2d0-b3f2564e496f",
"status": "done"
}
]
},
{
"repo": "railiance-cluster",
"path": "workplans/archived/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md",
"sha256": "4dc2f61e15adc3c5c81f9ecf8b933512275b95e8f829fac52ae7cde1385cea4a",
"record_id": "RAIL-BS-WP-0003",
"uuid": "7ee9ee22-1fae-4567-9194-8d70a9e0f45b",
"parent_record_id": "RAIL-BS-WP-0003",
"tasks": [
{
"label": "T01",
"uuid": "6841c93a-f146-47eb-9f7c-8fa0e02c1bbc",
"status": "done"
},
{
"label": "T02",
"uuid": "ab166073-30a7-4702-a037-4091e8706e20",
"status": "done"
},
{
"label": "T03",
"uuid": "140da396-8e30-4f4d-b88c-c42c0cd46c01",
"status": "done"
},
{
"label": "T04",
"uuid": "d8a3ba40-fda0-4c1f-a9f1-ffcd621a5b3d",
"status": "done"
}
]
},
{
"repo": "railiance-cluster",
"path": "workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md",
"sha256": "04f787078232395bd2d43861de16c521848559fa286f06e1caa9cb779aa71ac3",
"record_id": "RAIL-BS-WP-0004",
"uuid": "7e8b0c20-51eb-40c9-9e3b-85dd380d7625",
"parent_record_id": "RAIL-BS-WP-0004",
"tasks": [
{
"label": "T01",
"uuid": "4526a842-ea31-4874-9231-92ab556cfe7b",
"status": "done"
},
{
"label": "T02",
"uuid": "a6313e06-1976-46a7-8e31-df4eb2eca880",
"status": "done"
},
{
"label": "T03",
"uuid": "05d42a55-921f-4aa7-bb76-e8af9c7e0ac3",
"status": "done"
},
{
"label": "T04",
"uuid": "08233868-d522-4117-bc4e-6c0f52545665",
"status": "done"
},
{
"label": "T05",
"uuid": "2d5acff7-4a4e-4ddd-ad06-08237ad3dac8",
"status": "done"
},
{
"label": "T06",
"uuid": "f8e4a094-c367-40eb-b895-da17bc144b07",
"status": "done"
}
]
}
]''')
def bound_sources(repo_root: Path, repo_slug: str):
entries = [entry for entry in BINDINGS if entry["repo"] == repo_slug]
result = []
for entry in entries:
path = repo_root / entry["path"]
if path.is_symlink() or not path.is_file():
raise ValueError("Historical source binding missing or symlinked: " + entry["path"])
data = path.read_bytes()
if sha256(data).hexdigest() != entry["sha256"]:
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")

View file

@ -0,0 +1,6 @@
apiVersion: v2
name: state-hub
description: State Hub API service for private Railiance operation
type: application
version: 0.1.0
appVersion: "main-51bc70d"

View file

@ -0,0 +1,127 @@
# GENERATED — do not edit.
# Synced from the-custodian/canon/standards/repo-classification.allowed.yaml
# by scripts/sync_classification_allowed.py (CUST-WP-0067-T09).
# Canon is authoritative; this copy exists only so the container has one.
# Machine-readable allowed-values for the Repo Classification Standard.
#
# Single source of truth for the standard's controlled vocabularies, derived
# from canon/standards/repo-classification-standard_v1.0.md. Consumed by:
# - the per-repo .repo-classification.yaml linter (tools/validate_repo_classification.py)
# - the State Hub registration validator (CUST-WP-0050 T04)
#
# When the standard's vocabularies change, update this file and bump `version`
# to match the standard version. CUST-WP-0050 T01.
standard: "Repo Classification Standard"
version: "1.1"
canon_id: "canon-repo-classification"
# category — exactly 1 required (§5)
categories:
- experimental
- research
- project
- tooling
- product
- business
- publication
# domain / secondary_domains — primary exactly 1; secondaries 0..n (§6)
domains:
- infotech
- financials
- communication
- consumer
- health
- industrials
- energy
- utilities
- materials
- realestate
- crypto
- agents
- space
- government
# business_stake — 0..n; 2..6 recommended (§8)
business_stake:
- execution
- intelligence
- finance
- legal
- sales
- experience
- technology
- operations
- product
- people
- procurement
- sustainability
- automation
# business_mechanics — 0..n, optional (§9)
business_mechanics:
- intention
- control
- coordination
- operation
- adaptation
# capability_tags are intentionally OPEN-ENDED (§7): lowercase kebab-case, not
# restricted to this set. The families below are the standard's recommended
# canonical tags — used to warn on likely synonyms/typos, never to reject.
capability_families:
identity_and_access:
- identity
- authentication
- authorization
- access-control
- user-management
- tenancy
knowledge_and_evidence:
- knowledge
- citations
- evidence
- source-management
- traceability
- documentation
- decision-support
- history
- participation
platform_and_operations:
- platform
- deployment
- operations
- observability
- feature-control
- configuration
- orchestration
market_and_coordination:
- marketplace
- pricing
- monetization
- reputation
- challenges
- bounties
- collaboration
- coordination
product_and_lifecycle:
- product-development
- lifecycle
governance_and_control:
- governance
- policy
- compliance
- risk
- audit
- control
games_and_entertainment:
- game
- entertainment
# Validation guidance (advisory bounds the linter applies as warnings)
guidance:
secondary_domains_max: 3
business_stake_recommended_min: 2
business_stake_recommended_max: 6
capability_tag_pattern: "^[a-z0-9]+(-[a-z0-9]+)*$"

View file

@ -0,0 +1,69 @@
# Production values for the State Hub Railiance chart handoff.
# Non-secret values only. DATABASE_URL comes from the Secret `state-hub-env`.
namespace:
create: false
image:
repository: forgejo.coulomb.social/coulomb/state-hub
tag: "main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015"
ingress:
enabled: false
# MCP layer on central (CUST-WP-0067-T08). Enabled together with the image tag
# above: MCP_HOST landed in main-6c1262e, and enabling this on an older image
# would deploy a pod that binds loopback and never becomes reachable.
# ClusterIP only — no ingress, same reason the API's is disabled.
mcp:
enabled: true
resources:
requests:
# 2026-09-10: 1,078 observed 5-minute samples, p99 2.35m, peak 30.45m.
# Preserve the 500m limit for bursts; free 40m for the API rollout surge.
cpu: 10m
config:
# This deployment is the authoritative hub. Callers verify this rather than
# trusting that whatever answered on a port is central (CUST-WP-0067-T03).
instanceRole: primary
instanceLabel: railiance01
sbomNexusUrl: "http://sbom-nexus.sbom-nexus.svc.cluster.local:8010"
# Reversible T04 read cutover; set back to `legacy` to roll back.
sbomNexusReadMode: nexus
# Reversible T04 write cutover; set back to `legacy` to roll back future writes.
sbomNexusWriteMode: nexus
resources:
# Restored to the chart's 250m on 2026-09-21. The earlier 100m was a workaround
# for a node with <250m unallocated; ~830m of Knative/Kourier requests were
# released that day (the-custodian docs/kubernetes-change-gate-decision.md).
# State Hub idles near 5m but bursts past 400m under concurrent agent load.
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 1000m
memory: 2Gi
sweep:
# RMGR-WP-0005-T11: disabled while railiance01 checkouts still target the
# stale gitea-remote lineage. Re-enable only after the governed remote
# reconciliation and registrar preflight are complete.
enabled: false
# Present in the live release but previously missing here, so deploying from
# this file alone silently dropped it. Inert while enabled is false.
hostname: 239.62.205.92.host.secureserver.net
hostPath: /home/tegwick
sshHostPath: /home/tegwick/.ssh
# Forge read credential (STATE-WP-0084-T02). Coordinates only — the token lives
# in OpenBao at platform/workloads/state-hub/forge-derivation and reaches the
# pod through Kubernetes auth, never through this file or a Secret.
forgeRead:
enabled: true
# RPF-WP-0035-T04: dedicated ESO signing key, API-only explicit reference.
renamePreflight:
enabled: true
secretName: state-hub-rename-preflight

View file

@ -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"
]
}
}

View file

@ -0,0 +1,91 @@
#!/usr/bin/env python3
"""Sync the canon classification allowed-values into the chart (CUST-WP-0067-T09).
The State Hub validates repository classification against
``the-custodian/canon/standards/repo-classification.allowed.yaml``. That file is
canon and must stay authoritative, but a container has no the-custodian
checkout, so a copy has to travel with the release.
A copy that nobody checks is a copy that silently drifts. This script owns the
copy: ``--check`` fails when it diverges from canon, so the release can refuse
to ship a stale vocabulary rather than validate against yesterday's rules.
Usage:
python scripts/sync_classification_allowed.py # write the copy
python scripts/sync_classification_allowed.py --check # verify, exit 1 on drift
"""
from __future__ import annotations
import argparse
import difflib
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
CHART_COPY = (
REPO_ROOT
/ "deploy/railiance/apps/charts/state-hub/files/repo-classification.allowed.yaml"
)
CANON_CANDIDATES = (
Path("/home/worsch/the-custodian/canon/standards/repo-classification.allowed.yaml"),
Path("/home/tegwick/the-custodian/canon/standards/repo-classification.allowed.yaml"),
REPO_ROOT.parent / "the-custodian/canon/standards/repo-classification.allowed.yaml",
)
HEADER = (
"# GENERATED — do not edit.\n"
"# Synced from the-custodian/canon/standards/repo-classification.allowed.yaml\n"
"# by scripts/sync_classification_allowed.py (CUST-WP-0067-T09).\n"
"# Canon is authoritative; this copy exists only so the container has one.\n"
)
def find_canon() -> Path:
for candidate in CANON_CANDIDATES:
if candidate.is_file():
return candidate
raise SystemExit(
"ERROR: canon allowed-values not found. Looked in:\n "
+ "\n ".join(str(c) for c in CANON_CANDIDATES)
)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--check",
action="store_true",
help="Verify the chart copy matches canon; exit 1 on drift",
)
args = parser.parse_args()
canon = find_canon()
desired = HEADER + canon.read_text(encoding="utf-8")
if args.check:
if not CHART_COPY.is_file():
print(f"DRIFT: {CHART_COPY} is missing; run without --check to create it")
return 1
current = CHART_COPY.read_text(encoding="utf-8")
if current != desired:
print(f"DRIFT: chart copy differs from canon ({canon})")
sys.stdout.writelines(
difflib.unified_diff(
current.splitlines(keepends=True),
desired.splitlines(keepends=True),
fromfile="chart copy",
tofile="canon",
)
)
return 1
print(f"OK: chart copy matches canon ({canon})")
return 0
CHART_COPY.parent.mkdir(parents=True, exist_ok=True)
CHART_COPY.write_text(desired, encoding="utf-8")
print(f"Synced {canon} -> {CHART_COPY}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,180 @@
from pathlib import Path
import ast, asyncio, importlib.util, types, uuid
from dataclasses import dataclass, field
from datetime import datetime, timezone
import pytest
ROOT = Path(__file__).resolve().parents[1]
spec=importlib.util.spec_from_file_location('historical',ROOT/'api/services/historical_projection.py')
h=importlib.util.module_from_spec(spec);spec.loader.exec_module(h)
B=h.FROZEN_CLUSTER_BINDINGS[0]
def binding(_bound=B,**changes):
B=_bound
d=dict(workplan_id=uuid.UUID(B['uuid']),repo_slug=B['repo'],filename=Path(B['path']).name,relative_path=B['path'],archived=True,status='finished');d.update(changes);return d
class Obj:
def __init__(self,**kw):self.__dict__.update(kw);self.writes=[]
def __setattr__(self,k,v):
if 'writes' in self.__dict__:self.writes.append(k)
object.__setattr__(self,k,v)
class Entry(Obj):
def model_dump(self):return {k:v for k,v in self.__dict__.items() if k!='writes'}
class Col:
def in_(self,x):return self
def __eq__(self,x):return self
class Query:
def __init__(self):self.locked=False;self.refreshed=False;self.ordered=False
def where(self,*a):return self
def order_by(self,*a):self.ordered=True;return self
def with_for_update(self):self.locked=True;return self
def execution_options(self,**kw):self.refreshed=kw.get("populate_existing") is True;return self
class Model: id=Col();slug=Col();workplan_id=Col()
class Result:
def __init__(self,values):self.values=values
def scalars(self):return self
def all(self):return self.values
def scalar_one_or_none(self):return self.values[0] if self.values else None
class Session:
def __init__(self,rows):self.results=iter(rows);self.commits=0;self.queries=[]
async def execute(self,q):self.queries.append(q);return Result(next(self.results))
async def commit(self):self.commits+=1
class HTTPException(Exception):
def __init__(self,status_code,detail):self.status_code=status_code;super().__init__(detail)
def extracted(path,name,extra):
tree=ast.parse((ROOT/path).read_text());node=next(n for n in tree.body if isinstance(n,(ast.FunctionDef,ast.AsyncFunctionDef)) and n.name==name)
node.decorator_list=[];node.returns=None
for a in node.args.args + node.args.kwonlyargs:a.annotation=None
node.args.defaults=[]
ns=dict(Path=Path,datetime=datetime,timezone=timezone,**extra)
exec(compile(ast.Module(body=[node],type_ignores=[]),str(path),'exec'),ns);return ns[name]
def actual_report():
tree=ast.parse((ROOT/'scripts/consistency_check.py').read_text())
nodes=[n for n in tree.body if (isinstance(n,ast.ClassDef) and n.name in {'Issue','ConsistencyReport'}) or (isinstance(n,ast.AnnAssign) and isinstance(n.target,ast.Name) and n.target.id=='_AUTOMATION_ERROR_CHECKS')]
ns=dict(dataclass=dataclass,field=field,_AUTOMATION_ERROR_CHECKS=frozenset({'C-00','C-44'}))
exec(compile(ast.Module(body=nodes,type_ignores=[]),'actual_report','exec'),ns)
return ns['ConsistencyReport']('railiance-cluster','/unused')
def closure(B=B):
repo=Obj(id=uuid.uuid4());parent=Obj(id=uuid.UUID(B['uuid']),repo_id=repo.id,status='finished')
tasks=[Obj(id=uuid.UUID(t['uuid']),workplan_id=parent.id,status='done') for t in B['tasks']]
return repo,parent,tasks
async def run_api(entries,repo,parent,tasks,ordinary=None):
rows=([parent] if parent else [])+([ordinary] if ordinary else [])
session=Session([rows,[repo] if repo else [],tasks])
fn=extracted('api/routers/workstreams.py','sync_workplan_bindings',dict(frozen_cluster_binding=h.frozen_cluster_binding,validate_frozen_cluster_rows=h.validate_frozen_cluster_rows,HTTPException=HTTPException,select=lambda *_:Query(),Workplan=Model,Task=Model,ManagedRepo=Model,_invalidate_workplan_index_cache=lambda:None))
result=await fn(Obj(bindings=[Entry(**e) for e in entries]),session)
return result,session
@pytest.mark.parametrize('change',[dict(repo_slug='foreign'),dict(workplan_id=uuid.uuid4()),dict(relative_path='workplans/other.md'),dict(filename='other.md'),dict(archived=False),dict(status='active')])
def test_metadata_refuses(change):
with pytest.raises(ValueError):h.frozen_cluster_binding(binding(**change))
def test_unrelated_binding_unchanged():assert h.frozen_cluster_binding(binding(workplan_id=uuid.uuid4(),filename='ordinary.md',relative_path='workplans/ordinary.md',repo_slug='other')) is None
@pytest.mark.parametrize('fault',['parent_missing','foreign_repo','parent_active','task_missing','task_active','task_foreign','repo_missing'])
@pytest.mark.parametrize("bound",h.FROZEN_CLUSTER_BINDINGS)
def test_mixed_batch_refusal_precedes_all_writes(fault,bound):
repo,parent,tasks=closure(bound);ordinary=Obj(id=uuid.uuid4())
if fault=='repo_missing':repo=None
if fault=='parent_missing':parent=None
if fault=='foreign_repo':parent.repo_id=uuid.uuid4()
if fault=='parent_active':parent.status='active'
if fault=='task_missing':tasks.pop()
if fault=='task_active':tasks[0].status='progress'
if fault=='task_foreign':tasks[0].workplan_id=uuid.uuid4()
if parent:parent.writes.clear()
for t in tasks:t.writes.clear()
normal=binding(workplan_id=ordinary.id,repo_slug='ordinary',filename='ordinary.md',relative_path='workplans/ordinary.md')
with pytest.raises(HTTPException):asyncio.run(run_api([normal,binding(_bound=bound)],repo,parent,tasks,ordinary))
assert ordinary.writes==[]
if parent:assert parent.writes==[]
assert all(t.writes==[] for t in tasks)
@pytest.mark.parametrize("bound",h.FROZEN_CLUSTER_BINDINGS)
def test_exact_frozen_no_setters_or_commit(bound):
repo,parent,tasks=closure(bound);result,session=asyncio.run(run_api([binding(_bound=bound)],repo,parent,tasks))
assert result==dict(updated=0,received=1,preserved=1);assert session.commits==0;assert parent.writes==[]
assert all(q.locked and q.refreshed and q.ordered for q in (session.queries[0],session.queries[2]))
@pytest.mark.parametrize("bound",h.FROZEN_CLUSTER_BINDINGS)
def test_mixed_valid_preserves_frozen_and_updates_ordinary(bound):
repo,parent,tasks=closure(bound);ordinary=Obj(id=uuid.uuid4())
normal=binding(workplan_id=ordinary.id,repo_slug='ordinary',filename='ordinary.md',relative_path='workplans/ordinary.md')
result,session=asyncio.run(run_api([normal,binding(_bound=bound)],repo,parent,tasks,ordinary))
assert result==dict(updated=1,received=2,preserved=1);assert session.commits==1;assert parent.writes==[]
assert ordinary.writes==['backing_filename','backing_relative_path','backing_archived','backing_synced_at']
@pytest.mark.parametrize('tamper',['metadata','bytes','symlink','missing_scan','missing_uuid','valid'])
@pytest.mark.parametrize("bound_index", range(3))
def test_cli_exact_source_omission_or_whole_batch_refusal(tmp_path,tamper,bound_index):
bound=h.FROZEN_CLUSTER_BINDINGS[bound_index]
import shutil
infos=[]
for entry in h.FROZEN_CLUSTER_BINDINGS:
target=tmp_path/entry['path'];target.parent.mkdir(parents=True,exist_ok=True)
shutil.copyfile(Path('/home/worsch/railiance-cluster')/entry['path'],target)
infos.append((target,dict(state_hub_workstream_id=entry['uuid'],status='finished'),''))
path=tmp_path/bound['path']
meta=dict(state_hub_workstream_id=bound['uuid'],status='finished')
if tamper=='metadata':meta['status']='active'
if tamper=='missing_uuid':meta.pop('state_hub_workstream_id')
if tamper=='bytes':path.write_text(path.read_text()+'\n')
if tamper=='symlink':path.unlink();path.symlink_to(Path('/home/worsch/railiance-cluster')/bound['path'])
ordinary=tmp_path/'workplans/ordinary.md';ordinary.write_text('ordinary')
if tamper=='missing_scan':infos.pop(bound_index)
else:infos[bound_index]=(path,meta,'')
calls=[];report=actual_report()
fn=extracted('scripts/consistency_check.py','_sync_workplan_bindings',dict(FROZEN_CLUSTER_BINDINGS=h.FROZEN_CLUSTER_BINDINGS,frozen_cluster_binding=h.frozen_cluster_binding,normalise_workstream_status=lambda s:s,workplan_display_path=lambda root,p:p.relative_to(root).as_posix(),_api_put=lambda *a:calls.append(a) or dict(updated=1,received=1)))
fn('http://unused','railiance-cluster',[(ordinary,dict(state_hub_workstream_id=str(uuid.uuid4()),status='active'),''),*infos],tmp_path,report)
if tamper=='valid':assert len(calls)==1 and len(calls[0][2]['bindings'])==1
else:assert calls==[] and report.failures[0].check_id=='C-44' and report.automation_errors
def test_api_tampered_mixed_batch_refuses_before_queries_or_assignments():
repo,parent,tasks=closure();ordinary=Obj(id=uuid.uuid4())
with pytest.raises(HTTPException):
asyncio.run(run_api([binding(workplan_id=ordinary.id,repo_slug='ordinary',filename='ordinary.md',relative_path='workplans/ordinary.md'),binding(archived=False)],repo,parent,tasks,ordinary))
assert ordinary.writes==[] and parent.writes==[]
def test_foreign_repo_same_filename_path_is_ordinary():
assert h.frozen_cluster_binding(binding(workplan_id=uuid.uuid4(),repo_slug='foreign')) is None
@pytest.mark.parametrize('entry',h.FROZEN_CLUSTER_BINDINGS)
def test_all_three_registry_bindings_are_guarded(entry):
good=dict(workplan_id=entry['uuid'],repo_slug=entry['repo'],filename=Path(entry['path']).name,relative_path=entry['path'],archived=True,status='finished')
assert h.frozen_cluster_binding(good) is entry
with pytest.raises(ValueError):h.frozen_cluster_binding(dict(good,status='active'))
def test_real_cli_exit_criterion_is_nonzero_including_remote_all():
report=actual_report();report.add(severity='FAIL',check_id='C-44',message='refused')
fn=extracted('scripts/consistency_check.py','consistency_exit_code',{})
assert fn([report])==1 and fn([report],remote_all=True)==1
@pytest.mark.parametrize('check,key,protected_kind',[
('C-04','ws_id','parent'),('C-10','task_id','task'),('C-22','task_id','task'),
('C-09','db_id','parent'),('C-23','wp_file','path'),
])
@pytest.mark.parametrize('bound',h.FROZEN_CLUSTER_BINDINGS)
def test_complete_fix_batch_preflight_before_ordinary_writes(check,key,protected_kind,bound):
report=actual_report();report.repo_path='/tmp/fixture-cluster'
report.add(severity='WARN',check_id='C-05',message='ordinary first',fixable=True,_fix_context={'ws_id':str(uuid.uuid4()),'field':'title','value':'ordinary'})
value=bound['uuid'] if protected_kind=='parent' else bound['tasks'][0]['uuid'] if protected_kind=='task' else str(Path(report.repo_path)/bound['path'])
report.add(severity='WARN',check_id=check,message='protected later',fixable=True,db_id=value if key=='db_id' else '',_fix_context={key:value})
guard=extracted('scripts/consistency_check.py','_refuse_frozen_fix_batch',dict(FROZEN_CLUSTER_BINDINGS=h.FROZEN_CLUSTER_BINDINGS))
assert guard(report,'railiance-cluster') and report.automation_errors
calls=[]
fn=extracted('scripts/consistency_check.py','fix_repo',dict(check_repo=lambda *a,**kw:report,_refuse_frozen_fix_batch=guard,_api_post=lambda *a:calls.append(a),_api_patch=lambda *a:calls.append(a)))
assert fn('http://unused','railiance-cluster',None,False,False) is report and calls==[]
def test_unrelated_fix_context_not_refused():
report=actual_report();report.add(severity='WARN',check_id='C-05',message='ordinary',fixable=True,_fix_context={'ws_id':str(uuid.uuid4())})
guard=extracted('scripts/consistency_check.py','_refuse_frozen_fix_batch',dict(FROZEN_CLUSTER_BINDINGS=h.FROZEN_CLUSTER_BINDINGS))
assert not guard(report,'other')
def test_check_repo_guard_precedes_binding_sync_and_does_not_duplicate():
tree=ast.parse((ROOT/'scripts/consistency_check.py').read_text())
node=next(n for n in tree.body if isinstance(n,ast.FunctionDef) and n.name=='check_repo')
guard_lines=[n.lineno for n in ast.walk(node) if isinstance(n,ast.Call) and isinstance(n.func,ast.Name) and n.func.id=='_refuse_frozen_fix_batch']
sync_lines=[n.lineno for n in ast.walk(node) if isinstance(n,ast.Call) and isinstance(n.func,ast.Name) and n.func.id=='_sync_workplan_bindings']
assert len(guard_lines)==len(sync_lines)==1 and guard_lines[0]<sync_lines[0]
report=actual_report();report.add(severity='FAIL',check_id='C-44',message='already refused')
guard=extracted('scripts/consistency_check.py','_refuse_frozen_fix_batch',dict(FROZEN_CLUSTER_BINDINGS=h.FROZEN_CLUSTER_BINDINGS))
assert guard(report,'railiance-cluster') and len(report.issues)==1

View file

@ -0,0 +1,339 @@
---
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: 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.

View file

@ -0,0 +1,517 @@
.PHONY: start install install-cli dashboard-install dashboard-check db db-tools migrate seed api dashboard dashboard-local sync check-primary primary-port-clear check check-local test test-python clean register-project register-codex-project register-mcp configure-codex bootstrap-env dev-hub edge-relay mcp-profile validate-adr add-domain rename-domain add-repo list-repos register-path register-from-classification register-from-classification-all cleanup-stale tunnels-up tunnels-status tunnels-check bridges install-hooks install-hooks-all gitea-inventory token-reconcile railiance-state-hub-render railiance-state-hub-client-dry-run railiance-state-hub-server-dry-run railiance-state-hub-headroom
COMPOSE = docker compose -f infra/docker-compose.yml --env-file .env
PYTHON ?= python3
HELM ?= $(shell command -v helm 2>/dev/null || if [ -x "$$HOME/.local/bin/helm" ]; then printf "%s" "$$HOME/.local/bin/helm"; else printf "%s" "helm"; fi)
KUBECTL ?= $(shell command -v kubectl 2>/dev/null || if [ -x "$$HOME/.local/bin/kubectl" ]; then printf "%s" "$$HOME/.local/bin/kubectl"; else printf "%s" "kubectl"; fi)
RAILIANCE_STATE_HUB_RELEASE ?= state-hub
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-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.
UV ?= $(shell command -v uv 2>/dev/null || if [ -x "$$HOME/.local/bin/uv" ]; then printf "%s" "$$HOME/.local/bin/uv"; else printf "%s" "uv"; fi)
RMGR ?= $(shell command -v rmgr 2>/dev/null || if [ -x "$$HOME/repo-manager/.venv/bin/rmgr" ]; then printf "%s" "$$HOME/repo-manager/.venv/bin/rmgr"; else printf "%s" "rmgr"; fi)
STATE_HUB_API_BASE ?= http://127.0.0.1:8000
SYNC_PATH ?= .
SYNC_PUSH ?= 1
start:
@echo "# Normal production UI access (run in order)"
@echo "make bridges # connect local :8000 to the railiance01 primary"
@echo "make dashboard # verify the primary, then serve the UI on :3000"
@echo "make sync # push file-backed records and reconcile the exact forge commit"
@echo ""
@echo "# Optional local MCP adapter"
@echo "make mcp-http # local SSE adapter on :8001; not needed by the UI"
@echo ""
@echo "# Local fallback/development only"
@echo "make api # local Postgres + API; conflicts with the production :8000 tunnel"
@echo "make dashboard-local # dashboard against the deliberate local API"
install:
$(UV) sync
dashboard/node_modules/.bin/observable: dashboard/package.json dashboard/package-lock.json
cd dashboard && npm ci
dashboard-install: dashboard/node_modules/.bin/observable
dashboard-check: dashboard-install
cd dashboard && npm run build
## Symlink the custodian CLI into ~/.local/bin so it's on PATH system-wide
install-cli: install
mkdir -p ~/.local/bin
ln -sf "$(shell pwd)/.venv/bin/custodian" ~/.local/bin/custodian
ln -sf "$(shell pwd)/.venv/bin/statehub" ~/.local/bin/statehub
@echo "Installed: custodian → $$(readlink -f ~/.local/bin/custodian)"
@echo "Installed: statehub → $$(readlink -f ~/.local/bin/statehub)"
@echo "Make sure ~/.local/bin is on your PATH:"
@echo " echo 'export PATH=\"\$$HOME/.local/bin:\$$PATH\"' >> ~/.bashrc && source ~/.bashrc"
db:
$(COMPOSE) up -d postgres
db-tools:
$(COMPOSE) --profile tools up -d
migrate:
$(UV) run alembic upgrade head
seed:
$(UV) run python scripts/seed.py
## Start (or restart) the MCP SSE server on :8001 — primary transport for Claude Code.
## Remote clients (e.g. COULOMBCORE) connect via the ops-bridge tunnel (port 18001).
## Registration: claude mcp add-json -s user state-hub '{"type":"sse","url":"http://127.0.0.1:8001/sse"}'
mcp-http:
@fuser -k 8001/tcp 2>/dev/null && echo "Stopped running MCP server" || true
MCP_TRANSPORT=sse MCP_PORT=8001 $(UV) run python mcp_server/server.py
## Require the production identity before serving the normal dashboard. This
## prevents an accidental local `make api` from presenting an empty fallback DB
## as the live State Hub.
check-primary:
@health="$$(curl -fsS --max-time 5 http://127.0.0.1:8000/state/health 2>/dev/null)" || { \
echo "ERROR: State Hub primary is not reachable on 127.0.0.1:8000." >&2; \
echo "Run 'make bridges' first." >&2; \
exit 1; \
}; \
identity="$$(printf '%s' "$$health" | $(PYTHON) -c 'import json, sys; d=json.load(sys.stdin); print("{}/{}".format(d.get("instance_role", ""), d.get("instance_label", "")))')"; \
if [ "$$identity" != "primary/railiance01" ]; then \
echo "ERROR: 127.0.0.1:8000 is '$$identity', not the primary/railiance01 State Hub." >&2; \
echo "Stop the local API, then run 'make bridges'. Use 'make dashboard-local' only for intentional local development." >&2; \
exit 1; \
fi
## Refuse to start the production tunnel when a non-primary API already owns
## port 8000. With no listener, bridge is free to establish the tunnel.
primary-port-clear:
@if health="$$(curl -fsS --max-time 2 http://127.0.0.1:8000/state/health 2>/dev/null)"; then \
identity="$$(printf '%s' "$$health" | $(PYTHON) -c 'import json, sys; d=json.load(sys.stdin); print("{}/{}".format(d.get("instance_role", ""), d.get("instance_label", "")))')"; \
if [ "$$identity" != "primary/railiance01" ]; then \
echo "ERROR: port 8000 is occupied by '$$identity'." >&2; \
echo "Stop the local 'make api' process before starting the production tunnel." >&2; \
exit 1; \
fi; \
fi
dashboard: check-primary
@fuser -k 3000/tcp 2>/dev/null && echo "Stopped running dashboard" || true
$(MAKE) dashboard-install
cd dashboard && npm run dev
## Deliberate local-development dashboard; bypasses the production identity gate.
dashboard-local:
@echo "WARNING: serving the dashboard against the local/fallback API on :8000."
@fuser -k 3000/tcp 2>/dev/null && echo "Stopped running dashboard" || true
$(MAKE) dashboard-install
cd dashboard && npm run dev
## Fast work-record path. Repo Manager assigns only missing deterministic IDs,
## verifies the primary/railliance01 bridge, and asks central to derive the
## exact pushed Forgejo commit in one transactional request.
sync:
$(RMGR) sync --path "$(SYNC_PATH)" --api-base "$(STATE_HUB_API_BASE)" $(if $(filter 1 true yes,$(SYNC_PUSH)),--push,)
check: check-primary
@echo "State Hub primary/railiance01 is healthy."
check-local:
curl -sf http://127.0.0.1:8000/state/health | python3 -m json.tool
# CUST-WP-0067-T09. The chart ships a copy of the-custodian canon allowed-values
# because a container has no checkout to read it from. Regenerate after any
# canon vocabulary change; the -check target fails on drift so a release cannot
# validate classification against a stale vocabulary.
sync-classification-allowed:
python3 scripts/sync_classification_allowed.py
check-classification-allowed:
python3 scripts/sync_classification_allowed.py --check
# STATE-WP-0091: read-only surge-headroom check. Does not start Helm.
# Live: make railiance-state-hub-headroom
# Fixtures: make railiance-state-hub-headroom HEADROOM_FIXTURE=tests/fixtures/release_headroom/rev63-105m.json
HEADROOM_FIXTURE ?=
railiance-state-hub-headroom:
@if [ -n "$(HEADROOM_FIXTURE)" ]; then \
$(PYTHON) scripts/release_headroom_preflight.py --fixture "$(HEADROOM_FIXTURE)"; \
else \
$(PYTHON) scripts/release_headroom_preflight.py --kubectl "$(KUBECTL)"; \
fi
railiance-state-hub-render: check-classification-allowed
$(HELM) template $(RAILIANCE_STATE_HUB_RELEASE) $(RAILIANCE_STATE_HUB_CHART) \
--namespace $(RAILIANCE_STATE_HUB_NAMESPACE) \
-f $(RAILIANCE_STATE_HUB_VALUES) \
--set image.tag=$(RAILIANCE_STATE_HUB_IMAGE_TAG)
railiance-state-hub-client-dry-run:
@set -e; \
tmpdir="$$(mktemp -d)"; \
trap 'rm -rf "$$tmpdir"' EXIT; \
$(HELM) template $(RAILIANCE_STATE_HUB_RELEASE) $(RAILIANCE_STATE_HUB_CHART) \
--namespace $(RAILIANCE_STATE_HUB_NAMESPACE) \
-f $(RAILIANCE_STATE_HUB_VALUES) \
--set image.tag=$(RAILIANCE_STATE_HUB_IMAGE_TAG) > "$$tmpdir/state-hub.yaml"; \
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-credentials.sops.yaml.template; \
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-cluster.yaml; \
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-networkpolicies.yaml; \
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_APP_MANIFESTS)/state-hub-namespace.yaml; \
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_APP_MANIFESTS)/state-hub-env.secret.sops.yaml.template; \
$(KUBECTL) apply --dry-run=client -n $(RAILIANCE_STATE_HUB_NAMESPACE) -f "$$tmpdir/state-hub.yaml"
railiance-state-hub-server-dry-run:
@set -e; \
tmpdir="$$(mktemp -d)"; \
trap 'rm -rf "$$tmpdir"' EXIT; \
$(HELM) template $(RAILIANCE_STATE_HUB_RELEASE) $(RAILIANCE_STATE_HUB_CHART) \
--namespace $(RAILIANCE_STATE_HUB_NAMESPACE) \
-f $(RAILIANCE_STATE_HUB_VALUES) \
--set image.tag=$(RAILIANCE_STATE_HUB_IMAGE_TAG) > "$$tmpdir/state-hub.yaml"; \
$(KUBECTL) apply --server-side --dry-run=server -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-credentials.sops.yaml.template; \
$(KUBECTL) apply --dry-run=server -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-cluster.yaml; \
$(KUBECTL) apply --dry-run=server -f $(RAILIANCE_STATE_HUB_PLATFORM_DIR)/state-hub-db-networkpolicies.yaml; \
$(KUBECTL) apply --dry-run=server -f $(RAILIANCE_STATE_HUB_APP_MANIFESTS)/state-hub-namespace.yaml; \
if $(KUBECTL) get namespace $(RAILIANCE_STATE_HUB_NAMESPACE) >/dev/null 2>&1; then \
$(KUBECTL) apply --server-side --dry-run=server -f $(RAILIANCE_STATE_HUB_APP_MANIFESTS)/state-hub-env.secret.sops.yaml.template; \
$(KUBECTL) apply --dry-run=server -n $(RAILIANCE_STATE_HUB_NAMESPACE) -f "$$tmpdir/state-hub.yaml"; \
else \
echo "Namespace $(RAILIANCE_STATE_HUB_NAMESPACE) does not exist; validating namespaced app manifests with client dry-run."; \
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_APP_MANIFESTS)/state-hub-namespace.yaml; \
$(KUBECTL) apply --dry-run=client -f $(RAILIANCE_STATE_HUB_APP_MANIFESTS)/state-hub-env.secret.sops.yaml.template; \
$(KUBECTL) apply --dry-run=client -n $(RAILIANCE_STATE_HUB_NAMESPACE) -f "$$tmpdir/state-hub.yaml"; \
fi
test: test-python dashboard-check
test-python:
TEST_DATABASE_URL=postgresql+asyncpg://custodian:changeme@127.0.0.1:5432/custodian_test \
$(UV) run pytest -x -q
## Benchmark /state/summary revision cache (API must be running on :8000)
benchmark-summary-cache:
$(UV) run python scripts/benchmark_summary_cache.py
## ops-bridge managed tunnels
## Requires ops-bridge: bridge is at /home/worsch/.local/bin/bridge
tunnels-up: primary-port-clear
bridge up
tunnels-status:
bridge status
## End-to-end check: verifies SSH process alive + remote port listening on COULOMBCORE.
## Exits non-zero if any tunnel is not fully operational.
tunnels-check:
bridge check
## Ensure all ops-bridge tunnels are up and healthy.
## Brings up any stopped/stale tunnels, shows final status, exits non-zero if anything is still down.
bridges: primary-port-clear
@echo "==> Bringing up all tunnels..."
bridge up
@echo ""
@echo "==> Tunnel status:"
bridge status
@echo ""
@echo "==> Checking tunnel health..."
bridge check
## Start (or restart) the LOCAL FALLBACK backend — db + migrate + uvicorn.
## This replaces anything on :8000, including the production State Hub tunnel.
api: db
@echo "WARNING: starting the local fallback API; this is not the railiance01 primary."
@echo "Waiting for postgres..."; \
for i in 1 2 3 4 5 6 7 8 9 10; do \
nc -z 127.0.0.1 5432 2>/dev/null && break; \
sleep 1; \
done
$(MAKE) migrate
@fuser -k 8000/tcp 2>/dev/null && echo "Stopped running API" || true
$(UV) run uvicorn api.main:app --reload --reload-dir api --reload-dir mcp_server --reload-dir task_flow_engine --host 127.0.0.1 --port 8000
## Register a project (Claude Code): make register-project DOMAIN=railiance PROJECT_PATH=/home/worsch/railiance
register-project:
@test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN is required. Usage: make register-project DOMAIN=<domain> PROJECT_PATH=<path>"; exit 1)
@test -n "$(PROJECT_PATH)" || (echo "ERROR: PROJECT_PATH is required."; exit 1)
scripts/register_project.sh "$(DOMAIN)" "$(PROJECT_PATH)"
## Register a Codex project (AGENTS.md + HTTP API): make register-codex-project DOMAIN=capabilities PROJECT_PATH=/home/worsch/my-repo
register-codex-project:
@test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN is required. Usage: make register-codex-project DOMAIN=<domain> PROJECT_PATH=<path>"; exit 1)
@test -n "$(PROJECT_PATH)" || (echo "ERROR: PROJECT_PATH is required."; exit 1)
scripts/register_project.sh "$(DOMAIN)" "$(PROJECT_PATH)" --codex
## Register State Hub MCP for Claude Code. Optional: make register-mcp MCP_URL=http://127.0.0.1:18001/sse
register-mcp:
scripts/register-mcp.sh \
$(if $(MCP_URL),--url "$(MCP_URL)",) \
$(if $(API_BASE),--api-base "$(API_BASE)",) \
$(if $(DRY_RUN),--dry-run,)
## Configure Codex workspace-write networking for local State Hub access.
configure-codex:
scripts/configure-codex.sh \
$(if $(CODEX_HOME),--codex-home "$(CODEX_HOME)",) \
$(if $(API_BASE),--api-base "$(API_BASE)",) \
$(if $(WITH_MCP),--with-mcp,) \
$(if $(DRY_RUN),--dry-run,)
## Bootstrap a new operator/collaborator environment. Optional: make bootstrap-env ARGS="--install-missing"
bootstrap-env:
scripts/bootstrap-env.sh $(ARGS)
## Files-first local dev hub (CUST-WP-0054-T07): postgres + migrate + seed + API + repo registration.
## Optional: WITH_EDGE=1 WITH_MCP=1 make dev-hub
## Fleet tunnel when reachable: make dev-hub PROFILE=fleet
dev-hub:
bash scripts/dev_hub_up.sh $(if $(PROFILE),$(PROFILE),dev)
## Local edge relay for offline write buffering (STATE-WP-0068).
edge-relay:
@fuser -k 18080/tcp 2>/dev/null && echo "Stopped running edge relay" || true
STATEHUB_UPSTREAM_URL=$${STATEHUB_UPSTREAM_URL:-http://127.0.0.1:8000} \
$(UV) run uvicorn api.edge.relay:app --host 127.0.0.1 --port 18080
## Switch MCP/API profile: make mcp-profile PROFILE=dev|fleet|status
mcp-profile:
@test -n "$(PROFILE)" || (echo "ERROR: PROFILE is required (dev|fleet|status)."; exit 1)
bash scripts/mcp_hub_profile.sh "$(PROFILE)"
## Add a second repo to an existing domain: make add-repo DOMAIN=railiance REPO_PATH=/home/worsch/railiance-infra
add-repo:
@test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN is required."; exit 1)
@test -n "$(REPO_PATH)" || (echo "ERROR: REPO_PATH is required."; exit 1)
scripts/register_project.sh "$(DOMAIN)" "$(REPO_PATH)" --additional
## Create a new domain: make add-domain DOMAIN=my_domain NAME="My Domain"
add-domain:
@test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN is required (slug)."; exit 1)
@test -n "$(NAME)" || (echo "ERROR: NAME is required (display name)."; exit 1)
curl -sf -X POST http://127.0.0.1:8000/domains/ \
-H "Content-Type: application/json" \
-d "{\"slug\": \"$(DOMAIN)\", \"name\": \"$(NAME)\"}" | python3 -m json.tool
## Rename a domain: make rename-domain DOMAIN=old_slug NEW_SLUG=new_slug NEW_NAME="New Name"
rename-domain:
@test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN (old slug) is required."; exit 1)
@test -n "$(NEW_SLUG)" || (echo "ERROR: NEW_SLUG is required."; exit 1)
@test -n "$(NEW_NAME)" || (echo "ERROR: NEW_NAME is required."; exit 1)
curl -sf -X PATCH http://127.0.0.1:8000/domains/$(DOMAIN)/rename \
-H "Content-Type: application/json" \
-d "{\"new_slug\": \"$(NEW_SLUG)\", \"new_name\": \"$(NEW_NAME)\"}" | python3 -m json.tool
## Register this machine's local path for a repo: make register-path REPO=marki-docx PATH=/home/tegwick/marki-docx
register-path:
@test -n "$(REPO)" || (echo "ERROR: REPO is required. Usage: make register-path REPO=<slug> PATH=<path>"; exit 1)
@test -n "$(PATH)" || (echo "ERROR: PATH is required. Usage: make register-path REPO=<slug> PATH=<path>"; exit 1)
curl -sf -X POST "http://127.0.0.1:8000/repos/$(REPO)/paths" \
-H "Content-Type: application/json" \
-d "{\"host\": \"$$(hostname)\", \"path\": \"$(PATH)\"}" | python3 -m json.tool
## List repos for a domain: make list-repos DOMAIN=railiance
list-repos:
@test -n "$(DOMAIN)" || (echo "ERROR: DOMAIN is required."; exit 1)
curl -sf "http://127.0.0.1:8000/repos/?domain=$(DOMAIN)" | python3 -m json.tool
## Ingest SBOM data for a repo (all mechanisms: lockfiles + ansible + sbom-tools.yaml).
## Auto-detect all sources: make ingest-sbom REPO=the-custodian REPO_PATH=/home/worsch/the-custodian
## Single lockfile (explicit): make ingest-sbom REPO=the-custodian LOCKFILE=/path/to/uv.lock
## Dry-run (no submit): make ingest-sbom REPO=the-custodian REPO_PATH=... DRY_RUN=1
## Tip: run capture-tools first for repos with system-level tool dependencies.
ingest-sbom:
@test -n "$(REPO)" || (echo "ERROR: REPO is required."; exit 1)
$(UV) run python scripts/ingest_sbom.py --repo "$(REPO)" \
$(if $(LOCKFILE),--lockfile "$(LOCKFILE)") \
$(if $(REPO_PATH),--repo-path "$(REPO_PATH)") \
$(if $(DRY_RUN),--dry-run)
## Ingest capability declarations from SCOPE.md into the catalog.
## Usage: make ingest-capabilities REPO=the-custodian [REPO_PATH=/home/worsch/the-custodian]
## Or: make ingest-capabilities-all
## Add DRY_RUN=1 to preview without writing.
ingest-capabilities:
@test -n "$(REPO)" || (echo "ERROR: REPO is required."; exit 1)
$(UV) run python scripts/ingest_capabilities.py --repo "$(REPO)" \
$(if $(REPO_PATH),--repo-path "$(REPO_PATH)") \
$(if $(DRY_RUN),--dry-run)
ingest-capabilities-all:
$(UV) run python scripts/ingest_capabilities.py --all \
$(if $(DRY_RUN),--dry-run)
## Check Repository Definition of Integrated (DoI) criteria for a repo.
## Usage: make check-doi REPO=llm-connect
## Or: make check-doi-all
## Add JSON=1 for machine-readable output.
check-doi:
@test -n "$(REPO)" || (echo "ERROR: REPO is required."; exit 1)
$(UV) run python scripts/check_doi.py --repo "$(REPO)" $(if $(JSON),--json)
check-doi-all:
$(UV) run python scripts/check_doi.py --all $(if $(JSON),--json)
## Ingest tpsc.yaml service declarations from a repo into the TPSC catalog.
## Usage: make ingest-tpsc REPO=llm-connect
## Or: make ingest-tpsc-all
## Add DRY_RUN=1 to preview without writing.
ingest-tpsc:
@test -n "$(REPO)" || (echo "ERROR: REPO is required."; exit 1)
$(UV) run python scripts/ingest_tpsc.py --repo "$(REPO)" \
$(if $(DRY_RUN),--dry-run)
ingest-tpsc-all:
$(UV) run python scripts/ingest_tpsc.py --all \
$(if $(DRY_RUN),--dry-run)
## Run SBOM capture agent for a repo — generates/updates sbom-tools.yaml.
## Usage: make capture-tools REPO=railiance-infra [REPO_PATH=/home/worsch/railiance-infra]
## Add DRY_RUN=1 to preview without writing.
capture-tools:
@test -n "$(REPO)" || (echo "ERROR: REPO is required."; exit 1)
$(UV) run python scripts/capture_sbom_tools.py --repo "$(REPO)" \
$(if $(REPO_PATH),--repo-path "$(REPO_PATH)") \
$(if $(DRY_RUN),--dry-run)
## Check a repo for ADR-001 compliance: make validate-adr REPO=/path/to/repo [DOMAIN=custodian]
validate-adr:
@test -n "$(REPO)" || (echo "ERROR: REPO is required. Usage: make validate-adr REPO=<path> [DOMAIN=<slug>]"; exit 1)
$(UV) run python scripts/validate_repo_adr.py "$(REPO)" $(if $(DOMAIN),--domain "$(DOMAIN)",)
## Consistency exit contract:
## - Direct scripts/consistency_check.py: 0 clean, 2 warnings-only, 1 failures.
## - Agent/operator Make wrappers below normalize warning-only 2 to shell success
## while preserving visible WARN output and keeping real failures non-zero.
## Check a single repo for ADR-001 consistency: make check-consistency REPO=the-custodian [REPO_PATH=/override]
## Exit 0 = clean or warnings-only (warnings stay visible), exit 1 = failures
check-consistency:
@test -n "$(REPO)" || (echo "ERROR: REPO is required. Usage: make check-consistency REPO=<slug>"; exit 1)
$(UV) run python scripts/consistency_check.py --repo "$(REPO)" \
$(if $(API_BASE),--api-base "$(API_BASE)",) \
$(if $(REPO_PATH),--repo-path "$(REPO_PATH)",); \
e=$$?; [ $$e -eq 2 ] && exit 0 || exit $$e
## Check and auto-fix a single repo: make fix-consistency REPO=the-custodian [REPO_PATH=/override]
## Exit 0 = clean or warnings-only (warnings stay visible), exit 1 = failures
fix-consistency:
@test -n "$(REPO)" || (echo "ERROR: REPO is required. Usage: make fix-consistency REPO=<slug>"; exit 1)
$(UV) run python scripts/consistency_check.py --repo "$(REPO)" --fix \
$(if $(API_BASE),--api-base "$(API_BASE)",) \
$(if $(REPO_PATH),--repo-path "$(REPO_PATH)",); \
e=$$?; [ $$e -eq 2 ] && exit 0 || exit $$e
## Normalize workplan frontmatter and task status literals in attached repos.
## Usage: make normalize-attached-workplans REPO=artifact-store
## make normalize-attached-workplans DIRTY=1
normalize-attached-workplans:
$(UV) run python scripts/normalize_attached_repo_workplans.py \
$(if $(REPO),--repo "$(REPO)",) \
$(if $(DIRTY),--dirty,) \
$(if $(DRY_RUN),--dry-run,)
@test -n "$(REPO)$(DIRTY)" || (echo "ERROR: set REPO=<slug> or DIRTY=1"; exit 1)
## Regenerate AGENTS.md / CLAUDE.md / .claude/rules from templates.
## Usage: make update-agent-instructions REPO=artifact-store
## make update-agent-instructions DIRTY=1
update-agent-instructions:
$(UV) run python scripts/update_agent_instruction_files.py \
$(if $(REPO),--repo "$(REPO)",) \
$(if $(DIRTY),--dirty,)
@test -n "$(REPO)$(DIRTY)" || (echo "ERROR: set REPO=<slug> or DIRTY=1"; exit 1)
## Reconcile measured token sources against State Hub.
## Usage: make token-reconcile [SINCE=2026-05-19] [APPLY=1] [ZERO_FALLBACKS=1]
token-reconcile:
$(PYTHON) scripts/token_reconcile.py \
$(if $(SINCE),--since "$(SINCE)",) \
$(if $(API_BASE),--api-base "$(API_BASE)",) \
$(if $(CODEX_HOME),--codex-home "$(CODEX_HOME)",) \
$(if $(CLAUDE_HOME),--claude-home "$(CLAUDE_HOME)",) \
$(if $(APPLY),--apply,) \
$(if $(ZERO_FALLBACKS),--zero-superseded-fallbacks,)
## Pull then fix: single repo or all repos if REPO omitted
## make fix-consistency-remote — smart pull+fix all repos that need it
## make fix-consistency-remote REPO=slug — pull+fix one repo
fix-consistency-remote:
$(UV) run python scripts/consistency_check.py \
$(if $(REPO),--repo "$(REPO)",--all) \
--remote \
$(if $(API_BASE),--api-base "$(API_BASE)",) \
$(if $(NO_WRITEBACK),--no-writeback,); \
e=$$?; [ $$e -eq 2 ] && exit 0 || exit $$e
## Infer repo slug from git remote URL and check: make check-consistency-here [REPO_PATH=/path/to/repo]
## Omit REPO_PATH to use the Python script's CWD (i.e. pass an empty --here flag).
check-consistency-here:
$(UV) run python scripts/consistency_check.py \
--here $(if $(REPO_PATH),"$(REPO_PATH)",) \
$(if $(API_BASE),--api-base "$(API_BASE)",); \
e=$$?; [ $$e -eq 2 ] && exit 0 || exit $$e
## Infer repo slug from git remote URL and fix: make fix-consistency-here [REPO_PATH=/path/to/repo]
fix-consistency-here:
$(UV) run python scripts/consistency_check.py \
--here $(if $(REPO_PATH),"$(REPO_PATH)",) \
--fix \
$(if $(API_BASE),--api-base "$(API_BASE)",); \
e=$$?; [ $$e -eq 2 ] && exit 0 || exit $$e
## Check all registered repos for ADR-001 consistency
check-consistency-all:
$(UV) run python scripts/consistency_check.py --all $(if $(API_BASE),--api-base "$(API_BASE)",); \
e=$$?; [ $$e -eq 2 ] && exit 0 || exit $$e
## Check and auto-fix all registered repos
fix-consistency-all:
$(UV) run python scripts/consistency_check.py --all --fix $(if $(API_BASE),--api-base "$(API_BASE)",); \
e=$$?; [ $$e -eq 2 ] && exit 0 || exit $$e
## Cancel open tasks belonging to completed/archived workstreams.
## Safe to run at any time; also suitable for a daily cron job.
## Cron example: 0 3 * * * cd ~/state-hub && make cleanup-stale
cleanup-stale:
$(UV) run python scripts/cleanup_stale_tasks.py
## Install custodian post-commit sync hook into one repo: make install-hooks REPO=marki-docx
install-hooks:
@test -n "$(REPO)" || (echo "ERROR: REPO is required. Usage: make install-hooks REPO=<slug>"; exit 1)
bash scripts/install_hooks.sh --repo "$(REPO)"
## Install custodian post-commit sync hook into all active registered repos
install-hooks-all:
bash scripts/install_hooks.sh --all
## Remove custodian post-commit sync hook from one repo: make remove-hooks REPO=marki-docx
remove-hooks:
@test -n "$(REPO)" || (echo "ERROR: REPO is required. Usage: make remove-hooks REPO=<slug>"; exit 1)
bash scripts/install_hooks.sh --repo "$(REPO)" --remove
## Compare Gitea coulomb org repos against state-hub registered repos
## Requires GITEA_TOKEN in env or .env: make gitea-inventory GITEA_TOKEN=<token>
gitea-inventory:
$(UV) run python scripts/gitea_inventory.py $(if $(JSON),--json)
## Register/update one repo from .repo-classification.yaml:
## make register-from-classification REPO=state-hub
## make register-from-classification PATH=/path/to/repo
## Optional: DRY_RUN=1
register-from-classification:
@test -n "$(REPO)" -o -n "$(PATH)" || (echo "ERROR: REPO or PATH is required."; exit 1)
$(UV) run python scripts/register_from_classification.py \
$(if $(PATH),--repo-path "$(PATH)",--slug "$(REPO)") \
$(if $(DRY_RUN),--dry-run,)
## Bulk register/update all active repos with accessible local paths
register-from-classification-all:
$(UV) run python scripts/register_from_classification.py --bulk \
$(if $(DRY_RUN),--dry-run,)
clean:
$(COMPOSE) down -v

View file

@ -0,0 +1,591 @@
import asyncio
import logging
import uuid
import socket
import time
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
import yaml
from fastapi import APIRouter, Depends, HTTPException, Query, Request, Response, status
from sqlalchemy import or_, select
from sqlalchemy.ext.asyncio import AsyncSession
from api.database import get_session
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,
WorkplanRead,
WorkplanUpdate,
)
from api.services.lifecycle import transition_workplan_status
from api.services.legacy_compat import retire_legacy_route
from api.services.wait_kind import annotate_workplans
from api.work_record_flavor import (
RESIDUAL_FLAVOR,
WORK_RECORD_FLAVORS,
normalize_flavor,
)
from api.workplan_status import (
is_supported_workplan_status,
normalize_workplan_status,
ready_review_status,
)
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/workstreams", tags=["workstreams"])
workplan_router = APIRouter(prefix="/workplans", tags=["workplans"])
_INDEX_CACHE: dict[str, Any] | None = None
_INDEX_CACHE_AT: float = 0.0
_INDEX_TTL = 30.0
_INDEX_REFRESH_TASK: asyncio.Task | None = None
_INDEX_LAST_ERROR: str | None = None
_COMPLETED_WORKPLAN_EVENT = "org.statehub.workplan.completed"
def _repo_path(repo: ManagedRepo) -> Path | None:
hostname = socket.gethostname()
candidates = []
host_paths = repo.host_paths or {}
if host_paths.get(hostname):
candidates.append(host_paths[hostname])
if repo.local_path:
candidates.append(repo.local_path)
for raw in candidates:
path = Path(raw).expanduser()
if path.is_dir():
return path
return None
def _frontmatter(path: Path) -> dict[str, Any]:
try:
text = path.read_text(encoding="utf-8")
except OSError:
return {}
if not text.startswith("---\n"):
return {}
end = text.find("\n---", 4)
if end == -1:
return {}
try:
return yaml.safe_load(text[4:end].strip()) or {}
except yaml.YAMLError:
return {}
def _legacy_key(method: str, route: str) -> str:
return f"rest_api:{method} {route}"
async def _list_workplans(
*,
topic_id: uuid.UUID | None,
repo_id: uuid.UUID | None,
repo_goal_id: uuid.UUID | None,
status_filter: str | None,
owner: str | None,
slug: str | None,
flavor: str | None = None,
include_residuals: bool = True,
session: AsyncSession,
) -> list[Workplan]:
q = select(Workplan)
if topic_id:
q = q.where(Workplan.topic_id == topic_id)
if repo_id:
q = q.where(Workplan.repo_id == repo_id)
if repo_goal_id:
q = q.where(Workplan.repo_goal_id == repo_goal_id)
if status_filter:
normalised_status = normalize_workplan_status(status_filter)
if not is_supported_workplan_status(status_filter):
raise HTTPException(status_code=422, detail=f"Unsupported workplan status '{status_filter}'")
q = q.where(Workplan.status == normalised_status)
if owner:
q = q.where(Workplan.owner == owner)
if slug:
q = q.where(Workplan.slug == slug)
wanted = normalize_flavor(flavor)
if wanted is not None:
if wanted not in WORK_RECORD_FLAVORS:
raise HTTPException(
status_code=422,
detail=(
f"Unknown work-record flavor {wanted!r}; "
f"expected one of {', '.join(WORK_RECORD_FLAVORS)}"
),
)
q = q.where(Workplan.flavor == wanted)
elif not include_residuals:
q = q.where(or_(Workplan.flavor.is_(None), Workplan.flavor != RESIDUAL_FLAVOR))
q = q.order_by(
Workplan.planning_priority.asc().nullslast(),
Workplan.planning_order.asc().nullslast(),
Workplan.updated_at.desc(),
)
result = await session.execute(q)
return list(result.scalars().all())
async def _build_workplan_index(session: AsyncSession) -> dict[str, Any]:
result = await session.execute(
select(ManagedRepo).where(ManagedRepo.status == "active").order_by(ManagedRepo.slug)
)
index: dict[str, Any] = {}
for repo in result.scalars().all():
root = _repo_path(repo)
if root is None:
continue
for directory, archived in (
(root / "workplans", False),
(root / "workplans" / "archived", True),
):
if not directory.is_dir():
continue
for path in sorted(directory.glob("*.md")):
data = _frontmatter(path)
workplan_id = data.get("state_hub_workstream_id") or data.get("state_hub_workplan_id")
if not workplan_id:
continue
file_status = normalize_workplan_status(data.get("status", ""))
review = (
ready_review_status(
root,
data.get("reviewed_against_commit"),
data.get("context_paths"),
)
if file_status == "ready"
else None
)
index[str(workplan_id)] = {
"filename": path.name,
"relative_path": str(path.relative_to(root)),
"repo_slug": repo.slug,
"archived": archived,
"status": file_status or None,
"needs_review": bool(review and review.needs_review),
"health_labels": ["needs_review"] if review and review.needs_review else [],
}
await _merge_db_backing_index(session, index)
return {"workplans": index, "workstreams": index}
async def _merge_db_backing_index(session: AsyncSession, index: dict[str, Any]) -> None:
"""Fill index gaps from DB-backed file bindings synced by fix-consistency."""
result = await session.execute(
select(Workplan, ManagedRepo.slug)
.join(ManagedRepo, Workplan.repo_id == ManagedRepo.id)
.where(Workplan.backing_filename.isnot(None))
)
for wp, repo_slug in result.all():
key = str(wp.id)
if key in index:
continue
index[key] = {
"filename": wp.backing_filename,
"relative_path": wp.backing_relative_path,
"repo_slug": repo_slug,
"archived": bool(wp.backing_archived),
"status": normalize_workplan_status(wp.status) if wp.status else None,
"needs_review": False,
"health_labels": [],
}
def _invalidate_workplan_index_cache() -> None:
global _INDEX_CACHE, _INDEX_CACHE_AT
_INDEX_CACHE = None
_INDEX_CACHE_AT = 0.0
def _index_with_meta(*, stale: bool, refresh_in_progress: bool) -> dict[str, Any]:
age = time.monotonic() - _INDEX_CACHE_AT if _INDEX_CACHE_AT else None
return {
**(_INDEX_CACHE or {"workplans": {}, "workstreams": {}}),
"_meta": {
"generated_at": _INDEX_CACHE.get("_meta", {}).get("generated_at") if _INDEX_CACHE else None,
"stale": stale,
"cache_age_seconds": round(age, 3) if age is not None else None,
"refresh_in_progress": refresh_in_progress,
"last_error": _INDEX_LAST_ERROR,
},
}
async def _refresh_workplan_index_background() -> None:
global _INDEX_CACHE, _INDEX_CACHE_AT, _INDEX_LAST_ERROR
from api.database import async_session_factory
try:
async with async_session_factory() as session:
index = await _build_workplan_index(session)
index["_meta"] = {
"generated_at": datetime.now(timezone.utc).isoformat(),
"stale": False,
"cache_age_seconds": 0.0,
"refresh_in_progress": False,
"last_error": None,
}
_INDEX_CACHE = index
_INDEX_CACHE_AT = time.monotonic()
_INDEX_LAST_ERROR = None
except Exception as exc:
_INDEX_LAST_ERROR = str(exc)
def _ensure_index_refresh_started() -> None:
global _INDEX_REFRESH_TASK
if _INDEX_REFRESH_TASK is not None and not _INDEX_REFRESH_TASK.done():
return
_INDEX_REFRESH_TASK = asyncio.create_task(_refresh_workplan_index_background())
async def _workplan_index(
*,
refresh: bool,
session: AsyncSession,
) -> dict[str, Any]:
"""Map file-backed workplan ids to their local workplan filenames."""
global _INDEX_CACHE, _INDEX_CACHE_AT, _INDEX_LAST_ERROR
cache_age = time.monotonic() - _INDEX_CACHE_AT if _INDEX_CACHE_AT else None
if not refresh and _INDEX_CACHE is not None and cache_age is not None and cache_age < _INDEX_TTL:
refresh_running = _INDEX_REFRESH_TASK is not None and not _INDEX_REFRESH_TASK.done()
return _index_with_meta(stale=False, refresh_in_progress=refresh_running)
if not refresh and _INDEX_CACHE is not None:
_ensure_index_refresh_started()
return _index_with_meta(stale=True, refresh_in_progress=True)
index = await _build_workplan_index(session)
index["_meta"] = {
"generated_at": datetime.now(timezone.utc).isoformat(),
"stale": False,
"cache_age_seconds": 0.0,
"refresh_in_progress": False,
"last_error": None,
}
_INDEX_CACHE = index
_INDEX_CACHE_AT = time.monotonic()
_INDEX_LAST_ERROR = None
return _INDEX_CACHE
async def _create_workplan(
*,
body: WorkplanCreate,
session: AsyncSession,
) -> Workplan:
wp = Workplan(**body.model_dump())
session.add(wp)
await session.commit()
await session.refresh(wp)
return wp
async def _get_workplan(
*,
workplan_id: uuid.UUID,
session: AsyncSession,
) -> Workplan:
wp = await session.get(Workplan, workplan_id)
if wp is None:
raise HTTPException(status_code=404, detail="Workplan not found")
return wp
async def _update_workplan(
*,
workplan_id: uuid.UUID,
body: WorkplanUpdate,
session: AsyncSession,
) -> Workplan:
wp = await session.get(Workplan, workplan_id)
if wp is None:
raise HTTPException(status_code=404, detail="Workplan not found")
update_data = body.model_dump(exclude_unset=True)
status_update = update_data.pop("status", None)
prev_status = wp.status
for field, value in update_data.items():
setattr(wp, field, value)
if status_update is not None:
transition_workplan_status(wp, status_update)
await session.commit()
await session.refresh(wp)
if normalize_workplan_status(prev_status) != "finished" and wp.status == "finished":
await _publish_completion_events(wp, session)
return wp
async def _archive_workplan(
*,
workplan_id: uuid.UUID,
session: AsyncSession,
) -> Workplan:
wp = await session.get(Workplan, workplan_id)
if wp is None:
raise HTTPException(status_code=404, detail="Workplan not found")
transition_workplan_status(wp, "archived")
await session.commit()
await session.refresh(wp)
return wp
async def _publish_completion_events(wp: Workplan, session: AsyncSession) -> None:
workplan_envelope = EventEnvelope.new(
_COMPLETED_WORKPLAN_EVENT,
attributes={
"workplan_id": str(wp.id),
"slug": wp.slug,
"title": wp.title,
"topic_id": str(wp.topic_id) if wp.topic_id else None,
"repo_id": str(wp.repo_id) if wp.repo_id else None,
"repo_goal_id": str(wp.repo_goal_id) if wp.repo_goal_id else None,
},
)
asyncio.create_task(publish_event(_COMPLETED_WORKPLAN_EVENT, workplan_envelope))
@router.get("/", status_code=status.HTTP_410_GONE)
async def list_workstreams(
request: Request,
response: Response,
topic_id: uuid.UUID | None = None,
repo_id: uuid.UUID | None = None,
repo_goal_id: uuid.UUID | None = None,
status: str | None = None,
owner: str | None = None,
slug: str | None = None,
session: AsyncSession = Depends(get_session),
) -> None:
await retire_legacy_route(
session=session,
request=request,
response=response,
interface_key=_legacy_key("GET", "/workstreams/"),
replacement_ref="/workplans/",
detail="Legacy GET /workstreams/ retired; use GET /workplans/",
)
@workplan_router.get("/", response_model=list[WorkplanRead])
async def list_workplans(
topic_id: uuid.UUID | None = None,
repo_id: uuid.UUID | None = None,
repo_goal_id: uuid.UUID | None = None,
status: str | None = None,
owner: str | None = None,
slug: str | None = None,
flavor: str | None = Query(None),
include_residuals: bool = Query(True),
session: AsyncSession = Depends(get_session),
) -> list[Workplan]:
workplans = await _list_workplans(
topic_id=topic_id,
repo_id=repo_id,
repo_goal_id=repo_goal_id,
status_filter=status,
owner=owner,
slug=slug,
flavor=flavor,
include_residuals=include_residuals,
session=session,
)
return list(await annotate_workplans(session, workplans))
@router.get("/workplan-index", status_code=status.HTTP_410_GONE)
async def workplan_index(
request: Request,
response: Response,
refresh: bool = Query(False, description="Force cache invalidation"),
session: AsyncSession = Depends(get_session),
) -> None:
await retire_legacy_route(
session=session,
request=request,
response=response,
interface_key=_legacy_key("GET", "/workstreams/workplan-index"),
replacement_ref="/workplans/index",
detail="Legacy GET /workstreams/workplan-index retired; use GET /workplans/index",
)
@workplan_router.get("/index")
async def workplan_index_preferred(
refresh: bool = Query(False, description="Force cache invalidation"),
session: AsyncSession = Depends(get_session),
) -> dict[str, Any]:
return await _workplan_index(refresh=refresh, session=session)
@workplan_router.put("/index/bindings")
async def sync_workplan_bindings(
body: WorkplanBindingsSync,
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
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
wp.backing_filename = entry.filename
wp.backing_relative_path = entry.relative_path
wp.backing_archived = entry.archived
wp.backing_synced_at = synced_at
updated += 1
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)
async def create_workstream(
request: Request,
response: Response,
body: WorkplanCreate,
session: AsyncSession = Depends(get_session),
) -> None:
await retire_legacy_route(
session=session,
request=request,
response=response,
interface_key=_legacy_key("POST", "/workstreams/"),
replacement_ref="/workplans/",
detail="Legacy POST /workstreams/ retired; use POST /workplans/",
)
@workplan_router.post("/", response_model=WorkplanRead, status_code=status.HTTP_201_CREATED)
async def create_workplan(
body: WorkplanCreate,
session: AsyncSession = Depends(get_session),
) -> Workplan:
return await _create_workplan(body=body, session=session)
@router.get("/{workstream_id}", status_code=status.HTTP_410_GONE)
async def get_workstream(
request: Request,
response: Response,
workstream_id: uuid.UUID,
session: AsyncSession = Depends(get_session),
) -> None:
await retire_legacy_route(
session=session,
request=request,
response=response,
interface_key=_legacy_key("GET", "/workstreams/{workstream_id}"),
replacement_ref="/workplans/{workplan_id}",
detail="Legacy GET /workstreams/{workstream_id} retired; use GET /workplans/{workplan_id}",
)
@workplan_router.get("/{workplan_id}", response_model=WorkplanRead)
async def get_workplan(
workplan_id: uuid.UUID,
session: AsyncSession = Depends(get_session),
) -> Workplan:
wp = await _get_workplan(workplan_id=workplan_id, session=session)
await annotate_workplans(session, [wp])
return wp
@router.patch("/{workstream_id}", status_code=status.HTTP_410_GONE)
async def update_workstream(
request: Request,
response: Response,
workstream_id: uuid.UUID,
body: WorkplanUpdate,
session: AsyncSession = Depends(get_session),
) -> None:
await retire_legacy_route(
session=session,
request=request,
response=response,
interface_key=_legacy_key("PATCH", "/workstreams/{workstream_id}"),
replacement_ref="/workplans/{workplan_id}",
detail="Legacy PATCH /workstreams/{workstream_id} retired; use PATCH /workplans/{workplan_id}",
)
@workplan_router.patch("/{workplan_id}", response_model=WorkplanRead)
async def update_workplan(
workplan_id: uuid.UUID,
body: WorkplanUpdate,
session: AsyncSession = Depends(get_session),
) -> Workplan:
return await _update_workplan(workplan_id=workplan_id, body=body, session=session)
@router.delete("/{workstream_id}", status_code=status.HTTP_410_GONE)
async def archive_workstream(
request: Request,
response: Response,
workstream_id: uuid.UUID,
session: AsyncSession = Depends(get_session),
) -> None:
await retire_legacy_route(
session=session,
request=request,
response=response,
interface_key=_legacy_key("DELETE", "/workstreams/{workstream_id}"),
replacement_ref="DELETE /workplans/{workplan_id}",
detail="Legacy DELETE /workstreams/{workstream_id} retired; use DELETE /workplans/{workplan_id}",
)
@workplan_router.delete("/{workplan_id}", response_model=WorkplanRead)
async def archive_workplan(
workplan_id: uuid.UUID,
session: AsyncSession = Depends(get_session),
) -> Workplan:
return await _archive_workplan(workplan_id=workplan_id, session=session)

View file

@ -0,0 +1,238 @@
"""Exact closed source bindings predating canonical Forge identity.
These are frozen historical source views, not aliases or UUID allocation rules.
An altered/missing source requires explicit owner review; generic source UUIDs
remain untrusted. Shared Activity views represent the one archived parent.
"""
from pathlib import Path
from hashlib import sha256
import json
BINDINGS = json.loads(r'''[
{
"repo": "activity-core",
"path": "workplans/archived/260707-custodian-WP-0003-event-bridge.md",
"sha256": "83ea8bcb151e003419b73c59dbd028f4a1ce3d1120a82e4a128a772503ff3b76",
"record_id": "custodian-WP-0003",
"uuid": "b4eb45a9-69e3-4ab0-b00c-67a53c3117c5",
"parent_record_id": "custodian-WP-0003"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0001-temporal-backbone.md",
"sha256": "020e2eac973f2daa41c1687f8e867da9d521f945547e6aa82c87993b3ff6fc64",
"record_id": "custodian-WP-0001",
"uuid": "e22516cc-4fdf-4dad-a928-a9e04a9fa7c7",
"parent_record_id": "custodian-WP-0001"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0002-triggers-ops.md",
"sha256": "d8cbc61631c01b6f11a03a63f2fb6275fc853ff7f7459ce65973ed1a233daa01",
"record_id": "custodian-WP-0002",
"uuid": "3a4f47d9-8bc1-434e-acb4-bed5d4dacda0",
"parent_record_id": "custodian-WP-0002"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0003a-event-bridge-model-rules-registry.md",
"sha256": "ffa6682e9ba3aaf559bd44203d561c90dea69bcf1b1e778209a7b209e6e5cdd3",
"record_id": "custodian-WP-0003a",
"uuid": "b4eb45a9-69e3-4ab0-b00c-67a53c3117c5",
"parent_record_id": "custodian-WP-0003"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0003b-event-bridge-parser-workflow-triggers.md",
"sha256": "4fc34e0f821e2df51e5c08c7ab34da35b65690381d01f0cfdf55d00c970b414c",
"record_id": "custodian-WP-0003b",
"uuid": "b4eb45a9-69e3-4ab0-b00c-67a53c3117c5",
"parent_record_id": "custodian-WP-0003"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0003c-event-bridge-context-integration.md",
"sha256": "b6cd62b26515e509670ca39736b6bcec9633beedd46c9bd8f7598c06bac7276b",
"record_id": "custodian-WP-0003c",
"uuid": "b4eb45a9-69e3-4ab0-b00c-67a53c3117c5",
"parent_record_id": "custodian-WP-0003"
},
{
"repo": "activity-core",
"path": "workplans/custodian-WP-0004-railiance-ops.md",
"sha256": "20df02eea740e109c937130e7f8801a636fc0990ed8c6d2ced55659f8957adb1",
"record_id": "custodian-WP-0004",
"uuid": "759b1255-aa78-42b5-8fab-c5fcf168f5f4",
"parent_record_id": "custodian-WP-0004"
},
{
"repo": "the-custodian",
"path": "workplans/ADHOC-2026-08-25-retired.md",
"sha256": "344a1293dbb3a67a944eb34ca4a0337487d8f047226027a2107922346fbab2dd",
"record_id": "CUST-WP-ADHOC-2026-08-25-RETIRED",
"uuid": "1c53d3db-6633-5e2d-987f-10706d73be9e",
"parent_record_id": "CUST-WP-ADHOC-2026-08-25-RETIRED"
},
{
"repo": "railiance-cluster",
"path": "workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md",
"sha256": "8d6f8533c119117686bc0d5bb9ed5349726aa6c3189ff98b53409c68ac685ca9",
"record_id": "RAIL-BS-WP-0002",
"uuid": "4c63dfc6-9eac-4e79-9f77-8f644ad7147d",
"parent_record_id": "RAIL-BS-WP-0002",
"tasks": [
{
"label": "T01",
"uuid": "3f042630-eab0-4c6a-9167-e2b28ff20e40",
"status": "done"
},
{
"label": "T02",
"uuid": "e8510646-46ed-4697-a345-f3d3009eea78",
"status": "done"
},
{
"label": "T03",
"uuid": "dab2c07f-8aa0-4635-8df6-857e87e93fc5",
"status": "done"
},
{
"label": "T04",
"uuid": "5c3d40e4-239b-488e-9519-6f7a38d2325f",
"status": "done"
},
{
"label": "T05",
"uuid": "7f9e0e58-a130-467a-a2d0-b3f2564e496f",
"status": "done"
}
]
},
{
"repo": "railiance-cluster",
"path": "workplans/archived/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md",
"sha256": "4dc2f61e15adc3c5c81f9ecf8b933512275b95e8f829fac52ae7cde1385cea4a",
"record_id": "RAIL-BS-WP-0003",
"uuid": "7ee9ee22-1fae-4567-9194-8d70a9e0f45b",
"parent_record_id": "RAIL-BS-WP-0003",
"tasks": [
{
"label": "T01",
"uuid": "6841c93a-f146-47eb-9f7c-8fa0e02c1bbc",
"status": "done"
},
{
"label": "T02",
"uuid": "ab166073-30a7-4702-a037-4091e8706e20",
"status": "done"
},
{
"label": "T03",
"uuid": "140da396-8e30-4f4d-b88c-c42c0cd46c01",
"status": "done"
},
{
"label": "T04",
"uuid": "d8a3ba40-fda0-4c1f-a9f1-ffcd621a5b3d",
"status": "done"
}
]
},
{
"repo": "railiance-cluster",
"path": "workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md",
"sha256": "04f787078232395bd2d43861de16c521848559fa286f06e1caa9cb779aa71ac3",
"record_id": "RAIL-BS-WP-0004",
"uuid": "7e8b0c20-51eb-40c9-9e3b-85dd380d7625",
"parent_record_id": "RAIL-BS-WP-0004",
"tasks": [
{
"label": "T01",
"uuid": "4526a842-ea31-4874-9231-92ab556cfe7b",
"status": "done"
},
{
"label": "T02",
"uuid": "a6313e06-1976-46a7-8e31-df4eb2eca880",
"status": "done"
},
{
"label": "T03",
"uuid": "05d42a55-921f-4aa7-bb76-e8af9c7e0ac3",
"status": "done"
},
{
"label": "T04",
"uuid": "08233868-d522-4117-bc4e-6c0f52545665",
"status": "done"
},
{
"label": "T05",
"uuid": "2d5acff7-4a4e-4ddd-ad06-08237ad3dac8",
"status": "done"
},
{
"label": "T06",
"uuid": "f8e4a094-c367-40eb-b895-da17bc144b07",
"status": "done"
}
]
}
]''')
def bound_sources(repo_root: Path, repo_slug: str):
entries = [entry for entry in BINDINGS if entry["repo"] == repo_slug]
result = []
for entry in entries:
path = repo_root / entry["path"]
if path.is_symlink() or not path.is_file():
raise ValueError("Historical source binding missing or symlinked: " + entry["path"])
data = path.read_bytes()
if sha256(data).hexdigest() != entry["sha256"]:
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")

View file

@ -0,0 +1,6 @@
apiVersion: v2
name: state-hub
description: State Hub API service for private Railiance operation
type: application
version: 0.1.0
appVersion: "main-51bc70d"

View file

@ -0,0 +1,127 @@
# GENERATED — do not edit.
# Synced from the-custodian/canon/standards/repo-classification.allowed.yaml
# by scripts/sync_classification_allowed.py (CUST-WP-0067-T09).
# Canon is authoritative; this copy exists only so the container has one.
# Machine-readable allowed-values for the Repo Classification Standard.
#
# Single source of truth for the standard's controlled vocabularies, derived
# from canon/standards/repo-classification-standard_v1.0.md. Consumed by:
# - the per-repo .repo-classification.yaml linter (tools/validate_repo_classification.py)
# - the State Hub registration validator (CUST-WP-0050 T04)
#
# When the standard's vocabularies change, update this file and bump `version`
# to match the standard version. CUST-WP-0050 T01.
standard: "Repo Classification Standard"
version: "1.1"
canon_id: "canon-repo-classification"
# category — exactly 1 required (§5)
categories:
- experimental
- research
- project
- tooling
- product
- business
- publication
# domain / secondary_domains — primary exactly 1; secondaries 0..n (§6)
domains:
- infotech
- financials
- communication
- consumer
- health
- industrials
- energy
- utilities
- materials
- realestate
- crypto
- agents
- space
- government
# business_stake — 0..n; 2..6 recommended (§8)
business_stake:
- execution
- intelligence
- finance
- legal
- sales
- experience
- technology
- operations
- product
- people
- procurement
- sustainability
- automation
# business_mechanics — 0..n, optional (§9)
business_mechanics:
- intention
- control
- coordination
- operation
- adaptation
# capability_tags are intentionally OPEN-ENDED (§7): lowercase kebab-case, not
# restricted to this set. The families below are the standard's recommended
# canonical tags — used to warn on likely synonyms/typos, never to reject.
capability_families:
identity_and_access:
- identity
- authentication
- authorization
- access-control
- user-management
- tenancy
knowledge_and_evidence:
- knowledge
- citations
- evidence
- source-management
- traceability
- documentation
- decision-support
- history
- participation
platform_and_operations:
- platform
- deployment
- operations
- observability
- feature-control
- configuration
- orchestration
market_and_coordination:
- marketplace
- pricing
- monetization
- reputation
- challenges
- bounties
- collaboration
- coordination
product_and_lifecycle:
- product-development
- lifecycle
governance_and_control:
- governance
- policy
- compliance
- risk
- audit
- control
games_and_entertainment:
- game
- entertainment
# Validation guidance (advisory bounds the linter applies as warnings)
guidance:
secondary_domains_max: 3
business_stake_recommended_min: 2
business_stake_recommended_max: 6
capability_tag_pattern: "^[a-z0-9]+(-[a-z0-9]+)*$"

View file

@ -0,0 +1,69 @@
# Production values for the State Hub Railiance chart handoff.
# Non-secret values only. DATABASE_URL comes from the Secret `state-hub-env`.
namespace:
create: false
image:
repository: forgejo.coulomb.social/coulomb/state-hub
tag: "main-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015"
ingress:
enabled: false
# MCP layer on central (CUST-WP-0067-T08). Enabled together with the image tag
# above: MCP_HOST landed in main-6c1262e, and enabling this on an older image
# would deploy a pod that binds loopback and never becomes reachable.
# ClusterIP only — no ingress, same reason the API's is disabled.
mcp:
enabled: true
resources:
requests:
# 2026-09-10: 1,078 observed 5-minute samples, p99 2.35m, peak 30.45m.
# Preserve the 500m limit for bursts; free 40m for the API rollout surge.
cpu: 10m
config:
# This deployment is the authoritative hub. Callers verify this rather than
# trusting that whatever answered on a port is central (CUST-WP-0067-T03).
instanceRole: primary
instanceLabel: railiance01
sbomNexusUrl: "http://sbom-nexus.sbom-nexus.svc.cluster.local:8010"
# Reversible T04 read cutover; set back to `legacy` to roll back.
sbomNexusReadMode: nexus
# Reversible T04 write cutover; set back to `legacy` to roll back future writes.
sbomNexusWriteMode: nexus
resources:
# Restored to the chart's 250m on 2026-09-21. The earlier 100m was a workaround
# for a node with <250m unallocated; ~830m of Knative/Kourier requests were
# released that day (the-custodian docs/kubernetes-change-gate-decision.md).
# State Hub idles near 5m but bursts past 400m under concurrent agent load.
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 1000m
memory: 2Gi
sweep:
# RMGR-WP-0005-T11: disabled while railiance01 checkouts still target the
# stale gitea-remote lineage. Re-enable only after the governed remote
# reconciliation and registrar preflight are complete.
enabled: false
# Present in the live release but previously missing here, so deploying from
# this file alone silently dropped it. Inert while enabled is false.
hostname: 239.62.205.92.host.secureserver.net
hostPath: /home/tegwick
sshHostPath: /home/tegwick/.ssh
# Forge read credential (STATE-WP-0084-T02). Coordinates only — the token lives
# in OpenBao at platform/workloads/state-hub/forge-derivation and reaches the
# pod through Kubernetes auth, never through this file or a Secret.
forgeRead:
enabled: true
# RPF-WP-0035-T04: dedicated ESO signing key, API-only explicit reference.
renamePreflight:
enabled: true
secretName: state-hub-rename-preflight

View file

@ -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"
]
}
}

View file

@ -0,0 +1,91 @@
#!/usr/bin/env python3
"""Sync the canon classification allowed-values into the chart (CUST-WP-0067-T09).
The State Hub validates repository classification against
``the-custodian/canon/standards/repo-classification.allowed.yaml``. That file is
canon and must stay authoritative, but a container has no the-custodian
checkout, so a copy has to travel with the release.
A copy that nobody checks is a copy that silently drifts. This script owns the
copy: ``--check`` fails when it diverges from canon, so the release can refuse
to ship a stale vocabulary rather than validate against yesterday's rules.
Usage:
python scripts/sync_classification_allowed.py # write the copy
python scripts/sync_classification_allowed.py --check # verify, exit 1 on drift
"""
from __future__ import annotations
import argparse
import difflib
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
CHART_COPY = (
REPO_ROOT
/ "deploy/railiance/apps/charts/state-hub/files/repo-classification.allowed.yaml"
)
CANON_CANDIDATES = (
Path("/home/worsch/the-custodian/canon/standards/repo-classification.allowed.yaml"),
Path("/home/tegwick/the-custodian/canon/standards/repo-classification.allowed.yaml"),
REPO_ROOT.parent / "the-custodian/canon/standards/repo-classification.allowed.yaml",
)
HEADER = (
"# GENERATED — do not edit.\n"
"# Synced from the-custodian/canon/standards/repo-classification.allowed.yaml\n"
"# by scripts/sync_classification_allowed.py (CUST-WP-0067-T09).\n"
"# Canon is authoritative; this copy exists only so the container has one.\n"
)
def find_canon() -> Path:
for candidate in CANON_CANDIDATES:
if candidate.is_file():
return candidate
raise SystemExit(
"ERROR: canon allowed-values not found. Looked in:\n "
+ "\n ".join(str(c) for c in CANON_CANDIDATES)
)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--check",
action="store_true",
help="Verify the chart copy matches canon; exit 1 on drift",
)
args = parser.parse_args()
canon = find_canon()
desired = HEADER + canon.read_text(encoding="utf-8")
if args.check:
if not CHART_COPY.is_file():
print(f"DRIFT: {CHART_COPY} is missing; run without --check to create it")
return 1
current = CHART_COPY.read_text(encoding="utf-8")
if current != desired:
print(f"DRIFT: chart copy differs from canon ({canon})")
sys.stdout.writelines(
difflib.unified_diff(
current.splitlines(keepends=True),
desired.splitlines(keepends=True),
fromfile="chart copy",
tofile="canon",
)
)
return 1
print(f"OK: chart copy matches canon ({canon})")
return 0
CHART_COPY.parent.mkdir(parents=True, exist_ok=True)
CHART_COPY.write_text(desired, encoding="utf-8")
print(f"Synced {canon} -> {CHART_COPY}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,200 @@
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
@pytest.mark.parametrize('bound',h.FROZEN_CLUSTER_BINDINGS)
@pytest.mark.parametrize('key',['to_workstream_id','to_workplan_id','to_task_id'])
def test_ordinary_dependency_to_history_remains_allowed(bound,key):
report=actual_report();ordinary=str(uuid.uuid4())
target=bound['tasks'][0]['uuid'] if key=='to_task_id' else bound['uuid']
report.add(severity='WARN',check_id='C-20',message='ordinary inbound edge',fixable=True,db_id=ordinary,_fix_context={'from_workstream_id':ordinary,key:target,'relationship_type':'blocks'})
guard=extracted('scripts/consistency_check.py','_refuse_frozen_fix_batch',dict(FROZEN_CLUSTER_BINDINGS=h.FROZEN_CLUSTER_BINDINGS))
assert not guard(report,'railiance-cluster') and not report.automation_errors
@pytest.mark.parametrize('bound',h.FROZEN_CLUSTER_BINDINGS)
@pytest.mark.parametrize('key',['from_workstream_id','from_workplan_id','from_task_id','state_hub_workstream_id','state_hub_task_id','desired_record_id'])
def test_protected_dependency_origin_and_nested_binding_refused(bound,key):
report=actual_report();origin=bound['tasks'][0]['uuid'] if 'task' in key else bound['uuid']
context={key:origin,'to_workstream_id':str(uuid.uuid4())}
if key.startswith('state_hub'):context={'meta':context}
report.add(severity='WARN',check_id='C-20',message='protected origin',fixable=True,_fix_context=context)
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

View file

@ -0,0 +1,339 @@
---
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: 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.

View file

@ -0,0 +1,60 @@
"""Real disposable Git proof; does not import owner app or test conftest."""
import ast
from pathlib import Path
import subprocess
import sys
import pytest
SOURCE=Path(__file__).resolve().parents[1]/'scripts/consistency_check.py'
def actual_writeback():
tree=ast.parse(SOURCE.read_text());nodes=[]
for node in tree.body:
if isinstance(node,ast.FunctionDef) and node.name in {'_git_identity_config','_git_commit_writeback'}:
node.returns=None
for arg in node.args.args+node.args.kwonlyargs:arg.annotation=None
nodes.append(node)
env=dict(subprocess=subprocess,sys=sys,Path=Path)
exec(compile(ast.Module(body=nodes,type_ignores=[]),str(SOURCE),'exec'),env)
return env['_git_commit_writeback']
def git(root,*args):return subprocess.check_output(['git','-C',str(root),*args]).decode().strip()
@pytest.fixture
def repo(tmp_path):
root=tmp_path/'owner';root.mkdir();git(root,'init','-b','main');git(root,'config','user.name','Synthetic Test');git(root,'config','user.email','synthetic@example.invalid');git(root,'config','core.hooksPath','.git/hooks')
for name in ['writeback.md','.custodian-brief.md','evidence.json']:(root/name).write_text('original\n')
git(root,'add','.');git(root,'commit','-m','baseline')
(root/'.custodian-brief.md').write_text('staged unrelated\n');git(root,'add','.custodian-brief.md');(root/'.custodian-brief.md').write_text('working unrelated\n')
(root/'evidence.json').write_text('dirty evidence\n')
return root
def test_writeback_commits_only_target_preserves_unrelated_stage_and_worktree(repo):
before_index=git(repo,'ls-files','--stage','.custodian-brief.md','evidence.json');before_head=git(repo,'rev-parse','HEAD')
target=repo/'writeback.md';target.write_text('new status\n')
assert actual_writeback()(str(repo),target,['synthetic status'])
assert git(repo,'rev-parse','HEAD')!=before_head
assert git(repo,'diff-tree','--no-commit-id','--name-only','-r','HEAD')=='writeback.md'
assert git(repo,'show','HEAD:.custodian-brief.md')=='original'
assert git(repo,'show',':.custodian-brief.md')=='staged unrelated'
assert git(repo,'ls-files','--stage','.custodian-brief.md','evidence.json')==before_index
assert (repo/'.custodian-brief.md').read_text()=='working unrelated\n'
assert (repo/'evidence.json').read_text()=='dirty evidence\n'
def test_noop_never_commits_unrelated_staged_change(repo):
head=git(repo,'rev-parse','HEAD');index=git(repo,'ls-files','--stage')
assert not actual_writeback()(str(repo),repo/'writeback.md',['no change'])
assert git(repo,'rev-parse','HEAD')==head and git(repo,'ls-files','--stage')==index
@pytest.mark.parametrize('name',['new writeback.md','-leading-dash.md'])
def test_new_target_and_argument_boundary(repo,name):
target=repo/name;target.write_text('new file\n')
assert actual_writeback()(str(repo),target,['new synthetic target'])
assert git(repo,'diff-tree','--no-commit-id','--name-only','-r','HEAD')==name
assert git(repo,'show',':.custodian-brief.md')=='staged unrelated'
def test_normal_hook_refusal_preserves_unrelated_state(repo):
hook=repo/'.git/hooks/pre-commit';hook.write_text('#!/bin/sh\nexit 1\n');hook.chmod(0o755)
head=git(repo,'rev-parse','HEAD');index=git(repo,'ls-files','--stage','.custodian-brief.md','evidence.json')
target=repo/'writeback.md';target.write_text('new status\n')
assert not actual_writeback()(str(repo),target,['refused synthetic change'])
assert git(repo,'rev-parse','HEAD')==head
assert git(repo,'ls-files','--stage','.custodian-brief.md','evidence.json')==index
assert git(repo,'show',':.custodian-brief.md')=='staged unrelated'

View file

@ -0,0 +1,359 @@
---
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.
T09 source repair refinement, 2026-10-17: the attempted current-client regression
actually loaded an older packaged consistency checker from the owner virtualenv,
not the reviewed canonical source. That trial again failed parent-row hash
preservation; it is retained separately and does not count as R5 client proof.
Normal C-23 also legitimately normalized WP18 ready to active and created a
source commit, but the generic writeback commit included an unrelated staged
brief. A narrow successor scopes git add and git commit --only to the actual
writeback file, preserving unrelated index/worktree state. Real disposable Git
regression tests cover pre-existing staged changes, hooks, no-op and path names.
Verify the actually loaded CLI checker hash after ordinary owner package refresh
before any retry; capture legitimate source HEAD transitions rather than assume
a static revision. T07 and T10 remain wait. No original parent preimage recovery,
new runtime delivery or broader authority follows from this source repair.

View file

@ -0,0 +1,383 @@
---
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-WP-0095-T10]
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.
T09 source repair refinement, 2026-10-17: the attempted current-client regression
actually loaded an older packaged consistency checker from the owner virtualenv,
not the reviewed canonical source. That trial again failed parent-row hash
preservation; it is retained separately and does not count as R5 client proof.
Normal C-23 also legitimately normalized WP18 ready to active and created a
source commit, but the generic writeback commit included an unrelated staged
brief. A narrow successor scopes git add and git commit --only to the actual
writeback file, preserving unrelated index/worktree state. Real disposable Git
regression tests cover pre-existing staged changes, hooks, no-op and path names.
Verify the actually loaded CLI checker hash after ordinary owner package refresh
before any retry; capture legitimate source HEAD transitions rather than assume
a static revision. T07 and T10 remain wait. No original parent preimage recovery,
new runtime delivery or broader authority follows from this source repair.
T07 current-baseline client return, 2026-10-17: after the owner refreshed the
installed package and verified the actually loaded checker matched the reviewed
R6 source, ordinary Cluster deep consistency and fast projection passed.
Bindings synced 20/20, omitting the three frozen histories; all three parent
and 15 task CURRENT full-row hashes remained unchanged across
before-verified-client.json / after-verified-client.json. Unrelated staged brief
state was preserved. This proves the corrected installed client stage against
its fresh current baseline; earlier parent-hash failures remain retained
historical exceptions, not restored preimages or backfilled successes.
T10 remains wait/needs_human for exact proposal
STATE-WP-0095-T10-frozen-bindings-guard-r1 revision 1, metadata
sha256:81d365926fc585a186955d8c98ee83501fd3cec12135b2256d71b46a75097932,
pending decision df703266-6719-4c04-ae1b-ba9afd3bca1c. The inspected candidate
image is main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1,
from runtime source c103cbf3c8bed5dba87a03e320595ddded9301a3. Image-only
server dry-run passed eight identities with only three image-field changes.
The exact proposal and recovery limits are retained in the Custodian dated
2026-10-17 guard-successor-review-r1 packet. No new API runtime is approved or
delivered by this source-only note; its later workplan commit does not change
the image's source pin. T09 stays done, T07/T10 stay wait pending reviewed API
delivery and explicit successor/current-baseline acceptance disposition.

View file

@ -0,0 +1,79 @@
{
"schema": "state95.proposal-revision-close.v1",
"status": "applied",
"source_revision": "4c03c9d2c6a47f5c7247473f74b2e2f188b787bd",
"remote_revision": "4c03c9d2c6a47f5c7247473f74b2e2f188b787bd",
"instance_role": "primary",
"instance_label": "railiance01",
"expected_commit": "4c03c9d2c6a47f5c7247473f74b2e2f188b787bd",
"derived_commit": "4c03c9d2c6a47f5c7247473f74b2e2f188b787bd",
"tasks": [
{
"uuid": "6a5f87bb-e63d-5e20-b9fc-c1b5cd403ff7",
"record_id": "STATE-WP-0095-T01",
"status": "done",
"needs_human": false
},
{
"uuid": "ebb24194-e4cc-5130-aaa6-663f2ea2c822",
"record_id": "STATE-WP-0095-T02",
"status": "done",
"needs_human": false
},
{
"uuid": "e8566341-cf6e-5b9c-ad80-8564d14cd52a",
"record_id": "STATE-WP-0095-T03",
"status": "done",
"needs_human": false
},
{
"uuid": "3bbb311f-194e-5862-8aed-358a580f22b4",
"record_id": "STATE-WP-0095-T04",
"status": "done",
"needs_human": false
},
{
"uuid": "339b9a37-181c-5599-9a89-0393e199bd4b",
"record_id": "STATE-WP-0095-T05",
"status": "done",
"needs_human": false
},
{
"uuid": "6af973b4-1964-5d4d-a2f8-8dbd4c01fda4",
"record_id": "STATE-WP-0095-T06",
"status": "done",
"needs_human": false
},
{
"uuid": "cfd1ea99-e75b-542e-a434-acefb62f3c7d",
"record_id": "STATE-WP-0095-T07",
"status": "wait",
"needs_human": false
},
{
"uuid": "2dd9b04f-2250-5a8a-b5bc-c8a009872858",
"record_id": "STATE-WP-0095-T08",
"status": "done",
"needs_human": false
},
{
"uuid": "8be8f13b-6352-5295-9755-de6fbfb13593",
"record_id": "STATE-WP-0095-T09",
"status": "done",
"needs_human": false
},
{
"uuid": "88585c69-d9b9-5e1f-a256-6416ff71e824",
"record_id": "STATE-WP-0095-T10",
"status": "wait",
"needs_human": true
}
],
"whole_unrelated_index_dirty_preserved": true,
"proposal_revision": 2,
"proposal_status": "pending_unapproved_unexecuted",
"original_raw_receipt": "docs/changes/netkingdom-target-2026-10-16/cluster-historical-publication-r1/owner-followthrough-r8/source-commit-result.json",
"original_raw_receipt_sha256": "sha256:3291234c856b2dfd4a84f054475d0372bc7cbf7bdee37b6c08a6d327f46ab322",
"runtime_changed": false,
"new_UUIDs": false
}

View file

@ -0,0 +1,187 @@
{
"repo_slug": "state-hub",
"repo_path": "/home/worsch/state-hub",
"source_revision": "4c03c9d2c6a47f5c7247473f74b2e2f188b787bd",
"source_revision_reason": null,
"issues": [
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager warning: workplans/: repository uses more than one workplan prefix: ['CUST-WP', 'STATE-WP']",
"file_path": "workplans/",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-33",
"message": "WORK-RECORDS.md is missing or stale \u2014 regenerate via fix-consistency (CUST-WP-0061-T04 generated work-record index)",
"file_path": "WORK-RECORDS.md",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": true
},
{
"severity": "WARN",
"check_id": "C-20",
"message": "Workplan dependency target 'ACTIVITY-WP-0026' is not linked to State Hub",
"file_path": "workplans/STATE-WP-0078-ops-run-read-projection.md",
"db_id": "",
"file_value": "ACTIVITY-WP-0026",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-39",
"message": "Wait task 'STATE-WP-0094-T03' in workplan 'STATE-WP-0094' has neither depends_on, needs_human: true, blocked_on: message-from:\u2026 nor wait_until \u2014 unqualified wait",
"file_path": "workplans/STATE-WP-0094-routed-gates-and-owner-returns.md#STATE-WP-0094-T03",
"db_id": "",
"file_value": "wait",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-39",
"message": "Human wait task 'STATE-WP-0095-T10' in workplan 'STATE-WP-0095' has needs_human: true without decision_id \u2014 unrouted human gate",
"file_path": "workplans/STATE-WP-0095-historical-source-receiving.md#STATE-WP-0095-T10",
"db_id": "",
"file_value": "needs_human: true",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-40",
"message": "blocker satisfied, task still wait: 'STATE-WP-0095-T10' in workplan 'STATE-WP-0095' \u2014 every depends_on target is terminal (STATE-WP-0095-T09)",
"file_path": "workplans/STATE-WP-0095-historical-source-receiving.md#STATE-WP-0095-T10",
"db_id": "",
"file_value": "STATE-WP-0095-T09",
"db_value": "",
"fixable": false
},
{
"severity": "INFO",
"check_id": "C-08",
"message": "Retired DB workstream 'adhoc-2026-07-01@retired-20260827' (id=1cdb288a\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
"file_path": "",
"db_id": "1cdb288a-9bcb-4e53-8c4f-cb43e3abe9c5",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "INFO",
"check_id": "C-08",
"message": "Retired DB workstream 'adhoc-2026-06-04@retired-20260827' (id=2a8f3aff\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
"file_path": "",
"db_id": "2a8f3aff-8f5d-4e42-b33f-225d60e0b30b",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-25",
"message": "Owner-return task 'STATE-WP-0094-T05' waiting on railiance-platform has 1 reply/replies after its last dated note (or unread when undated) \u2014 owner action needed: 639c6969-42ce-4080-b2f8-3eb3bb18c970",
"file_path": "workplans/STATE-WP-0094-routed-gates-and-owner-returns.md#STATE-WP-0094-T05",
"db_id": "",
"file_value": "message-from:railiance-platform",
"db_value": "",
"fixable": false,
"repo_slug": "state-hub",
"source_revision": "4c03c9d2c6a47f5c7247473f74b2e2f188b787bd",
"source_revision_reason": null,
"owner_return": {
"task_record_id": "STATE-WP-0094-T05",
"message_ids": [
"639c6969-42ce-4080-b2f8-3eb3bb18c970"
]
}
},
{
"severity": "WARN",
"check_id": "C-28",
"message": "2 unread inbox message(s) older than 3 day(s): the-custodian:23172cb2, the-custodian:51e9eace",
"file_path": "",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-28",
"message": "2 unread message(s) lack thread_id for supersession tracking: the-custodian:23172cb2, the-custodian:51e9eace",
"file_path": "",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-26",
"message": "Workplan id 'CUST-WP-0003' uses prefix 'CUST-WP' but repo canonical prefix is 'STATE-WP' \u2014 new plans must conform",
"file_path": "workplans/CUST-WP-0003-whi-kpi-card.md",
"db_id": "",
"file_value": "CUST-WP-0003",
"db_value": "STATE-WP",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-26",
"message": "Workplan id 'CUST-WP-0012' uses prefix 'CUST-WP' but repo canonical prefix is 'STATE-WP' \u2014 new plans must conform",
"file_path": "workplans/CUST-WP-0012-multi-user-onboarding.md",
"db_id": "",
"file_value": "CUST-WP-0012",
"db_value": "STATE-WP",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-26",
"message": "Workplan id 'CUST-WP-0038' uses prefix 'CUST-WP' but repo canonical prefix is 'STATE-WP' \u2014 new plans must conform",
"file_path": "workplans/CUST-WP-0038-state-hub-threephoenix-ha.md",
"db_id": "",
"file_value": "CUST-WP-0038",
"db_value": "STATE-WP",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-26",
"message": "Workplan id 'CUST-WP-0042' uses prefix 'CUST-WP' but repo canonical prefix is 'STATE-WP' \u2014 new plans must conform",
"file_path": "workplans/CUST-WP-0042-workplan-state-model-cleanup.md",
"db_id": "",
"file_value": "CUST-WP-0042",
"db_value": "STATE-WP",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-26",
"message": "Workplan id 'CUST-WP-0011' uses prefix 'CUST-WP' but repo canonical prefix is 'STATE-WP' \u2014 new plans must conform",
"file_path": "workplans/archived/260706-CUST-WP-0011-state-hub-threephoenix-migration.md",
"db_id": "",
"file_value": "CUST-WP-0011",
"db_value": "STATE-WP",
"fixable": false
}
],
"fixes_applied": [
"bindings: synced 70/70"
],
"summary": {
"fail": 0,
"automation_error": 0,
"warn": 14,
"info": 2
},
"result": "warn"
}

View file

@ -0,0 +1,394 @@
---
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-WP-0095-T10]
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.
T09 source repair refinement, 2026-10-17: the attempted current-client regression
actually loaded an older packaged consistency checker from the owner virtualenv,
not the reviewed canonical source. That trial again failed parent-row hash
preservation; it is retained separately and does not count as R5 client proof.
Normal C-23 also legitimately normalized WP18 ready to active and created a
source commit, but the generic writeback commit included an unrelated staged
brief. A narrow successor scopes git add and git commit --only to the actual
writeback file, preserving unrelated index/worktree state. Real disposable Git
regression tests cover pre-existing staged changes, hooks, no-op and path names.
Verify the actually loaded CLI checker hash after ordinary owner package refresh
before any retry; capture legitimate source HEAD transitions rather than assume
a static revision. T07 and T10 remain wait. No original parent preimage recovery,
new runtime delivery or broader authority follows from this source repair.
T07 current-baseline client return, 2026-10-17: after the owner refreshed the
installed package and verified the actually loaded checker matched the reviewed
R6 source, ordinary Cluster deep consistency and fast projection passed.
Bindings synced 20/20, omitting the three frozen histories; all three parent
and 15 task CURRENT full-row hashes remained unchanged across
before-verified-client.json / after-verified-client.json. Unrelated staged brief
state was preserved. This proves the corrected installed client stage against
its fresh current baseline; earlier parent-hash failures remain retained
historical exceptions, not restored preimages or backfilled successes.
T10 remains wait/needs_human for exact proposal
STATE-WP-0095-T10-frozen-bindings-guard-r1, now revision 2, metadata
sha256:5d0ddebc21bcb035809cc8b57f4c2e1ba8ed09204be5b965460e178a90913f1e,
pending decision df703266-6719-4c04-ae1b-ba9afd3bca1c. The inspected candidate
image is main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1,
from runtime source c103cbf3c8bed5dba87a03e320595ddded9301a3. Image-only
server dry-run passed eight identities with only three image-field changes.
The exact proposal and recovery limits are retained in the Custodian dated
2026-10-17 guard-successor-review-r2 packet. No new API runtime is approved or
delivered by this source-only note; its later workplan commit does not change
the image's source pin. T09 stays done, T07/T10 stay wait pending reviewed API
delivery and explicit successor/current-baseline acceptance disposition.
T10 proposal revision history: revision 1 metadata
sha256:81d365926fc585a186955d8c98ee83501fd3cec12135b2256d71b46a75097932
and its original helper remain immutable local evidence, unapproved and
unexecuted. Revision 2 of the same stable proposal changes only the native
probe's EXPECTED hash metadata into module/sha256 records for normal publication
scanning; probe behavior, exact image, recovery and acceptance scope remain
unchanged. The same decision df703266-6719-4c04-ae1b-ba9afd3bca1c remains open
and pending. This source-only amendment grants no runtime authority or new
image/code change; T07/T10 remain wait and T09 remains done.

View file

@ -0,0 +1,34 @@
{
"schema": "state95.conditional-verified-closure.v1",
"status": "preparation_only_not_reviewed_for_publication",
"base_revision": "4c03c9d2c6a47f5c7247473f74b2e2f188b787bd",
"approval": {
"proposal_id": "STATE-WP-0095-T10-frozen-bindings-guard-r1",
"revision": 2,
"sha256": "5d0ddebc21bcb035809cc8b57f4c2e1ba8ed09204be5b965460e178a90913f1e",
"disposition": "founder_approved_reported_by_root_execution_owned_by_root"
},
"T10_done_requires": [
"Exact approved new image/delivered guard hashes and expected schema/primary/API+MCP Ready",
"Eight exact native binding probe results with enforced rollback and fresh transaction restoration; no committed synthetic effects",
"Exact owned trial cleanup/quiescence or explicit verified no recovery needed; no undisclosed recovery condition"
],
"T07_done_requires": [
"Post-delivery actually loaded reviewed checker metadata verified before normal Cluster consistency/fast projection",
"Fresh CURRENT full3parent15task row hashes before/after ordinary pipeline equal",
"All15 original task identities/row hashes and all3 archive byte digests unchanged; no new/moved/renamed historical records",
"Actual receiving/Cluster owner return for RMGR19T06 verified, not source-only status assumption",
"Retained original three-parent hash mismatches accepted only as approved historical exception, never restored/backfilled"
],
"owner_source_close": [
"All10 tasks completed only after above evidence",
"Review own remaining scope; real residual work requires live record before finished; no invented acceptance",
"After root reviewed final source patch, commit only approved paths with normal hooks preserving original staged brief/dirty WORK/weekly evidence",
"Required normal owner rmgr sync --push applied primary/railiance01 exact HEAD=derived, remote and10UUID/status/human/edges readback"
],
"runtime_source_pin": "c103cbf3c8bed5dba87a03e320595ddded9301a3",
"original_frozen_chart_unchanged": true,
"candidate_live_action": false,
"final_evidence_prose": "pending_root_exact_delivery_receipt_no_placeholder_may_be_published",
"source_pin_overlay": "separate reviewable proposal because Make/values still select previous image; not applied"
}

View file

@ -0,0 +1,8 @@
--- 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-51bc70d"
+appVersion: "main-c103cbf"

View file

@ -0,0 +1,22 @@
--- 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-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015
+RAILIANCE_STATE_HUB_IMAGE_TAG ?= main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1
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/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-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015"
+ tag: "main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1"
ingress:
enabled: false

View file

@ -0,0 +1,394 @@
---
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: done
priority: high
depends_on: [STATE-WP-0095-T06, RMGR-WP-0019-T06, STATE-WP-0095-T10]
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: done
priority: high
depends_on: [STATE-WP-0095-T09]
needs_human: false
intervention_note: "Exact revision 2 approved and verified; no pending rollout intervention."
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.
T09 source repair refinement, 2026-10-17: the attempted current-client regression
actually loaded an older packaged consistency checker from the owner virtualenv,
not the reviewed canonical source. That trial again failed parent-row hash
preservation; it is retained separately and does not count as R5 client proof.
Normal C-23 also legitimately normalized WP18 ready to active and created a
source commit, but the generic writeback commit included an unrelated staged
brief. A narrow successor scopes git add and git commit --only to the actual
writeback file, preserving unrelated index/worktree state. Real disposable Git
regression tests cover pre-existing staged changes, hooks, no-op and path names.
Verify the actually loaded CLI checker hash after ordinary owner package refresh
before any retry; capture legitimate source HEAD transitions rather than assume
a static revision. T07 and T10 remain wait. No original parent preimage recovery,
new runtime delivery or broader authority follows from this source repair.
T07 current-baseline client return, 2026-10-17: after the owner refreshed the
installed package and verified the actually loaded checker matched the reviewed
R6 source, ordinary Cluster deep consistency and fast projection passed.
Bindings synced 20/20, omitting the three frozen histories; all three parent
and 15 task CURRENT full-row hashes remained unchanged across
before-verified-client.json / after-verified-client.json. Unrelated staged brief
state was preserved. This proves the corrected installed client stage against
its fresh current baseline; earlier parent-hash failures remain retained
historical exceptions, not restored preimages or backfilled successes.
T10 remains wait/needs_human for exact proposal
STATE-WP-0095-T10-frozen-bindings-guard-r1, now revision 2, metadata
sha256:5d0ddebc21bcb035809cc8b57f4c2e1ba8ed09204be5b965460e178a90913f1e,
pending decision df703266-6719-4c04-ae1b-ba9afd3bca1c. The inspected candidate
image is main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1,
from runtime source c103cbf3c8bed5dba87a03e320595ddded9301a3. Image-only
server dry-run passed eight identities with only three image-field changes.
The exact proposal and recovery limits are retained in the Custodian dated
2026-10-17 guard-successor-review-r2 packet. No new API runtime is approved or
delivered by this source-only note; its later workplan commit does not change
the image's source pin. T09 stays done, T07/T10 stay wait pending reviewed API
delivery and explicit successor/current-baseline acceptance disposition.
T10 proposal revision history: revision 1 metadata
sha256:81d365926fc585a186955d8c98ee83501fd3cec12135b2256d71b46a75097932
and its original helper remain immutable local evidence, unapproved and
unexecuted. Revision 2 of the same stable proposal changes only the native
probe's EXPECTED hash metadata into module/sha256 records for normal publication
scanning; probe behavior, exact image, recovery and acceptance scope remain
unchanged. The same decision df703266-6719-4c04-ae1b-ba9afd3bca1c remains open
and pending. This source-only amendment grants no runtime authority or new
image/code change; T07/T10 remain wait and T09 remains done.

View file

@ -0,0 +1,26 @@
--- a/workplans/STATE-WP-0095-historical-source-receiving.md
+++ b/workplans/STATE-WP-0095-historical-source-receiving.md
@@ -235,7 +235,7 @@
```task
id: STATE-WP-0095-T07
-status: wait
+status: done
priority: high
depends_on: [STATE-WP-0095-T06, RMGR-WP-0019-T06, STATE-WP-0095-T10]
state_hub_task_id: "cfd1ea99-e75b-542e-a434-acefb62f3c7d"
@@ -325,11 +325,11 @@
```task
id: STATE-WP-0095-T10
-status: wait
+status: done
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."
+needs_human: false
+intervention_note: "Exact revision 2 approved and verified; no pending rollout intervention."
state_hub_task_id: "88585c69-d9b9-5e1f-a256-6416ff71e824"
```

View file

@ -0,0 +1,9 @@
{
"normal_make_render_exit": 0,
"objects": 8,
"verified_actual_image_fields": 3,
"T10": "done",
"T07": "wait",
"parent": "active",
"runtime_mutation": false
}

View file

@ -0,0 +1,32 @@
{
"schema": "state95.verified-guard-source-followthrough.v1",
"base_revision": "4c03c9d2c6a47f5c7247473f74b2e2f188b787bd",
"files": [
{
"path": "workplans/STATE-WP-0095-historical-source-receiving.md",
"base_sha256": "163acbdf8252f37e309f4cb0b51d48706c6bd43d439fdbf7802b953b78ae35ac",
"candidate_sha256": "bcde1a6502dfad71586dc38b2f681723dcb310c22f66f052b305ded89bf9b00d"
},
{
"path": "deploy/railiance/apps/charts/state-hub/Chart.yaml",
"base_sha256": "61a989fcf277d05ffbececb7332ee3ca1d8f369c72f960ab1bc4dbc6815536b8",
"candidate_sha256": "7d6d71dfe6dc85005f067d65b76d5f70215b96c6aa1c82b218ded1152c40fb43"
},
{
"path": "Makefile",
"base_sha256": "c5b70e8d4b8ae70380e0ade1c35f9e7fe80038de10080a4723b21ef07a98001e",
"candidate_sha256": "3bc22d33cc055d0b7cb0a3b4ee6df406aa7b32d5916cb456264cbb348a3cf764"
},
{
"path": "deploy/railiance/apps/helm/state-hub-values.yaml",
"base_sha256": "b51296fc937450f4ecf0ae43243262a631b551389c0d09c8bfe58fa10e2f3ee9",
"candidate_sha256": "0e05127167fce1508321866792ad58796eaa4dedfb2bcd05e2e3ff39db804c5b"
}
],
"patch_sha256": "sha256:d5b3a123ed0ffaee4ff811b0864b9f1d35b39b8e793fd382c5f4a02f916b942b",
"T10": "done_verified",
"T07": "wait_for_RMGR19T06_actual_closure",
"parent": "active",
"new_UUIDs": false,
"runtime_changed": false
}

View file

@ -0,0 +1,207 @@
{
"ok": true,
"status": "applied",
"repo_slug": "state-hub",
"commit": "bb525bc304cbd79b8a8a1540aa28bf073a4e2c32",
"requests": 2,
"elapsed_ms": 19035.3,
"git": {
"upstream": "origin/main",
"behind": 0,
"ahead": 0
},
"identifiers": {
"schema": "repo-manager.identifier-assignment.v1",
"ok": true,
"executed": true,
"repo": "state-hub",
"namespace": "helixforge",
"assignments": [],
"historical_invalid_identifiers": [],
"historical_identity_collisions": [],
"files_changed": []
},
"receipt": {
"schema": "state-hub.repository-projection-reconcile.v1",
"instance_role": "primary",
"instance_label": "railiance01",
"expected_commit": "bb525bc304cbd79b8a8a1540aa28bf073a4e2c32",
"derived_commit": "bb525bc304cbd79b8a8a1540aa28bf073a4e2c32",
"outcome": {
"schema": "state-hub.projection-reset.v1",
"repo_slug": "state-hub",
"commit": "bb525bc304cbd79b8a8a1540aa28bf073a4e2c32",
"status": "applied",
"counts": {
"created": 0,
"updated": 70,
"retired": 0,
"refused": 0,
"released": 0,
"created_tasks": 0,
"updated_tasks": 1,
"cancelled_tasks": 0,
"created_edges": 0,
"removed_edges": 1,
"unresolved_edges": 0,
"external_edges": 6
},
"created": [],
"updated": [
"CUST-WP-0003",
"CUST-WP-0011",
"CUST-WP-0012",
"CUST-WP-0038",
"CUST-WP-0042",
"STATE-WP-0043",
"STATE-WP-0044",
"STATE-WP-0045",
"STATE-WP-0046",
"STATE-WP-0047",
"STATE-WP-0048",
"STATE-WP-0049",
"STATE-WP-0050",
"STATE-WP-0051",
"STATE-WP-0052",
"STATE-WP-0053",
"STATE-WP-0054",
"STATE-WP-0055",
"STATE-WP-0056",
"STATE-WP-0057",
"STATE-WP-0058",
"STATE-WP-0059",
"STATE-WP-0060",
"STATE-WP-0061",
"STATE-WP-0062",
"STATE-WP-0063",
"STATE-WP-0064",
"STATE-WP-0065",
"STATE-WP-0066",
"STATE-WP-0067",
"STATE-WP-0068",
"STATE-WP-0069",
"STATE-WP-0070",
"STATE-WP-0071",
"STATE-WP-0072",
"STATE-WP-0073",
"STATE-WP-0074",
"STATE-WP-0075",
"STATE-WP-0076",
"STATE-WP-0077",
"STATE-WP-0078",
"STATE-WP-0079",
"STATE-WP-0080",
"STATE-WP-0081",
"STATE-WP-0082",
"STATE-WP-0083",
"STATE-WP-0084",
"STATE-WP-0085",
"STATE-WP-0086",
"STATE-WP-0087",
"STATE-WP-0088",
"STATE-WP-0089",
"STATE-WP-0090",
"STATE-WP-0091",
"STATE-WP-0092",
"STATE-WP-0093",
"STATE-WP-0094",
"STATE-WP-0095",
"STATE-WP-0096",
"STATE-WP-0097",
"STATE-WP-0098",
"STATE-WP-ADHOC-2026-06-04",
"STATE-WP-ADHOC-2026-07-01",
"STATE-WP-ADHOC-2026-08-08",
"STATE-WP-ADHOC-2026-08-23",
"STATE-WP-ADHOC-2026-08-30",
"STATE-WP-ADHOC-2026-09-04",
"STATE-WP-ADHOC-2026-09-22",
"STATE-WP-ADHOC-2026-10-06",
"STATE-WP-ADHOC-2026-10-13"
],
"retired": [],
"released": [],
"created_tasks": [],
"updated_tasks": [
"STATE-WP-0095-T10"
],
"cancelled_tasks": [],
"created_edges": [],
"removed_edges": [
{
"id": "2dbd5851-295e-4260-ba2f-2357324e5c8c",
"from_workplan_id": "79579572-8120-5d3b-aca5-dd4aff60d058",
"from_task_id": "88585c69-d9b9-5e1f-a256-6416ff71e824",
"to_workplan_id": null,
"to_task_id": "8be8f13b-6352-5295-9755-de6fbfb13593",
"relationship_type": "starts_after"
}
],
"unresolved_edges": [],
"external_edges": [
{
"workplan": "STATE-WP-0098",
"from": "STATE-WP-0098-T04",
"target": "HUB-WP-0011-T01",
"relationship_type": "starts_after",
"from_workplan_id": "0c68970e-295b-57fc-ac70-e472ee3a7bc4",
"from_task_id": "077797c8-289b-55ec-9597-28f223891b02",
"to_workplan_id": null,
"to_task_id": "588e4b58-4694-513c-8e7a-1fab38fb6ce3"
},
{
"workplan": "CUST-WP-0038",
"from": "CUST-WP-0038",
"target": "CUST-WP-0011",
"relationship_type": "blocks",
"from_workplan_id": "ccb87fa4-6381-5f22-b097-6e026f56a9c1",
"from_task_id": null,
"to_workplan_id": "967baafb-d92d-405a-ba0b-0d00d37c4940",
"to_task_id": null
},
{
"workplan": "STATE-WP-0079",
"from": "STATE-WP-0079-T04",
"target": "HUB-WP-0011-T03",
"relationship_type": "starts_after",
"from_workplan_id": "ed077b62-7048-5752-bf65-f90471f45854",
"from_task_id": "3a454438-1955-547f-a491-8eab7cd25710",
"to_workplan_id": null,
"to_task_id": "61c727a5-f4b1-54c0-b634-7d71a5416496"
},
{
"workplan": "STATE-WP-0079",
"from": "STATE-WP-0079-T04",
"target": "HUB-WP-0006-T06",
"relationship_type": "starts_after",
"from_workplan_id": "ed077b62-7048-5752-bf65-f90471f45854",
"from_task_id": "3a454438-1955-547f-a491-8eab7cd25710",
"to_workplan_id": null,
"to_task_id": "7c8225f7-03dc-57b0-a668-c3d781bd828a"
},
{
"workplan": "STATE-WP-0079",
"from": "STATE-WP-0079-T06",
"target": "STATE-WP-0079-T04",
"relationship_type": "starts_after",
"from_workplan_id": "ed077b62-7048-5752-bf65-f90471f45854",
"from_task_id": "9230c99b-af58-5794-9291-452e619a30fa",
"to_workplan_id": null,
"to_task_id": "3a454438-1955-547f-a491-8eab7cd25710"
},
{
"workplan": "STATE-WP-0079",
"from": "STATE-WP-0079-T06",
"target": "STATE-WP-0079-T05",
"relationship_type": "starts_after",
"from_workplan_id": "ed077b62-7048-5752-bf65-f90471f45854",
"from_task_id": "9230c99b-af58-5794-9291-452e619a30fa",
"to_workplan_id": null,
"to_task_id": "f6cf8c01-eb59-5ca8-a541-23928d4a000c"
}
],
"refused": [],
"notes": []
}
}
}

View file

@ -0,0 +1,31 @@
from pathlib import Path
import hashlib,json,importlib.util,subprocess,yaml,re
p=Path(__file__).resolve().parent;owner=Path('/home/worsch/state-hub')
spec=importlib.util.spec_from_file_location('pub',p.parents[1]/'owner-followthrough-r5/publish_followthrough_source.py');pub=importlib.util.module_from_spec(spec);spec.loader.exec_module(pub)
d=json.loads((p/'manifest.json').read_text());sha=lambda b:hashlib.sha256(b).hexdigest();names={e['path'] for e in d['files']};assert len(names)==4
assert pub.git(owner,'rev-parse','HEAD').stdout.decode().strip()==d['base_revision']
assert sha((p/'state-hub-verified-followthrough.patch').read_bytes())=='d5b3a123ed0ffaee4ff811b0864b9f1d35b39b8e793fd382c5f4a02f916b942b'
before=pub.preserved_state(owner,names)
for e in d['files']:
f=owner/e['path'];s=p/'state-hub'/e['path'];assert not f.is_symlink() and not s.is_symlink()
assert sha(f.read_bytes())==e['base_sha256'] and sha(s.read_bytes())==e['candidate_sha256']
assert not pub.git(owner,'diff','--cached','--',e['path']).stdout
for e in d['files']:(owner/e['path']).write_bytes((p/'state-hub'/e['path']).read_bytes())
assert pub.preserved_state(owner,names)==before
render=subprocess.run(['make','railiance-state-hub-render'],cwd=owner,capture_output=True,text=True,timeout=45);assert render.returncode==0
objects=[o for o in yaml.safe_load_all(render.stdout[render.stdout.index('---'):]) if o];images=[]
def walk(v):
if isinstance(v,dict):
for k,x in v.items():
if k=='image':images.append(x)
walk(x)
elif isinstance(v,list):
for x in v:walk(x)
for obj in objects:walk(obj)
assert len(objects)==8 and len(images)==3 and set(images)=={'forgejo.coulomb.social/coulomb/state-hub:main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1'}
text=(owner/'workplans/STATE-WP-0095-historical-source-receiving.md').read_text();tasks={b['id']:b for b in (yaml.safe_load(x) for x in re.findall(r'```task\n(.*?)```',text,re.S))};assert len(tasks)==10
assert tasks['STATE-WP-0095-T10']['status']=='done' and tasks['STATE-WP-0095-T10']['needs_human'] is False
assert tasks['STATE-WP-0095-T07']['status']=='wait'
(p/'canonical-validation.json').write_text(json.dumps({'normal_make_render_exit':0,'objects':8,'verified_actual_image_fields':3,'T10':'done','T07':'wait','parent':'active','runtime_mutation':False},indent=2)+'\n')
r=pub.commit_source(owner,d,pub.SCANNER,execute=True,push=False);assert pub.preserved_state(owner,names)==before
(p/'source-commit-result.json').write_text(json.dumps(r,indent=2)+'\n');print(json.dumps({k:v for k,v in r.items() if k not in {'preserved_before','preserved_after'}}))

View file

@ -0,0 +1,10 @@
from pathlib import Path
import subprocess,json
p=Path(__file__).resolve().parent
r=subprocess.run(['make','sync','SYNC_PATH=.','SYNC_PUSH=1','STATE_HUB_API_BASE=http://127.0.0.1:8000'],cwd='/home/worsch/state-hub',capture_output=True,text=True,timeout=180)
try:d=json.loads(r.stdout[r.stdout.index('{'):])
except Exception:d={'status':'unverified','reason':'normal_sync_receipt_unparseable','exit':r.returncode}
(p/'normal-sync-result.json').write_text(json.dumps(d,indent=2)+'\n')
receipt=d.get('receipt',{})
print(json.dumps({'exit':r.returncode,'status':d.get('status'),'commit':d.get('commit'),'instance_role':receipt.get('instance_role'),'instance_label':receipt.get('instance_label'),'expected_commit':receipt.get('expected_commit'),'derived_commit':receipt.get('derived_commit'),'identifier_assignments':d.get('identifiers',{}).get('assignments')}))
raise SystemExit(r.returncode)

View file

@ -0,0 +1,80 @@
{
"schema": "state95.verified-guard-source-readback.v1",
"status": "applied",
"source_revision": "bb525bc304cbd79b8a8a1540aa28bf073a4e2c32",
"remote_revision": "bb525bc304cbd79b8a8a1540aa28bf073a4e2c32",
"instance_role": "primary",
"instance_label": "railiance01",
"expected_commit": "bb525bc304cbd79b8a8a1540aa28bf073a4e2c32",
"derived_commit": "bb525bc304cbd79b8a8a1540aa28bf073a4e2c32",
"tasks": [
{
"uuid": "6a5f87bb-e63d-5e20-b9fc-c1b5cd403ff7",
"record_id": "STATE-WP-0095-T01",
"status": "done",
"needs_human": false
},
{
"uuid": "ebb24194-e4cc-5130-aaa6-663f2ea2c822",
"record_id": "STATE-WP-0095-T02",
"status": "done",
"needs_human": false
},
{
"uuid": "e8566341-cf6e-5b9c-ad80-8564d14cd52a",
"record_id": "STATE-WP-0095-T03",
"status": "done",
"needs_human": false
},
{
"uuid": "3bbb311f-194e-5862-8aed-358a580f22b4",
"record_id": "STATE-WP-0095-T04",
"status": "done",
"needs_human": false
},
{
"uuid": "339b9a37-181c-5599-9a89-0393e199bd4b",
"record_id": "STATE-WP-0095-T05",
"status": "done",
"needs_human": false
},
{
"uuid": "6af973b4-1964-5d4d-a2f8-8dbd4c01fda4",
"record_id": "STATE-WP-0095-T06",
"status": "done",
"needs_human": false
},
{
"uuid": "cfd1ea99-e75b-542e-a434-acefb62f3c7d",
"record_id": "STATE-WP-0095-T07",
"status": "wait",
"needs_human": false
},
{
"uuid": "2dd9b04f-2250-5a8a-b5bc-c8a009872858",
"record_id": "STATE-WP-0095-T08",
"status": "done",
"needs_human": false
},
{
"uuid": "8be8f13b-6352-5295-9755-de6fbfb13593",
"record_id": "STATE-WP-0095-T09",
"status": "done",
"needs_human": false
},
{
"uuid": "88585c69-d9b9-5e1f-a256-6416ff71e824",
"record_id": "STATE-WP-0095-T10",
"status": "done",
"needs_human": false
}
],
"parent_status": "active",
"whole_unrelated_index_dirty_preserved": true,
"new_UUIDs": false,
"runtime_mutated_by_source_followthrough": false,
"declared_runtime_source": "c103cbf3c8bed5dba87a03e320595ddded9301a3",
"declared_image": "main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1",
"original_frozen_archive_unchanged": true,
"T07": "wait_for_RMGR19T06_published_closure"
}

View file

@ -0,0 +1,79 @@
--- a/workplans/STATE-WP-0095-historical-source-receiving.md
+++ b/workplans/STATE-WP-0095-historical-source-receiving.md
@@ -325,11 +325,11 @@
```task
id: STATE-WP-0095-T10
-status: wait
+status: done
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."
+needs_human: false
+intervention_note: "Exact revision 2 approved and verified; no pending rollout intervention."
state_hub_task_id: "88585c69-d9b9-5e1f-a256-6416ff71e824"
```
@@ -392,3 +392,31 @@
unchanged. The same decision df703266-6719-4c04-ae1b-ba9afd3bca1c remains open
and pending. This source-only amendment grants no runtime authority or new
image/code change; T07/T10 remain wait and T09 remains done.
+
+
+T10 verified receiving return, 2026-10-17: the founder approved and the owner
+executed exact revision 2 of STATE-WP-0095-T10-frozen-bindings-guard-r1,
+sha256:5d0ddebc21bcb035809cc8b57f4c2e1ba8ed09204be5b965460e178a90913f1e,
+under decision df703266-6719-4c04-ae1b-ba9afd3bca1c. Helm revision 77 delivered
+runtime source c103cbf3c8bed5dba87a03e320595ddded9301a3 and image
+main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1.
+API/MCP are Ready on expected schema c4d5e6f7a8b9. Eight actual native cases
+passed with zero probe commits. All three parent and 15 task CURRENT full-row
+hashes remained unchanged after delivery/probes and the actual normal Cluster
+pipeline (20 ordinary bindings, three frozen bindings omitted by the verified
+loaded checker). All three archived sources remained byte-identical. Exact
+owned Job/pod success was observed, then their absence and removal of owned
+chart staging were verified; no recovery was needed.
+
+The verified return is the Custodian dated 2026-10-17
+ guard-successor-execution-r2/verified-receiving-return.json,
+sha256:09958bd69367cc6903e2a21a56eb81b6a8a46c2a1907afc93f787cd63732e92a.
+The original three-parent hash failures remain the founder-accepted historical
+exception: unavailable original preimages were not guessed/restored, and failed
+outcomes were not backfilled. The 15 original task rows and archived bytes stay
+preserved. Earlier pending review notes are retained as proposal history; T10
+is now done and needs_human false. T07 and this parent remain open until the
+actual RMGR-WP-0019-T06 owner closure is published and returned. No native
+security-target gate or broader autonomy is established by this infrastructure
+return. Source appVersion and both maintained image pins now declare this same
+verified image; the approved frozen rollout archive remains unchanged.
--- 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-51bc70d"
+appVersion: "main-c103cbf"
--- 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-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015
+RAILIANCE_STATE_HUB_IMAGE_TAG ?= main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1
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/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-51bc70d@sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015"
+ tag: "main-c103cbf@sha256:4cc4ce17bbcdb1b66132d07c5b7b741f46df83124ef1c8403ffabdafba6993d1"
ingress:
enabled: false