fix(projection): refuse slug collisions, and record the first fleet pass
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:
parent
5d09e8f5b0
commit
6c1262ef6e
3 changed files with 119 additions and 3 deletions
|
|
@ -510,6 +510,42 @@ async def reset_repository_projection(
|
|||
)
|
||||
).scalars()
|
||||
)
|
||||
# `slug` carries its own unique constraint across the whole table, so an
|
||||
# identifier check alone is not enough: two repositories can derive
|
||||
# different identifiers whose slugs still collide. Missing this is what
|
||||
# left disaster-control raising IntegrityError after the identifier
|
||||
# refusal was added.
|
||||
slug_clash = list(
|
||||
(
|
||||
await session.execute(
|
||||
select(Workplan).where(
|
||||
Workplan.slug.in_([w.record_id.lower() for w in creating]),
|
||||
Workplan.repo_id != repo.id,
|
||||
)
|
||||
)
|
||||
).scalars()
|
||||
)
|
||||
if slug_clash:
|
||||
held = {(r.slug or "").lower(): r for r in slug_clash}
|
||||
outcome.status = "refused"
|
||||
for w in creating:
|
||||
row = held.get(w.record_id.lower())
|
||||
if row is None:
|
||||
continue
|
||||
outcome.refused.append(
|
||||
{
|
||||
"reason": "slug already belongs to another repository",
|
||||
"record_id": w.record_id,
|
||||
"slug": w.record_id.lower(),
|
||||
"held_by_id": str(row.id),
|
||||
}
|
||||
)
|
||||
outcome.notes.append(
|
||||
"Identifier collision is an identity decision, not a projection "
|
||||
"one; acknowledging retirements does not authorise it."
|
||||
)
|
||||
return outcome
|
||||
|
||||
if foreign:
|
||||
owned = {str(r.id): r for r in foreign}
|
||||
outcome.status = "refused"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue