diff --git a/.project/cards/T-0223-only-cards-notice-that-a-retitled-record-s-filenam.md b/.project/cards/T-0223-only-cards-notice-that-a-retitled-record-s-filenam.md new file mode 100644 index 0000000..d8e69b0 --- /dev/null +++ b/.project/cards/T-0223-only-cards-notice-that-a-retitled-record-s-filenam.md @@ -0,0 +1,28 @@ +--- +id: T-0223 +title: Only cards notice that a retitled record's filename went stale +status: backlog +type: bug +priority: low +area: core +tags: [health] +effort: S +scope: [packages/workfile/src/modules/health] +origin: [LRN-0033] +created: 2026-08-07 +updated: 2026-08-07 +--- + +`diagnoseCards` reports `filename-stale` when a card's filename no longer matches its title, and `doctor --fix` renames it. The comment there states the reason plainly: the filename is the handle people and agents grep by, and a stale one misdirects long after anyone remembers the rename. + +Memory records, documents and changelog fragments derive their filenames from their titles in exactly the same way, and none of them has the rule. So retitling a learning through `memory patch` leaves a file named after a title the record no longer has, and nothing reports it — found by doing it: LRN-0033 was retitled and sat under `LRN-0033-a-card-outlives-the-decision-it-was-filed-under-...` with `doctor` reporting 0 errors and 0 warnings. It was renamed by hand, which is the only repair available. + +Worth doing as one pass rather than per collection, and worth doing at all for the same reason it was worth doing for cards: the drift is invisible, it accumulates on exactly the records that got the most attention, and `--fix` already exists as the shape of the repair. + +## Acceptance criteria + +- [ ] `doctor` reports a stale filename for a memory record, a managed document and a changelog fragment, as a warning, the way it does for a card. +- [ ] `doctor --fix` renames them, and a record whose id is referenced elsewhere keeps resolving. +- [ ] The rule is written once rather than per collection. +- [ ] A record whose filename is stale because it was renamed *by hand* to something legitimate is not fought over — state what happens. +- [ ] `pnpm run check` green, doctor 0/0. diff --git a/.project/memory/learnings/LRN-0033-a-card-outlives-the-decision-it-was-filed-under-and-its-mechanics-go-s.md b/.project/memory/learnings/LRN-0033-a-card-outlives-the-decision-it-was-filed-under-and-its-mechanics-go-s.md deleted file mode 100644 index 1abacf7..0000000 --- a/.project/memory/learnings/LRN-0033-a-card-outlives-the-decision-it-was-filed-under-and-its-mechanics-go-s.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -id: LRN-0033 -title: A card outlives the decision it was filed under, and its mechanics go stale in silence -status: active -confidence: high -related: [ADR-0018, LRN-0027, T-0207] -tags: [ui, process] -created: 2026-08-07 -updated: 2026-08-07 ---- - -T-0207 was filed as ADR-0017's surviving finding. ADR-0018 then superseded ADR-0017 and moved the layout: docs and history became views that own their readers, so the shared drawer stopped opening for either. The card was never touched, and it did not have to be wrong to be misleading — every sentence in it had been true when written. - -What it still said was that `RecordPanel` serves "decisions, learnings, incidents, conventions and documents opened from outside the docs view". It serves none of those: memory records get `MemoryPanel`, documents get `DocPanel`, and `RecordPanel` is the fallback for changelog fragments and releases. So the card's scope named one file, `RecordPanel.tsx`, and a fix that touched only that file would have satisfied the card, passed review, and left serial reading in history — the complaint the whole chain started from — exactly as broken as it was. - -The superseding record even flagged this. ADR-0018 has a "What survives from ADR-0017" section naming T-0207 and saying it "stands on its own". It does stand, but it stands on the old geometry: what survived was the *finding*, not the mechanics the card wrote down around it. - -**Why:** a card is a snapshot of an understanding, and a decision record is the thing that changes understandings. Superseding an ADR updates the decision graph and leaves the cards that cite it describing a codebase that has moved. Nothing reports that, because nothing is broken: the links resolve, `doctor` is clean, and the prose is internally consistent. - -**How to apply:** when a card's `origin` or `related` names a decision that is now `superseded`, re-read the card against the superseding one before starting — the finding usually survives and the mechanics usually do not. Treat its `scope` as the weakest part: it was written from the old arrangement and it is what silently narrows the fix. Then do what [[LRN-0027]] says and open the view, which is the only thing that settles which panel is actually on screen. Correct the premise on the card rather than quietly working around it, so the next reader inherits the correction instead of the snapshot. diff --git a/.project/memory/learnings/LRN-0033-a-cards-finding-is-evidence-its-scope-and-its-stated-fix-are-a-guess.md b/.project/memory/learnings/LRN-0033-a-cards-finding-is-evidence-its-scope-and-its-stated-fix-are-a-guess.md new file mode 100644 index 0000000..bdd9d0c --- /dev/null +++ b/.project/memory/learnings/LRN-0033-a-cards-finding-is-evidence-its-scope-and-its-stated-fix-are-a-guess.md @@ -0,0 +1,23 @@ +--- +id: LRN-0033 +title: A card's finding is evidence; its scope and its stated fix are a guess +status: active +confidence: high +related: [ADR-0018, LRN-0027, T-0207, T-0202, T-0161] +tags: [process, acceptance] +created: 2026-08-07 +updated: 2026-08-07 +occurrences: 3 +--- + +Three cards in two days, and only the first of them involved a superseded decision. What they share is not why the card went stale but which part of it did. + +**T-0207 — the decision moved under it.** Filed as ADR-0017's surviving finding; ADR-0018 then superseded ADR-0017 and gave docs and history their own readers, so the shared drawer stopped opening for either. The card was never touched and did not have to be wrong to mislead: every sentence had been true when written. It still said `RecordPanel` serves "decisions, learnings, incidents, conventions and documents opened from outside the docs view". It serves none of those. So its `scope` named one file, and a fix touching only that file would have satisfied all five criteria, passed review, and left serial reading in history — the complaint the whole chain started from — exactly as broken. ADR-0018 even flagged it, with a section saying T-0207 "stands on its own": what survived was the *finding*, not the mechanics written around it. + +**T-0202 — the enumeration was of what the author had found.** Its scope named the three record list endpoints that matched a query by substring. There was a fourth site with a rule of its own, the command palette, and it was the worst of the four: in the hosted demo it could not find a record by a word in its body at all. Nothing had superseded anything. The list was just short. + +**T-0161 — the stated mechanism was never run.** "One branch in `validateCardCandidate`... `candidate.id` is set by the time it runs — that is how the existing self-parent check catches the same case on creation." Both halves false. Creation validates against `id: "pending"` and the allocation decides the id later, under a lock; a self `parent` on create is refused by `CARD_PARENT_NOT_FOUND`, the right outcome for the wrong reason. Writing the branch the card asked for left the reported bug reproducing exactly as filed. + +**Why:** a card's *finding* is evidence — somebody hit it. Its *scope* and its *account of the fix* are a hypothesis, written before the work by someone who had not done it, and they are what fails. Nothing reports it, because nothing is broken: the links resolve, `doctor` is clean, and the prose is internally consistent. The failure mode is not a card that looks wrong; it is a card that looks finishable and whose criteria can all be met while the reported defect survives. + +**How to apply:** read the finding as a claim about the world and the scope as the author's guess. Before starting, reproduce the reported behaviour — not the mechanism, the behaviour — and count the sites yourself rather than trusting the enumeration. If the card names a decision, check whether it is still `accepted`. Then, when the guess turns out wrong, correct it on the card rather than quietly working around it, so the next reader inherits the correction instead of the snapshot. [[LRN-0027]] is the same lesson for anything visual, and its instruction generalises: reading the code is not running it either.