fix(projection): retirement must release the identifier it holds
`slug` is unique across the whole table, so retiring a row by timestamp alone leaves its identifier locked to a record nothing derives any more — and the repository that legitimately owns it can never claim it. core-hub's inherited REPO-WP-0001/0002 rows retired cleanly and repo-seed stayed refused with "slug already belongs to another repository", pointing at two retired rows. Retirement now stamps the slug `<slug>@retired-YYYYMMDD`. The row, its UUID and its progress events are untouched, so history stays attached to the record it happened under; only the human-facing identifier is freed. Re-retiring an already-stamped row replaces the mark rather than stacking it, or the 100-char column overflows after a few passes. The UUIDs never collided here: core-hub's rows predate derived identity and carry v4 UUIDs, while repo-seed derives v5 ones. The identifier was the only thing in the way. 733 pass. 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
4b121f3796
commit
70c585bb39
3 changed files with 60 additions and 11 deletions
|
|
@ -585,6 +585,28 @@ class ResetOutcome:
|
|||
}
|
||||
|
||||
|
||||
RETIRED_SLUG_MARK = "@retired-"
|
||||
|
||||
|
||||
def _tombstone_slug(slug: str, when: datetime) -> str:
|
||||
"""Release the identifier a retired row was holding.
|
||||
|
||||
`slug` is unique across the whole table, so retirement that only sets a
|
||||
timestamp leaves the identifier locked to a record nothing derives any
|
||||
more — and the repository that legitimately owns it can never claim it.
|
||||
That is what kept repo-seed refused after core-hub's inherited REPO-WP rows
|
||||
were retired.
|
||||
|
||||
The row, its UUID, and its progress events are untouched; only the
|
||||
human-facing identifier is stamped, so history stays attached to the record
|
||||
it happened under. Re-retiring an already-stamped row must not stack marks,
|
||||
or the column overflows after a few passes.
|
||||
"""
|
||||
base = (slug or "").split(RETIRED_SLUG_MARK)[0]
|
||||
stamped = f"{base}{RETIRED_SLUG_MARK}{when:%Y%m%d}"
|
||||
return stamped[:100]
|
||||
|
||||
|
||||
RETIRE_REASON = "no longer derived from the forge"
|
||||
|
||||
|
||||
|
|
@ -864,9 +886,10 @@ async def reset_repository_projection(
|
|||
outcome.updated.append(w.record_id)
|
||||
|
||||
for r in stale:
|
||||
outcome.retired.append(r.slug or str(r.id))
|
||||
r.projection_retired_at = now
|
||||
r.projection_retired_reason = RETIRE_REASON
|
||||
outcome.retired.append(r.slug or str(r.id))
|
||||
r.slug = _tombstone_slug(r.slug or str(r.id), now)
|
||||
|
||||
if outcome.created or outcome.updated or outcome.retired:
|
||||
outcome.status = "applied"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue