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
This commit is contained in:
tegwick 2026-08-26 16:39:26 +02:00
parent 5d09e8f5b0
commit 6c1262ef6e
3 changed files with 119 additions and 3 deletions

View file

@ -583,4 +583,51 @@ reset was reporting the truth about a fleet whose files could not be read.
That is the argument for `T02` existing separately from `T03`. Had the reset
applied on first run, it would have retired live work in at least five
repositories, and every one of those retirements would have looked like tidy-up.
repositories, and every one of those retirements would have looked like tidy-up.
## First fleet-wide pass (2026-08-26)
121 repositories, refuse mode. The first measured answer to whether the hub
matches the forge.
| | |
|---|---|
| applied | 91 — 737 updated, 8 created |
| refused | 16 repositories, **64 records** |
| noop | 2 |
| errored | 12 |
**745 workplans now carry the commit they derived from.** `ADR-012` decision 2
is satisfied for the first time: until today no record could name its source, and
`git_fingerprint` had held the initial commit since the repository began. Zero
records were retired and hub-native progress events were untouched, as intended.
**64 is the real size of the stale-row problem** that `CUST-WP-0068-T09` has been
waiting on — measured rather than estimated. `railiance-platform` (20),
`vergabe-teilnahme` (16) and `railiance-apps` (8) hold more than two thirds.
### Private repositories are invisible to central
Eleven of the twelve errors are the same:
```text
fatal: could not read Username for 'https://forgejo.coulomb.social'
```
Those repositories are private, and the pod clones anonymously. `rapp-core-hub`,
`rapp-issue-core`, `rapp-openbao`, `rapp-policy-nexus` and seven others cannot be
derived at all.
This is a genuine limit on `ADR-012` decision 1: *the forge is the projection
source* holds only for repositories central can read. Until it has a deploy token
or equivalent, a whole class of repositories can never be reset — and, worse,
their absence looks like an error rather than a policy, so nothing distinguishes
"cannot read" from "does not exist".
### A second collision dimension
`disaster-control` raised `IntegrityError: Key (slug)=(repo-wp-0001) already
exists`. The identifier refusal added earlier checks `id`; `slug` carries its own
unique constraint across the whole table, so two repositories can derive
different identifiers whose slugs still collide. Now refused with the holder
named, and covered by test.