feat(tasks): give task rows a canonical record identifier
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

Every work-record type carried a stable identifier except tasks, whose rows held
only id, workplan_id, title, status and priority — nothing connecting a row to
CUST-WP-0067-T01 in the file it came from. Matching was therefore by title, so a
renamed heading looked like one task vanishing and another appearing, and the
forge-derived reset had to refuse to touch tasks at all.

Adds tasks.record_id (nullable: no migration can invent an identity for an
existing row) and a backfill that reads the pairing from the repository files,
where a task declares both its canonical id and its projection UUID. 5516 pairs
across 121 repositories with zero conflicts; 4456 of 6073 cache task rows
identified.

Diff and reset now key on record_id where present, falling back to a
title-prefixed key so an unidentified row stays visibly unidentified.

Unknown stays unknown: a row the files do not claim keeps no identity and the
reset keeps refusing to act on it, and an existing identity is never
overwritten — a mismatch is recorded as a conflict rather than resolved.

Refs STATE-WP-0083-T06

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 02:05:51 +02:00
parent 43ffe883c3
commit 8b207a991a
6 changed files with 325 additions and 6 deletions

View file

@ -219,7 +219,7 @@ filesystem.
```task
id: STATE-WP-0083-T06
status: todo
status: progress
priority: high
```
@ -239,6 +239,35 @@ Acceptance: task rows carry their canonical record id; the task diff for
`whitehat-security` reports clean; `the-custodian`'s task diff falls to something
manual inspection confirms.
**Built (2026-08-26).** Migration `e2b3c4d5f6a7` adds `tasks.record_id`,
nullable because no migration can invent an identity for an existing row — only
the repository files hold the mapping.
The backfill *reads* that mapping rather than inferring it: a file task declares
both its canonical id and the projection UUID it was registered under, so the
pairing is stated, not guessed. Across 121 repositories and 1104 files it
recovered **5516 pairs with zero conflicts**, and identified **4456 of 6073**
task rows in the cache database.
Diff and reset now key on `record_id` where present, falling back to a
`title:`-prefixed key so an unidentified row is *visibly* unidentified rather
than silently title-matched.
Two refusals are tested rather than documented: a row the files do not claim
keeps no identity, and a row that already has one is never overwritten — a
mismatch is recorded as a conflict, not resolved. Title matching would have
"worked" and destroyed a record every time someone edited a heading.
**Remaining, in order.** Deploy the migration to central; then run the backfill
against central as an explicit operation — deliberately *not* inside the Helm
hook, where a partial failure would silently leave half the tasks identified.
The cache matched 4456 of 6073 and central has a different history, so the
numbers will differ and a dry run should be compared before applying.
Only then may `T03` be extended to tasks of existing workplans, which is a
further change with its own verification. `CUST-WP-0068-T09` is therefore two
moves away, not one.
## Restore a migration mechanism for central