* 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)
99 lines
5.1 KiB
Markdown
99 lines
5.1 KiB
Markdown
# 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.
|