Commit graph

58 commits

Author SHA1 Message Date
b43a1e5728 fix(projection): qualify bare task ids before deriving their identity
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 26s
Retiring llm-connect's five legacy rows failed on `duplicate key value violates
unique constraint "tasks_pkey"`, and the cause is that a bare `T01` is not an
identifier: it is unique within its workplan, not in the fleet. Unqualified,
uuid5("T01") is the same UUID for every workplan in the fleet that has one —
llm-connect's 91 task blocks derive 49 distinct UUIDs, so creating its
workplans inserts the same task primary key repeatedly in one flush.

Tasks are now qualified with their owning workplan before derivation, which is
the rule `task_record_id_backfill.qualify_task_id` already applies to stored
ids; the two must agree or the backfill and the projection disagree about what
a task is called. Already-qualified ids are untouched.

The create path's comment claimed it was "safe only because nothing exists to
mis-match against: this workplan is new to the hub". That was true of other
workplans and false within one: the collision was among the tasks it was
inserting itself.

The failed pass rolled back cleanly — llm-connect's five legacy rows are still
live and progress events are intact.

742 pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-28 10:38:12 +02:00
d4ca94198e fix(projection): the derived UUID is identity, check it before the slug
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 26s
Two rows were being proposed for retirement that were correct all along.
`testdrive-jsui-publication` and `three-phoenix-ha-cluster` carry legacy title
slugs, but their UUIDs are exactly uuid5 of MARKITECT-WP-0002 and
RCLUSTER-WP-0007 — they are those records. `_identity_is_derived` short-circuits
on the UUID *version* before the slug rule ever runs, so both read as re-keys.
Acknowledging retirements for either repository would have retired a live
record on the strength of its slug.

The derived UUID is the strongest key available and was not used in matching at
all. It is now checked first: if a row's id equals uuid5 of a wanted
identifier, that row is that record, whatever its slug says. The slug and path
heuristics now only decide rows the UUID cannot identify.

This also corrects a claim I made from the retirement list alone: those files
derive normally. Nothing was wrong with them.

738 pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-28 03:17:21 +02:00
7a21fef0e4 fix(projection): repair rows retired before retirement released identifiers
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 25s
The previous commit made retirement stamp the slug, but `stale` excludes rows
already retired — so nothing revisits them and the stamp never reaches the 32
rows retired earlier today: core-hub's two REPO-WP rows and the 30 ad-hoc
records. Each still holds an identifier no file derives, blocking whichever
repository legitimately owns it.

The reset now stamps any retired row whose identifier is still unreleased,
using that row's own retirement timestamp rather than today's, so the record
keeps saying when it was retired. Completing a decision already taken is not a
new retirement, so this runs without acknowledgement and is reported as
`released`, separately from `retired`.

Repairing it in the reset rather than by hand keeps the hub a read model and
makes the fix reproducible for any row that reaches this state again.

735 pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-28 01:25:06 +02:00
70c585bb39 fix(projection): retirement must release the identifier it holds
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 0s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 24s
`slug` is unique across the whole table, so retiring a row by timestamp alone
leaves its identifier locked to a record nothing derives any more — and the
repository that legitimately owns it can never claim it. core-hub's inherited
REPO-WP-0001/0002 rows retired cleanly and repo-seed stayed refused with
"slug already belongs to another repository", pointing at two retired rows.

Retirement now stamps the slug `<slug>@retired-YYYYMMDD`. The row, its UUID and
its progress events are untouched, so history stays attached to the record it
happened under; only the human-facing identifier is freed. Re-retiring an
already-stamped row replaces the mark rather than stacking it, or the 100-char
column overflows after a few passes.

The UUIDs never collided here: core-hub's rows predate derived identity and
carry v4 UUIDs, while repo-seed derives v5 ones. The identifier was the only
thing in the way.

733 pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-28 01:15:02 +02:00
ccf4959423 fix(projection): recognise a re-key by the slug, not the UUID version
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 24s
The previous commit tested UUID version as a proxy for "identity is derived".
It covers 19 of the 44 ad-hoc rows; the other 25 are legacy v4, so they kept
path matching and would have been updated in place — slug still
`adhoc-2026-07-02` while the file says `ADAPTIVE-WP-ADHOC-2026-07-02`. The
divergence would have persisted, and silently, for most of the records this
was meant to fix.

The distinction that actually matters is whether the row's slug is a
work-record identifier. A row whose slug is an identifier claims to be that
record, so a different derived identifier for the same file means two records.
A row whose slug is a title — `three-phoenix-ha-cluster` for RCLUSTER-WP-0007 —
never claimed one: those are hub-first rows from before ADR-001 where the
backing path is the only link there has ever been, and path matching is all
that holds them together.

730 pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-28 00:52:26 +02:00
b2efadf254 fix(projection): a re-keyed record is new, not a renamed file
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 27s
The ad-hoc requalification changed 30 identifiers while deliberately keeping
the filenames, per canon. The matcher's path fallback therefore matched every
old row to its new record and updated it in place — so the files said
CUST-WP-ADHOC-2026-08-25 while the hub still said adhoc-2026-08-25, with the
row's UUID still encoding the old identifier. Exactly the file/hub identity
divergence ADR-007 exists to prevent.

Path matching cannot distinguish a re-key from a rename when the path does not
change. Identity can: a derived row is UUIDv5 over the record id, so a changed
id is a different record — the old row retires and the new one is created.
Rows predating derived identity carry v4 UUIDs, where the identifier is a label
rather than the identity; those keep path matching, so rename detection still
works where it is the right answer.

The unmatched old row keeps its own slug as key, so it lands in `stale` and
becomes a retirement candidate rather than disappearing from the outcome.

720 pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-28 00:38:50 +02:00
470ece82ed feat(forge): resolve an optional forge read credential (STATE-WP-0084-T02/T03)
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 27s
Nine repositories are invisible to derivation because central may not read
them. This adds the consuming half of the credential lane MASON-WP-0003 built.

The cluster has no agent injector and no secrets-store CSI driver, so the pod
authenticates to OpenBao with a projected ServiceAccount token (audience
`openbao`, not the API server) and reads the KV path itself. `forgeRead.*`
carries coordinates only; no credential is a chart value, an image layer, or a
Kubernetes Secret.

The credential reaches git through GIT_CONFIG_* setting http.extraHeader, not
through `-c` and not through userinfo in the clone URL — both of those put the
token in the process listing. It is redacted from ForgeDeriveError, which is
logged, stored in reset outcomes, and returned over the API.

Absent stays a supported state: with no credential, or with OpenBao
unreachable, resolution returns None and public derivation runs unchanged.
Raising would turn "nine repositories are unreadable" into "the pass failed",
which is what T01 exists to prevent.

Chart default is disabled. 717 pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-27 23:12:57 +02:00
85181cd3e4 feat(forge): report unreadable repositories as unreadable (STATE-WP-0084-T01)
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 25s
A private repository failed derivation the same way a broken one did, so
"cannot read" and "does not exist" were indistinguishable from outside.
They authorise opposite things: only the second can justify retiring a
record.

- ForgeUnreadableError (a ForgeDeriveError, so old callers still catch it)
  for permission-shaped clone failures, including Forgejo's 404 for an
  unauthenticated private repo — indistinguishable here, and the safe
  reading of an ambiguous answer cannot destroy a record.
- GIT_TERMINAL_PROMPT=0: an unattended pass must fail, not block on a
  username prompt. Failing is what makes the case observable.
- DerivedProjection.retirement_eligible separates "no records found" from
  "no records exist". A checkout with no workplans/ directory cannot
  evidence an absence — the empty-clone path that would have proposed
  every record in a repository for retirement.
- Retirement from an ineligible source is refused even when acknowledged.
- Fleet keeps unreadable out of the error bucket.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 3377672@bnt-lap001
Assistant-Session: 15463ccf-238f-4e13-b163-93aa25c6d166
2026-08-26 21:45:03 +02:00
6c1262ef6e fix(projection): refuse slug collisions, and record the first fleet pass
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 26s
The identifier refusal checked id only. slug carries its own unique constraint
across the whole table, so two repositories can derive different identifiers
whose slugs still collide — which left disaster-control raising IntegrityError.

First fleet-wide pass over 121 repositories: 91 applied (737 updated, 8
created), 16 refused covering 64 records, 12 errored. 745 workplans now carry
the commit they derived from, satisfying ADR-012 decision 2 for the first time.

64 is the measured size of the stale-row problem CUST-WP-0068-T09 has waited on.

Eleven of the twelve errors are private repositories the pod cannot clone
anonymously — a real limit on "the forge is the projection source", since their
absence currently looks like an error rather than a policy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-26 16:39:26 +02:00
532583ce17 feat(projection): add the fleet reset as a loop over the repository form
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 25s
ADR-012 decision 7 requires the fleet form to share the per-repository
implementation: the rarely-run wide operation must be the frequently-run narrow
one, or the wide one is trusted on the strength of never having been exercised.

Failure behaviour is the substance. A refusal does not stop the pass — aborting
on the first refusal means one unresolved repository blocks reconstruction
everywhere, which in practice means permanently. An error does not stop it
either. Each repository gets its own session so one failure cannot roll back
another's work, and only repositories that applied are committed.

Refs STATE-WP-0083-T04

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-26 13:23:08 +02:00
5e4d0be31c fix(projection): refuse a colliding identifier instead of failing on a constraint
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 26s
The reset raised IntegrityError on net-kingdom: its ADHOC-2026-08-23 derives to
an identifier another repository already holds — the collision CUST-WP-0066
documents, where two repositories created the same daily identifier on the same
day. Derivation is deterministic, so the clash is real rather than incidental.

It now checks, before creating anything, whether a derived identifier belongs to
another repository, and refuses naming both the record and the holder. A refusal
is something the caller can rule on; a constraint violation is a stack trace.

Acknowledging retirements deliberately does not authorise a collision. Those are
different decisions — one says the work is gone, the other says take an
identifier another repository owns — and conflating them would let a routine
acknowledgement smuggle an identity change through.

Refs STATE-WP-0083-T03

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-26 09:42:14 +02:00
7f41d42488 fix(backfill): qualify short task ids with their workplan
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 28s
A task id written as a bare "T01" is unique only inside its own workplan.
Stored as a canonical identifier it makes every workplan's first task share one
identity: 51 such ids were assigned to 148 rows on central before this was
caught, found because identified rows outnumbered distinct identities.

Short ids are now qualified as WORKPLAN-ID-T01. A short id in a file with no
workplan id in frontmatter is left unidentified — an identity that is not unique
is worse than none, which is the same rule the rest of this module already
follows.

The 136 affected rows on central have been cleared so the corrected backfill can
reassign them; the backfill never overwrites an existing identity, so they had to
be nulled rather than re-derived over.

Refs STATE-WP-0083-T06

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-26 02:17:46 +02:00
cdd5cef373 fix(backfill): source task identities from the forge, not a workstation
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 26s
The first implementation took local filesystem paths. Central has no workstation
checkout and must not depend on one: ADR-012 decision 1 makes the forge the
projection source, and a backfill reading someone's laptop would reintroduce the
exact coupling that ADR removes.

Surfaced concretely — central's postgres is not reachable from the workstation
(only the API tunnel, which is HTTP), so the local-path variant cannot reach the
database it needs to update, while the pod can clone the forge and already holds
the connection.

A repository that cannot be cloned contributes nothing rather than reducing what
the rest can identify.

Refs STATE-WP-0083-T06

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-26 02:11:23 +02:00
8b207a991a feat(tasks): give task rows a canonical record identifier
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 26s
Every work-record type carried a stable identifier except tasks, whose rows held
only id, workplan_id, title, status and priority — nothing connecting a row to
CUST-WP-0067-T01 in the file it came from. Matching was therefore by title, so a
renamed heading looked like one task vanishing and another appearing, and the
forge-derived reset had to refuse to touch tasks at all.

Adds tasks.record_id (nullable: no migration can invent an identity for an
existing row) and a backfill that reads the pairing from the repository files,
where a task declares both its canonical id and its projection UUID. 5516 pairs
across 121 repositories with zero conflicts; 4456 of 6073 cache task rows
identified.

Diff and reset now key on record_id where present, falling back to a
title-prefixed key so an unidentified row stays visibly unidentified.

Unknown stays unknown: a row the files do not claim keeps no identity and the
reset keeps refusing to act on it, and an existing identity is never
overwritten — a mismatch is recorded as a conflict rather than resolved.

Refs STATE-WP-0083-T06

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-26 02:05:51 +02:00
43ffe883c3 feat(projection): reconcile a repository's projection against the forge
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 26s
Implements ADR-012 decision 7 as amended (STATE-WP-0083-T03). Creates what the
forge has and the hub lacks, updates what differs, retires what no longer
derives. It never deletes: hub-native records reference workplans with ON DELETE
RESTRICT, and destroying a progress event to tidy a derived projection would
lose hub-native truth to fix a derived-state problem.

Retirement is refused by default. A record that stops deriving may mean a
deliberately deleted file or a caller pointed at the wrong branch; only the
caller can say which.

Verified against live data and rolled back: whitehat-security applied 5 updates
with no retirements; the-custodian refused, naming the four hub-first records
confirmed by hand to have no backing file.

Tasks of existing workplans are deliberately untouched — hub tasks carry no
canonical identifier, so matching is by title and a renamed heading would
destroy and recreate a record. Tasks are created only alongside a new workplan,
where nothing exists to mis-match. Tracked as T06.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-26 01:19:10 +02:00
97c8762a71 feat(deploy): run migrations as part of the release, and report schema state
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 27s
Central was serving two revisions behind the code it shipped: review_contracts
did not exist there although its migration was inside the running image. There
was no migration mechanism at all — bare uvicorn CMD, nothing chart-declared —
and nothing surfaced the mismatch. The API starts happily against a schema it
was not built for and only fails when a request touches a missing table.

Adds a chart-managed Helm pre-install/pre-upgrade hook running alembic upgrade
head, weighted to complete before the API rolls. A hook rather than an init
container: init containers run per pod, so more than one replica means
concurrent alembic upgrade with no locking. Failed jobs are deliberately
retained — a migration that fails and vanishes is how this drifted in the first
place.

/state/health now reports applied and expected revisions. "unknown" is
deliberately not "ok": an instance that cannot establish agreement must not
claim it, the same principle as instance_role defaulting to unknown.

Refs STATE-WP-0083-T07

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-26 00:00:09 +02:00
fd0d0d537b feat(projection): derive a repository's projection from the forge
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 23s
Implements ADR-012 decisions 1 and 2 (STATE-WP-0083 T01, T02 partial). Central
clones the default branch from Forgejo and derives its own projection: 69
workplans and 459 tasks from the-custodian at d5013ae, identical across runs,
with the commit recorded as provenance.

Identifiers are derived in the ADR-007 namespace and verified against live
records, so a forge-derived projection and a preliminary overlay agree on
identity without reconciliation.

The diff first matched hub records by UUID and was badly wrong: most hub records
carry pre-ADR-007 random identifiers, so nearly everything appeared
simultaneously missing and stale, and a reset built on it would have destroyed
and recreated the entire projection. It now matches canonical record id, falling
back to the backing file. whitehat-security — bootstrapped straight from files —
now reports clean, which is the control.

Task-level comparison is deliberately not trusted: hub tasks carry no canonical
record id, only a title, so matching is by title. Recorded as T06; T03 is
limited to workplans until it lands.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-25 23:34:25 +02:00
b0e1af24f9 fix(retirement): close projection and launch contract gaps
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 23s
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02b7c-1c49-76a0-955a-49e7b3ddfc0d
2026-08-23 00:52:18 +02:00
598f6418e7 feat(review): add multi-owner contracts and receipts
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 25s
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a023c0-a0a3-7c03-b395-5a0d2757214d
2026-08-22 20:57:51 +02:00
76e6eda086 feat: project Nexus SBOM state into summaries
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 25s
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a028f0-a42f-7582-89a8-ebaad7343834
2026-08-22 20:12:16 +02:00
b75234a533 feat: route SBOM writes to Nexus behind flag
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 24s
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a028f0-a42f-7582-89a8-ebaad7343834
2026-08-22 19:55:08 +02:00
5fc4c56215 feat: add reversible SBOM Nexus read facade
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 25s
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a028f0-a42f-7582-89a8-ebaad7343834
2026-08-22 18:39:21 +02:00
cb1b028fd1 feat(identifiers): add reversible projection migration
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 24s
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a023c0-a0a3-7c03-b395-5a0d2757214d
2026-08-22 00:27:48 +02:00
81861d816b feat(retirement): route SBOM scans through repo-manager
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 25s
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a023c0-a0a3-7c03-b395-5a0d2757214d
2026-08-21 23:15:34 +02:00
de58a0cf90 feat(STATE-WP-0079): add repo-manager receiving adapters
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 23s
2026-08-21 17:15:28 +02:00
fb363b37d3 feat(STATE-WP-0079): retire the suggestion-backlog surface (slice E1)
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 37s
History is archived fleet-side, so the read surface has no remaining job.

- api/routers/suggestions.py: whole prefix 410s with a pointer to intake and
  to the archive; the router drops from 176 lines to a stub
- mcp_server: the 6 suggestion tools removed outright rather than stubbed --
  a retired tool that still appears in the tool list costs every agent
  session context on every call, which is the opposite of retiring it
- write_idempotency: 5 /suggestions rules dropped
- dashboard: suggestions.md deleted, nav entry removed, reference.md and
  wsjf-triage.md updated; docs/suggestions.md rewritten as archive pointer
- tests: two tests pinned the old read-live behaviour and now pin the
  retirement contract instead

Tables stay: they are retire/archive in SHR-INV-0001 and are captured by the
final dump at T06.

Untouched, and worth knowing during cutover: ui-feedback.md / todo.md
'suggestions' are Shift+click dashboard feedback backed by technical_debt, a
different entity that shares the word. E3 (dashboard-meta) is that page; its
owner is state-hub-until-cutover so it retires at the T06 window, not now.

Full suite 612 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 08:11:14 +02:00
93e95f93e7 feat(legacy-meter): scale retirement quiet period to call volume
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 28s
A clean review window only proves an interface was silent for that window.
For a heavily used interface that is weak evidence -- an infrequent caller
can fall outside the window entirely, so the interface reads as retirable
while still having consumers.

_retirement_state now also requires silence since last_seen_at, scaled by
all-time volume (RETIREMENT_QUIET_LADDER): 7d under 100 calls, 30d under
10k, 60d above. Existing guards (retired / manual hold / replacement ref /
replacement verified / in-window traffic) still take precedence.

On the 2026-08-20 capture this makes 15 of 19 legacy interfaces retirable
and holds 4: the three six-figure /workstreams/ read paths, and
GET /tasks/?workstream_id which still has live traffic.

Unblocks the bulk of Wave E2 in the cutover slice plan. 7 new tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 07:13:44 +02:00
d8e0eddb22 feat: delegate project register; registrar-only ID minting
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 39s
STATE-WP-0080-T02: statehub register routes project-flavor scaffolding
through rmgr scaffold and keeps only repo + host-path registration.
T01 refuse remains when GOAL.md is missing and --wp-prefix is not set.

RMGR-WP-0005-T01: C-06/C-11/C-32 skip mint+writeback unless this
instance is the identifier registrar (STATEHUB_REGISTRAR or railiance
hostname).
2026-08-18 21:51:30 +02:00
71c94c940a feat: dual-run config file, bulk writeback, writeback_push
RMGR-WP-0003: load ~/.repo-manager/dual-run.yaml; bulk-status-sync uses
RM writeback; optional push after writeback when configured.
2026-08-16 02:53:13 +02:00
bfaff50b4e feat: dual-run facade to repo-manager for task writeback
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Failing after 57s
RMGR-WP-0002 Stage B: when RM_WRITEBACK/RM_RECONCILE flags are set, PATCH
/tasks and C-15 writeback call rmgr; fix_repo runs rmgr reconcile for pilots.
2026-08-09 23:19:56 +02:00
5927591be8 Prepare State Hub retirement baseline
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 1m0s
2026-08-09 16:19:53 +02:00
7036e2aa6b feat(api): retire phase-1/2 /workstreams REST aliases with 410 stranglers
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Has been cancelled
Build and Publish Multi-Context Image / build-and-push (push) Successful in 2m18s
Add retire_legacy_route() helper and convert CRUD, dependency, and
execution intent legacy routes to 410 Gone while preserving final-call
legacy-meter attribution. Update tests, retirement backlog, and close
STATE-WP-0075 with handoff to STATE-WP-0070 T02 and STATE-WP-0073 T04.
2026-07-10 13:31:57 +02:00
e0c954d098 Finish STATE-WP-0069: retire legacy completion event and DELETE /workstreams
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Stop dual-publishing org.statehub.workstream.completed; return 410 Gone for
legacy DELETE /workstreams/{id}. Migrate fix-consistency, MCP adhoc bootstrap,
and dashboard token summary to /workplans/. Add legacy-meter evidence capture
script and pytest snapshot; update docs and close out the workplan.
2026-07-08 23:37:46 +02:00
388b330809 STATE-WP-0069 T04/T06: body metering and workplan-first state internals
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Share LegacyWorkstreamIdBodyMixin across create schemas; meter POST /tasks/
and /decisions/ workstream_id bodies. State summary uses workplan flow;
NextStep dual-writes workplan_* fields alongside legacy workstream_*.
2026-07-08 23:13:28 +02:00
6e5e150803 STATE-WP-0069 T04: Sunset headers and POST /progress body metering
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Add Sunset to legacy compat responses (Jun 2027 planning horizon).
Meter POST /progress/ bodies that use workstream_id; wire hub-core body
hook. Consolidate workstreams deprecation headers via legacy_compat.
2026-07-08 23:06:45 +02:00
793a39a1a4 STATE-WP-0069 T04: meter workstream_id query-param aliases
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 3s
Add legacy_compat helper and Deprecation headers when callers filter
/tasks, /decisions, /token-events, or /execution/launch-requests with
workstream_id. Preferred workplan_id filters are unchanged. Route removal
remains gated on legacy-meter zero-usage windows.
2026-07-08 21:32:14 +02:00
f2e042a278 Finish CUST-WP-0011 and implement STATE-WP-0061 suggestion backlog.
Add persisted Suggestion entities with WSJF ranking, relevance bumps,
REST/MCP write surfaces, /suggestions dashboard page, and daily triage
digest integration. Document the cluster operating model, archive the
completed migration workplan, and seed WARDEN-WP-0012 routing scenarios.
2026-07-06 10:52:49 +02:00
b536741539 feat(statehub): add offline write buffer relay 2026-06-25 13:44:27 +02:00
94c7817339 feat(summary): revision-gated cache with stale-while-revalidate (STATE-WP-0066)
Replace the fixed 15s TTL on GET /state/summary with per-table revision
watermarks, stale-while-revalidate background refresh, and a progress-tail
section split. SQLAlchemy write hooks invalidate core or progress sections
on mutation. Adds tests, benchmark script, and operator docs.
2026-06-22 16:27:32 +02:00
0949d4c0d8 feat(classification-spine): implement STATE-WP-0065 repo-anchored model
Replace the ad-hoc coordination-domain spine with the Repo Classification
Standard: 14 market domains, classification columns on managed_repos, and
workplans anchored by repo_id (topic_id optional).

- Add Alembic migration d8e9f0a1b2c3 with data backfill and workstream→workplan rename
- Add api/classification.py validation and register-from-classification tooling
- Expose workplan-first REST/MCP surface with legacy workstream aliases
- Add C-24 consistency rule and legacy domain frontmatter mapping
- Update dashboard repos page with category/capability/stake filters
- Update orientation docs; mark STATE-WP-0065 finished
2026-06-22 13:52:13 +02:00
39ed5459b9 finish(STATE-WP-0064): cut over scheduler and split sweep errors from failures
STATE-WP-0064 cutover (state-hub only):
- Retire local custodian-sync.timer; archive units under infra/systemd/archived/
- Mark workplan finished; update infra/README, cron-migration, runbook, AGENTS.md
- Point activity-core-delegation at the consistency-sweep runbook

Consistency engine — automation error vs assessment failure:
- C-00 is an automation error; C-01..C-23 assessment failures are recorded
  for follow-up but no longer fail --remote --all scheduled sweeps (exit 0)
- Skip workplans/README.md in the workplan glob (human index, not a workplan)
- Progress events and compare script expose automation_error and
  assessment_failures separately from exit_code
2026-06-22 01:20:59 +02:00
ab14e77e77 feat(STATE-WP-0064): start parallel week with source-tagged sweep runners
Tag consistency_sweep_remote_all progress events by source, route the local
timer through the API, add a parallel-week comparison script, and document
the 2026-06-21 to 2026-06-28 observation window for T03.
2026-06-21 21:46:43 +02:00
821b5d6c89 fix(STATE-WP-0064): parse consistency sweep stdout with skip prefixes
Extract the JSON payload from mixed script output and document Railiance01
kubectl sync steps. Mark T02 done after cluster bridge and resolver canaries.
2026-06-21 20:56:35 +02:00
5a7a6ef5ee feat(STATE-WP-0064): add consistency sweep remote-all API endpoint
Expose POST /consistency/sweep/remote-all so activity-core can trigger
the workstation ADR-001 remote-all sweep via the bridge tunnel pattern.
Records consistency_sweep_remote_all progress events and documents the
cutover runbook while the local custodian-sync timer remains interim.
2026-06-21 20:19:22 +02:00
166aedfa8d feat: add workplan aliases and legacy meter
Adds preferred workplan REST/event surfaces, legacy-meter telemetry and weekly review summaries, documentation/dashboard terminology updates, dashboard API loading fixes, and close-out sync for STATE-WP-0052 and STATE-WP-0054.
2026-06-04 08:25:31 +02:00
38835e9e79 feat(tasks): adopt canonical task statuses 2026-05-26 01:32:50 +02:00
f25569d9d4 Implement financial Fabric vNext read model 2026-05-24 02:52:59 +02:00
ec5742543c Add Fabric graph read model ingest 2026-05-23 21:17:58 +02:00
d4dea7864d Add workplan execution queue 2026-05-23 19:11:30 +02:00
0ea46f081c Close lifecycle transition helper workplan 2026-05-23 18:42:32 +02:00