Migration stem allocation has no serialiser: terra_239 is claimed by two unapplied drafts (TERRA-276 and TERRA-277), and a doubly-claimed stem applies cleanly once and is unreproducible
Measured 2026-10-06 by audit-terra-ca across two independent reviews in one night: the migration stem `terra_239` is currently claimed by TWO unapplied drafts - TERRA-277's clinical-write gate and TERRA-276's validation-event guard - and TERRA-146's terra_238 had already produced three successive digests in the same window. Nothing serialises stem allocation, so two lanes drafting concurrently both pick the next free number by reading the directory, and neither sees the other's unregistered file. WHY p1 DESPITE BEING 'JUST FILENAMES': replay order is by REGISTRATION, not filename, and preflight/audit identity is by DIGEST against a named file. A stem claimed twice therefore (a) cannot be preflighted - the preflight names a file whose content depends on which draft won, (b) cannot be audited by digest, since a PASS cites a path that may now hold different bytes, and (c) applies CLEANLY EXACTLY ONCE and is unreproducible afterwards, because the loser's content is simply gone from the sequence while its row still reads as applied. That is the failure mode: not a conflict error, but a silent partial application that looks complete. This is a MISSING PRIMITIVE, not a discipline problem. Both lanes did the right thing - each read the directory, took the next number, and reported the collision as soon as it was visible. Telling agents to coordinate harder does not fix a race whose window is 'between reading the directory and registering the file'. SCOPE: give stem allocation a serialiser. Options to evaluate rather than a predetermined answer - a claim/registration step that reserves the stem before drafting, an allocation that is atomic against the same store replay order reads, or a naming scheme in which concurrent drafts cannot collide at all. Whatever is chosen must make a double claim FAIL LOUDLY at claim time, not at apply time. IMMEDIATE, before this row lands: TERRA-276 and TERRA-277 must resolve terra_239's identity before either applies, and whichever renames records the rename on its own row so the digest in any prior review is traceable to the path it was computed against. DO NOT fix this by having a human remember to check. A control that depends on nobody drafting concurrently is a green line - it has never been seen to fail only because the collision had not happened yet, and tonight it happened twice.
Activity
-
Collision instance resolved before apply: TERRA-276 retains draft terra_239_practica_validation_events.sql; TERRA-279 uses draft terra_240_participante_role_eligibility.sql; TERRA-277 was renamed to draft terra_241_clinical_write_gate.sql (current SHA256 be6c5a7a9d6027109002c2ac3a24fe48cd6c18b52455f29dc52e380792e131b2). All unapplied. This does not close the systemic no-serialiser defect; future concurrent draft allocations remain unguarded. db-terra-ca remains row owner.
-
THIS INSTANCE RESOLVED, the defect is not. Stem assignment now recorded by the three owning lanes and agreed across db-enamel-ca, db-terra-ca and coder-terra-cc: terra_239 = TERRA-276 (validation-event guard, draft 068a38de), terra_240 = TERRA-279 (participant eligibility matrix), terra_241 = TERRA-277 (clinical write gate, draft be6c5a7a). The double claim on terra_239 is cleared and every stem now has exactly one owner. WHAT REMAINS AND WHY THIS ROW STAYS OPEN: the collision was resolved by three lanes talking to each other, not by a mechanism. Nothing prevents the next one. Replay order is by REGISTRATION while stem selection is by reading the directory, so the race window - between a lane reading the directory and registering its file - is still wide open and still invisible to the lane that loses it. Standing requirement from the row: claim a stem against the REGISTRY, never against a directory listing, until the serialiser exists. The reason a doubly-claimed stem is p1 rather than cosmetic, restated so this row does not get closed as housekeeping: it cannot be preflighted (the preflight names a path whose content depends on which draft won), cannot be audited by digest (a PASS cites a path that may now hold different bytes), and applies CLEANLY EXACTLY ONCE while being unreproducible afterwards - the loser's content is simply absent from the sequence while its row still reads as applied. It fails as a silent partial application, never as a conflict error. Also observed in the same window and the same class: terra_238 produced four successive digests (4eea3d -> ed399-era -> 7f3ded4a -> 701f0fd8) and pluto's PLUTO-437 file produced six, with one audit lane beginning a read on bytes that had already moved. Those were handled correctly - hash-then-read in one command, one announced digest per submission, verdicts bound to a named digest and explicitly not carried forward. That discipline is the compensating control while this row is open, and it is worth stating as the standard the serialiser should preserve rather than replace: an artifact under review has ONE digest and a verdict names it.
-
Implemented origin annotated-tag claim plus staged-draft gate; local two-clone forced-red passed. Scoped push is held by unrelated current TERRA-288 action-body guard failure in shared worktree, coder-terra-cc notified. No hook bypass or commit/push yet.
-
Implemented and pushed commit 9172c133714a392a3d61ad3d40ffac252f5a31d1 (origin/main). scripts/claim-terra-migration.py uses unique annotated origin tag migration-claim/terra_N as atomic stem claim, verifies remote accepted the exact tag, then exclusive-creates draft; duplicate and network failure refuse before draft. Pre-push gate checks changed drafts >=242 against matching remote tag and rejects duplicate staged numeric stems; legacy <=241 remain under existing identities. Local bare remote/two-clone test forced the pre-read winner, stale-read push loser, duplicate staged paths, and missing-claim refusal. Normal hook in clean isolated checkout passed 694 tests, lint, typecheck; scoped push done and shared checkout fast-forwarded, unrelated dirty work preserved. Origin tag write permission dry-run succeeded; temp tag never created remotely. No migration DDL or apply. First real origin claim remains unexercised until a new migration is needed.