168 records -> docs/decisions/records/<area>/<topic>.md (163 active, 23 dirs) and docs/decisions/archive/<area>/<topic>.md (5 archived). The filename IS the key, so one-active-record-per-key becomes a filesystem property rather than a validator check, and supersession becomes a `git mv`. WHY: the monolith was a concurrency problem before an aesthetic one. A 3,900-line append target made parallel sessions collide -- PR #605 and PR #614 both hit append-vs-append conflicts during routine rebases, and hand-resolving those inside the corpus is exactly the operation the rationale-rewrite guard exists to police. HOW IT IS VERIFIED: a ~170-file diff cannot be meaningfully read, so correctness does not rest on reading it. The parser was taught BOTH formats first, so the body-diff guard parses the old form at the merge-base and the new form at head -- the migration validates itself, no bypass. The proof is a field-level equivalence harness: 168 records before and after, zero lost, zero gained, zero field mismatches, zero rationale bodies differing. Reviewers should scrutinise the harness; it is the actual evidence. What measuring caught that reading would not have: - ~500 lines sit OUTSIDE any record -- decisions.md's lifecycle schema and each topic file's preamble, mostly the only copy. Source files are kept and stripped, never deleted. They also cannot be filed per-area: topic files hold several areas and 4 of 23 areas span several files. - Archive discovery was a non-recursive glob; after the split it found ZERO archived records, surfacing as four bogus "supersedes points to unknown key" errors rather than an obvious failure. - ~32 live docs point into the corpus BY DATE, which the split dangles. Each stripped file now ends with a generated "Records formerly in this file" index, which also rescues the identical breadcrumbs in old issue comments. - decisions.md's "In this file:" list was 97 same-file anchor bullets that the split makes WRONG, not merely stale. Dropped; the generated index replaces them with links that resolve. The equivalence harness now runs against a checked-in FIXTURE, not the live corpus. The earlier version migrated the real tree, which made it a one-shot: the moment the migration landed there was nothing left to move and the tests failed for reasons unrelated to the code. A fixture keeps them testing the SCRIPT rather than the repo's current state. Keys preserved verbatim, warts included: `sched` (12) and `scheduling` (1) remain two directories for one concept. Renaming a key is not a move -- it changes identity, breaks the equivalence proof, and invalidates MemPalace's per-key drawers. Taxonomy normalisation is separate work. refs #610
2.6 KiB
key, title, status, since, supersedes, superseded-by, rule, signals, mechanics
| key | title | status | since | supersedes | superseded-by | rule | signals | mechanics |
|---|---|---|---|---|---|---|---|---|
| session.shared-checkout-refresh | 2026-07-21 — Session end fast-forwards the shared checkout; a stale tree serves stale FILES (#541) | active | 2026-07-21 | none | none | Session end runs `scripts/refresh-shared-checkout.sh`, which fast-forwards `/Users/timothy/ersatztv` to `origin/main` (and reinstalls `web/node_modules` when the lockfile moved), refusing to touch anything unless that tree is on a clean, non-ahead `main`. | shared checkout, stale kickoff paste, `/Users/timothy/ersatztv`, session-end protocol, H13 · paths: `scripts/refresh-shared-checkout.sh`, `docs/handoffs/chicorytv-issue-queue.md` · issues: #541, #520, #311, #312 | `docs/handoffs/chicorytv-issue-queue.md` → session-end step 6 + the shared-tree lore bullet |
The existing rule had a hole, and it is a hole no rule can close. The standing guidance — never
commit in the shared tree, never read its HEAD/git log/git status as truth about main — is
written entirely around a session reading git state. On 2026-07-21 the trap arrived as a file:
the kickoff prompt was pasted out of that tree while it was 81 commits behind, and the handoff doc it
carried still described the queue protocol #520 had retired the previous day (read tracker #237, which
main now says must not be read for queue state). No git command touched that tree all session, so
no discipline check could have fired. Selection happened to go through scripts/select-queue.sh, which
is why nothing broke — routing luck, not a control.
So the fix removes the stale condition rather than adding a check, which is what the shared-tree lore bullet already prescribed for its first two failure modes: "a check does not stay true."
The script is deliberately timid, because the tree is shared. It refuses — loudly, exit 0,
changing nothing — when the tree is not on main, is dirty, has local commits, or is mid-rebase or
mid-merge. It never switches branches, never stashes, never discards. A refusal is a normal outcome,
not a failure, because the common reason for one is that another session is legitimately mid-flight.
Two details that testing forced. The first version used npm install, which rewrote
package-lock.json and left the shared tree dirty — the exact state the next run refuses on, so the
tool would have disabled itself after one use. It uses npm ci, which installs strictly from the
lockfile and never writes it. And it asserts the tree is clean at exit, reporting loudly if not:
leaving the shared tree dirty is the one outcome that would make this script a net negative.