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
3.3 KiB
key, title, status, since, supersedes, superseded-by, rule, signals, mechanics
| key | title | status | since | supersedes | superseded-by | rule | signals | mechanics |
|---|---|---|---|---|---|---|---|---|
| release.merge-consent-autogrant | 2026-07-12 — Merge-consent gate auto-grants when satisfied (no redundant prompt); state IS the consent (#314) | active | 2026-07-12 | none | none | When Done-when boxes are ticked, CI is green, and a fresh positive Review-verdict references head, the merge-consent hook emits `permissionDecision: allow` to actually suppress the redundant mechanical prompt — the derived state IS the consent, no separate conversational confirmation on that path. | auto-grant, permissionDecision allow, merge consent · paths: `.claude/settings.json` · issues: #314, #303, #317 | `pretooluse-merge-consent.sh`; CLAUDE.md → Task Completion Protocol |
Completes the #303 H6/H10 intent — derive merge-consent from state — which the original hook only
half-delivered. The rule the user set: merge permission is auto-granted for the session when the
linked issue's ## Done-when boxes are all ticked, a fresh positive Review-verdict references the
current head, and CI is green — no separate confirmation, conversational or mechanical.
Root cause of the bug this fixes: pretooluse-merge-consent.sh's satisfied path did a bare
exit 0. A PreToolUse hook that exits 0 with no JSON does not auto-approve — it only declines to
block, so control falls through to the normal permission system and the raw MCP permission prompt
still fires (the merge tool isn't allow-listed). So the gate only ever added a deny/ask net; it never
removed the baseline prompt on the happy path. Net effect for the operator: a ready-to-merge PR was
confirmed twice — once conversationally (the per-session merge-consent norm) and again by a redundant
mechanical prompt the gate was supposed to have subsumed.
Fix: ONLY the genuinely-satisfied merge path (a+b+c all true) now emits
{"hookSpecificOutput":{"permissionDecision":"allow", ...}} (a new grant decision), which actually
suppresses the prompt. Deny (unticked/red/negative/stale) and ask (non-derivable: no creds, Gitea
down, no linked issue, no ## Done-when, no verdict) are unchanged — the gate still fails closed, not
open. Two paths deliberately do not auto-grant and keep the bare exit 0 passthrough (normal
permissioning → one prompt): non-merge pull_request_write methods (auto-grant is scoped to
method=merge only), and the docs/process-only exemption. The exemption is a file-TYPE bypass, not
the a+b+c "provably reviewed & ready" proof, so it must not silently self-merge — critically, its set
includes .claude//.gitea//.husky/ (the gate, CI workflows, and git hooks themselves), so a PR
that weakens the gate still gets a human prompt (ersatztv#317 review nit). Verified by 8 pipe tests
(satisfied→allow, docs-only→passthrough, unticked→deny, stale→deny, red-CI→deny, no-verdict→ask,
no-creds→ask, non-merge→passthrough).
Process consequence: the state-derived gate is the consent on the satisfied path — do not also ask conversationally to merge a PR whose gate auto-grants. A separate human confirmation is still warranted only when the gate asks (state not derivable). This supersedes the "always confirm merge consent in-conversation per session" phrasing in the kickoff HARD CONSTRAINTS (updated in the same PR).