Files
msd-core/gsd-core/references/debugger-prevention.md
Tom Boucher c67f301867 feat(#1963): emit blameless-postmortem Prevention block at resolution (#2410)
* test(#1963): add failing-first prevention/postmortem contract tests

Epic #1957 Phase 3B. Source-text-is-the-product contract tests: blameless
5-Whys that BRANCHES per Phase 2A RCA (not a single-cause chain; treats agent
error as 'why was that possible?'), the 'why wasn't this caught?' question,
the recurrence-guard taxonomy (regression test / assertion / lint rule / KB
pattern), the KB-entry why_not_caught + recurrence_guard fields with backward
compat, the session-manager prevention summary line, and the Zawinski
scope-boundary (a block, not a subsystem).

Failing-first: reference, archive_session edit, KB schema extension, and
session-manager summary do not yet exist.

* feat(#1963): emit blameless-postmortem Prevention block at resolution

Epic #1957 Phase 3B. At archive_session the debugger now produces a
Prevention block with three blame-free components: a branching 5-Whys causal
chain (branches per Phase 2A RCA, not a single chain; 'agent error' prompts
'why was that possible?', never blame), a 'why wasn't this caught?' answer
naming the missed gate (test/typecheck/lint/review/verify), and a concrete
recurrence guard (regression test / assertion / lint rule / KB pattern).

The knowledge-base entry gains two structured fields (why_not_caught +
recurrence_guard) so future Phase-0 recall surfaces the prior prevention, not
just the prior fix. Additive: old entries without the fields still load. The
session-manager compact summary surfaces a one-line prevention summary.

Full rules extracted to gsd-core/references/debugger-prevention.md (slim
archive_session step + 2 KB fields kept in the agent). INVENTORY + manifest +
agent-size baseline + install-parity goldens + AGENTS.md updated.

* fix(#1963): address orthogonal review (CRITICAL append-template drift + Phase-0 consumption + parity test)

- CRITICAL: the archive_session KB append template omitted Why not caught +
  Recurrence guard (only the Entry Format had them) — the feature's core
  deliverable silently did not happen. Added both fields to the append template
  the agent actually follows (nearest-instruction wins).
- HIGH: Phase 0 (KB read) only surfaced root_cause + fix; the new fields were
  dead data. Extended the Phase 0 Evidence line to consume why_not_caught +
  recurrence_guard when present (absent on old entries — backward compat holds).
- MEDIUM: added a cross-section parity test (every Entry-Format field must also
  appear in the append template — the guard that would have caught the
  Critical) + a Phase-0-consumption assertion.
- MEDIUM: the 'branches per Phase 2A' claim is now wired — reuses
  reasoning_checkpoint.candidate_causes across the four categories.
- MEDIUM: recurrence-guard taxonomy gains type refinement + config-default
  change; LOW: added 'build' gate to both surfaces for parity.
- NIT: compact-summary fallback shape ('no gate existed'); verify the guard
  artifact exists before recording it.

* test(#1963): anchor Phase-0 consumption test on the specific heading

The regex /Phase 0[\s\S]{0,1200}/ matched the first 'Phase 0' in the file
(in knowledge_base_protocol prose), not the Phase 0 block in investigation_loop.
Anchor on '**Phase 0: Check knowledge base**' and widen to 1500 chars.

* chore(#1963): backfill changeset pr number (PR #2410)
2026-07-18 17:38:54 -04:00

5.1 KiB

Prevention / Blameless-Postmortem Output

Loaded by gsd-debugger via @-include from archive_session. Emits the forward-looking half of a resolved debug session — not just what was wrong and the fix, but why it happened, why it wasn't caught, and the guard that prevents its whole class from returning.

Why this exists

When a session resolves, the debugger records root_cause + fix and appends a keyword entry to the knowledge base. What it did not produce is the forward-looking half that industry incident practice (Google SRE Book, AWS COE) treats as the whole point: a blameless postmortem / Correction of Error. The bug gets fixed; the class of bug and the reason it slipped through are never captured — so the same class recurs and no guardrail is added. This block closes that gap. It reuses the existing debug file and knowledge base; it does not add a new command or workflow.

The Prevention block (three blame-free components)

At archive_session, after the fix is confirmed, emit a Prevention block and fold its two structured fields (why_not_caught, recurrence_guard) into the knowledge-base entry.

1. Blameless 5-Whys that BRANCHES (per Phase 2A RCA)

A causal chain — but branch across ≥2 Ishikawa categories, do not collapse to a single linear "why" (the same single-cause bias Phase 2A guards the diagnosis against applies to the postmortem). For each branch ask "why" until you reach an actionable condition.

Reuse the diagnosis branches: the reasoning_checkpoint.candidate_causes recorded at Phase 2A already enumerated the candidate causes across the four categories (code / config / environment / data — see debugger-rca-branching.md); start the postmortem from those branches and the AND-gate answer rather than re-deriving a chain from scratch.

Blame-free: treat "agent error" / "human error" as a prompt for "why was that error possible?" — not a terminal cause. A postmortem that stops at "the engineer made a mistake" prevents nothing; one that asks "why was the mistake possible / not caught" produces a guard. Never assign blame to a person.

2. "Why wasn't this caught?"

Name the existing gate that should have caught this bug class and didn't — a test, a type check, a lint rule, code review, the verify step, the build. If the honest answer is "no gate existed for this class," that itself is the finding (and the recurrence guard below is "add the gate").

3. The recurrence guard

The concrete artifact that prevents this class from returning. Choose the strongest applicable:

  • a regression test (already produced by Test-First Debugging — reference it),
  • an assertion / precondition (fail loud at runtime if the bad condition recurs),
  • a type refinement (make the bad state unrepresentable — the strongest guard in a typed codebase; e.g., a branded type / exhaustive union that rules out the invalid value at compile time),
  • a config-default change (eliminate the misconfiguration that enabled the bug — flip the default so the unsafe path is opt-in, not the path of least resistance),
  • a lint rule / broken-window ledger entry (fail the build / surface in review),
  • a knowledge-base pattern — this very entry, so a future Phase-0 recall surfaces the prior guard when a similar symptom appears.

State the guard concretely (which file, which rule, which test name) — not "add a test" but "the regression test at tests/foo.test.cjs:42 now covers this class." Verify the artifact exists before recording it (the test passes, the type compiles, the lint rule is registered) — a stale or unverified path is worse than none, since a future Phase-0 match would surface it as if it were real.

Knowledge-base entry: the two structured fields

The KB entry gains two fields (additive — see backward-compat below):

  • Why not caught: {the existing gate that should have caught it, or "no gate existed for this class"}
  • Recurrence guard: {the concrete artifact — regression test / assertion / lint rule / KB pattern — with its location}

These ride alongside the existing Error patterns / Root cause(s) / Fix / Files changed fields so a future Phase-0 match surfaces not just the prior fix but the prior prevention.

Backward compatibility (additive — no format break)

Old knowledge-base entries without why_not_caught / recurrence_guard still load unchanged. The matcher reads the Error patterns field (which every entry has); the two new fields are consumed when present and ignored when absent. A knowledge base with a mix of old and new entries works correctly — there is no migration, no schema version bump.

Scope boundary (Zawinski's Law)

A block, not an incident-management subsystem. It reuses the existing debug file's archive_session step and the existing knowledge base; it adds two fields and three prompt-level questions. It is not a new command, not a reporting framework, not a metrics pipeline. Where a bug is trivial and the postmortem would add nothing, a one-line recurrence guard suffices — the discipline scales down.