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
4.7 KiB
key, title, status, since, supersedes, superseded-by, rule, signals, mechanics
| key | title | status | since | supersedes | superseded-by | rule | signals | mechanics |
|---|---|---|---|---|---|---|---|---|
| startup.parallel-orientation | 2026-07-21 — Parallel orientation + selection is the startup protocol; #237 retired (#520) | active | 2026-07-21 | docs.queue-state-gitea-tracker@2026-07-11 | none | A fresh session runs two concurrent tracks at startup — Orientation (`AGENTS.md`/`CLAUDE.md` → `docs/README.md` task-signal map → the active decisions catalog `docs/decisions/README.md`) and, only when no issue is named, Selection (`scripts/select-queue.sh N`, deterministic live-Gitea ranking). A named issue skips Selection entirely. ersatztv#237, the closed pickup tracker this replaces, is reduced to a single archival breadcrumb and MUST NOT be read for live state. | startup protocol, queue selection, MemPalace retrieval, breadcrumb rule · paths: `docs/handoffs/chicorytv-issue-queue.md`, `scripts/select-queue.sh`, `docs/README.md`, `scripts/check-kickoff-guard.sh` · issues: #237, #520, #521, server-management#642 | `docs/handoffs/chicorytv-issue-queue.md` → "Two concurrent tracks at session start" + "Knowledge retrieval"; `scripts/select-queue.sh`; regression guard `scripts/check-kickoff-guard.sh` (wired into the `decisions lifecycle` CI job) |
Why this reverses docs.queue-state-gitea-tracker (2026-07-11), not just extends it. That
record's rule was "queue state lives in pinned tracker #237, not in the handoff file" — sound in
2026-07-11 when the arc was live and #237's body/comments were the only concurrency-safe place to
put session state. Two things changed: (1) scripts/select-queue.sh (2026-07-19) already replaced
the mechanical selection logic with a deterministic query over live Gitea state — no script reads
#237's prose; and (2) the arc itself completed and #237 closed on 2026-07-13. A closed issue
cannot be "where live queue state lives" — continuing to point agents at it was no longer just
stale, it was actively wrong, and the old record's own text ("Pinned tracker issue #237 holds the
goal + ordered arc") was live regression bait: a session reading it verbatim would try to treat a
closed tracker as authoritative. So this is a genuine reversal (the store changes from "a specific
Gitea issue" to "live Gitea queries, no pinned issue required"), not a mechanical follow-on.
What the new protocol is. Session start runs two independent tracks, neither blocking the other:
Orientation — read AGENTS.md/CLAUDE.md, then docs/README.md's task-signal map (replacing
the old mandatory 1–10 reading order), then the active decisions catalog docs/decisions/README.md
for "why do we do X" questions. Selection — only when the user hasn't named an issue, run
scripts/select-queue.sh [N], which excludes in-progress/parked/PRs, resolves
GET /issues/{n}/dependencies, tiers by LOCAL .milestone.state/review/priority: filters, and
orders deterministically; it flags CLAIM?/UMBRELLA? for the orchestrator to resolve by reading
the flagged issue, and does not rank an arc tier (none is active in maintenance/backlog mode — if
one is ever re-established, extend the script, never revert to prose-derived ranking). A
user-named issue skips Selection outright and goes straight to focused retrieval on that issue.
MemPalace retrieval contract (server-management#642, the sole Gitea→MemPalace exporter).
MemPalace is candidate discovery only, verified against its cited source before use, at a default
ErsatzTV-Decisions wing; the four load-bearing orientation bullets — catalog-first for current
rules, issue history as evidence not authority, the breadcrumb rule (a file path in a historical
comment resolves by concept via the active wing, never by literal path-chase), and the
catalog→rg exact-search fallback when MemPalace is stale/down — are carried verbatim in
docs/README.md and docs/handoffs/chicorytv-issue-queue.md. Never derive live queue state from
MemPalace, #237, or historical comments; live Gitea state via scripts/select-queue.sh is the only
source of truth for pickup ordering. The ## Closing record template (Outcome / Root cause /
Decisions changed / Reusable knowledge / Verification / Deferred / Docs updated) is the structured
per-issue artifact this retrieval contract is built to consume — see CLAUDE.md → Task Completion
Protocol and the kickoff's "Closing record" section.
Regression guard. scripts/check-kickoff-guard.sh greps the standing startup docs
(docs/handoffs/chicorytv-issue-queue.md, docs/README.md, CLAUDE.md, scripts/select-queue.sh)
for #237-as-live-state phrasings ("read #237", "tracker #237", "queue state lives in", …), exempting
lines/sections that are explicitly archival. It is wired into the decisions lifecycle CI job so a
future PR cannot silently reintroduce the reversed rule.