fix(projection): refuse a colliding identifier instead of failing on a constraint
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
This commit is contained in:
parent
8404ab32f4
commit
5e4d0be31c
2 changed files with 96 additions and 2 deletions
|
|
@ -492,6 +492,45 @@ async def reset_repository_projection(
|
|||
key = cand[0] if len(cand) == 1 else key
|
||||
matched[key] = row
|
||||
|
||||
# An identifier this repository would create may already belong to another
|
||||
# repository. Two repositories creating the same daily identifier on the
|
||||
# same day is a documented case (CUST-WP-0066), and the derivation is
|
||||
# deliberately deterministic, so the collision is real rather than
|
||||
# incidental. Refuse and say so: a constraint violation is a stack trace,
|
||||
# a refusal is something the caller can rule on.
|
||||
creating = [w for k, w in want.items() if k not in matched]
|
||||
if creating:
|
||||
foreign = list(
|
||||
(
|
||||
await session.execute(
|
||||
select(Workplan).where(
|
||||
Workplan.id.in_([uuid.UUID(w.uuid) for w in creating]),
|
||||
Workplan.repo_id != repo.id,
|
||||
)
|
||||
)
|
||||
).scalars()
|
||||
)
|
||||
if foreign:
|
||||
owned = {str(r.id): r for r in foreign}
|
||||
outcome.status = "refused"
|
||||
for w in creating:
|
||||
held = owned.get(w.uuid)
|
||||
if held is None:
|
||||
continue
|
||||
outcome.refused.append(
|
||||
{
|
||||
"reason": "derived identifier already belongs to another repository",
|
||||
"record_id": w.record_id,
|
||||
"uuid": w.uuid,
|
||||
"held_by_slug": held.slug,
|
||||
}
|
||||
)
|
||||
outcome.notes.append(
|
||||
"Identifier collision is an identity decision, not a projection "
|
||||
"one; acknowledging retirements does not authorise it."
|
||||
)
|
||||
return outcome
|
||||
|
||||
stale = [
|
||||
r for k, r in matched.items()
|
||||
if k not in want and r.projection_retired_at is None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue