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
This commit is contained in:
tegwick 2026-08-26 01:19:10 +02:00
parent 6bb1fe823a
commit 43ffe883c3
3 changed files with 365 additions and 1 deletions

View file

@ -139,7 +139,7 @@ until tasks carry their canonical id, which is `T06`.
```task
id: STATE-WP-0083-T03
status: todo
status: done
priority: high
```
@ -156,6 +156,30 @@ Acceptance: reset twice produces the same projection; a repository holding
records the forge lacks is refused with those records named; hub-native record
counts are unchanged across a reset.
**Done (2026-08-26).** `reset_repository_projection()` in
`api/services/forge_projection.py`. Verified against live data, rolled back:
`whitehat-security` applied 5 updates and 0 retirements; `the-custodian`
**refused**, naming `cust-wp-0023`, `cust-wp-0024`, `state-hub-v0.1` and
`state-hub-v0.2` — the four records confirmed by hand as genuine hub-first
records with no file — and changed nothing.
Refusal is the default because a record that stops deriving is ambiguous: the
file may have been deleted deliberately, or the caller may have pointed at the
wrong branch. Only the caller can say which, so only the caller may authorise it.
Retirement never deletes, with a test asserting the row survives and
`session.delete` is never called. A record that derives again is un-retired
rather than left contradicting the forge.
The first execution of this write path was run against the **cache** database
rather than central, and rolled back. A write path's first run belongs on the
discardable copy.
**Scope limit, enforced in the code rather than documented beside it.** Tasks of
existing workplans are untouched; tasks are created only alongside a *new*
workplan, where nothing exists to mis-match. The outcome carries that as a note
so partial convergence cannot be mistaken for full. 678 tests pass.
## Fleet form as a loop over the repository form
```task