Skip to content

Instantly share code, notes, and snippets.

@aviadr1
Created March 26, 2026 21:58
Show Gist options
  • Select an option

  • Save aviadr1/4a978f56edd94aa9e1320bd77197ff80 to your computer and use it in GitHub Desktop.

Select an option

Save aviadr1/4a978f56edd94aa9e1320bd77197ff80 to your computer and use it in GitHub Desktop.
Planning Quality Research — Round 3: Synthesis (Category Library, Plan Battery, Information Architecture)

Round 3: Category Library and Hypothesis Formation Protocol

March 2026 — Synthesis layer


1. What a Category Is

A category is not a label. It is a predictive structure: a named pattern of problem-space geometry that allows an agent to load failure-specific priors before committing to a design.

Formally, a category has five components:

Category: <slug>
  recognition_signature:
    surface_features: [keywords, design shapes, API patterns]
    structural_test: "A question that returns YES/NO when applied to a design"
    precursor_signals: [early phrases in specs or issues that predict this category]
  failure_topology:
    modes: [named failure modes, each with trigger condition and observable effect]
    latency: immediate | deferred | session-boundary | cumulative
    visibility: always-visible | visible-on-crash | silent
  design_taxonomy:
    resilient: [named design shapes that survive all known failure modes]
    fragile: [named design shapes with documented failure mode associations]
    context_modifiers: [environmental factors that change which shape is resilient]
  invariants: [architectural rules that make this category's failures structurally impossible]
  incidents:
    - issue: <number>
      shape_that_failed: <slug>
      recovery_cost: low | medium | high
      recurrence: <count>
  confidence: 0.0–1.0  # derived from incident count and recency

The structural test is the critical component that makes a category mechanically usable. It is not "does this look like a state problem?" — it is "does this design store state in a variable whose lifetime is bounded by the session, to be consumed after the session ends?" A structural test with a YES/NO answer takes seconds to apply. A vague description takes minutes and produces uncertain answers.

The failure topology's latency and visibility fields address the second hardest problem in category recognition: many failure modes are invisible in the happy path and only surface at session boundaries or under load. The agent must know this before trusting a design that appears to work in tests.


2. The Starter Category Set for Kaizen

The following 10 categories are derived from kaizen's incident history (issues #713, #871, #895, #897, #901, #920, #939, #975), the policies in policies.md, and the failure patterns identified in Round 2.

Category: session-boundary-state Recognition signature: any design where state is accumulated in a variable or process memory to be written or consumed after a session boundary. Structural test: "If this process dies at step N of M, is progress from steps 1 through N-1 recoverable without re-execution?" Failure modes: crash-before-flush (data lost), partial-flush (partial data written, corrupt view), read-of-unwritten (consumer sees empty on restart). Latency: session-boundary. Visibility: silent (happy path always passes). Resilient shapes: write-through (agent writes immediately on completion), append-only-log (each agent appends; no batch). Fragile shapes: accumulate-then-flush (orchestrator collects, writes at end), end-of-phase-storage (write deferred to phase completion). Invariants: "All state that must survive session death is persisted atomically by the writing agent before any coordination signal is sent." Incidents: review-findings-loss (orchestrator batch pattern); related to issues from the review-fix work (#883, #896). Confidence: 0.85

Category: worktree-isolation Recognition signature: any design where hook, state file, or process reads/writes state without filtering by current worktree identity. Structural test: "If two agents run concurrently in different worktrees, can one agent's state affect the other's gate decisions?" Failure modes: cross-contamination (worktree A's gate blocks worktree B's work), false-positive-cascade (deleted worktree state poisons new sessions), stale-state-inheritance. Latency: deferred (appears only under concurrent use). Visibility: visible-on-crash. Resilient shapes: worktree-keyed-state (state files carry BRANCH field; readers filter by current branch). Fragile shapes: global-state-scan (iterate /tmp/.pr-review-state/ without branch filter). Invariants: "All state file iteration goes through isStateForCurrentWorktree; never iterate state directories directly." Incidents: #939 (worktree ephemeral lifecycle blindness), #871 (false positive MM status). Confidence: 0.90 (explicit policy exists, multiple incidents)

Category: gate-bypass-via-exit Recognition signature: any enforcement mechanism that fires between actions rather than at completion; any gate the actor can avoid by stopping. Structural test: "If the actor's next action is to exit (stop, close session, finish), does this gate still fire?" Failure modes: exit-before-enforcement (actor stops without triggering gate), silent-skip (gate never reached because workflow terminates early), continuation-assumption (gate assumes actor will keep going). Latency: deferred (appears only when actor exits early). Visibility: silent. Resilient shapes: completion-dependent-gate (fires when actor declares done), post-hoc-correction (harness runs after actor exits, outside actor's lifecycle). Fragile shapes: pretools-only-enforcement (gate fires before next tool use — bypassed by exit), inline-check (check inserted mid-sequence). Invariants: "Completion-critical policies enforce at Stop hooks or post-hoc harnesses, not PreToolUse alone." Incidents: #975 (pr-review-loop exits silently on null hook input), exit-before-enforcement pattern (#895, #966). Confidence: 0.80

Category: gate-false-positive Recognition signature: any hook or gate that matches on a pattern (grep, regex, keyword) where the pattern is shared between the target signal and unrelated legitimate text. Structural test: "Does this pattern match the empty case, the error case, or any legitimate non-target text in the codebase?" Failure modes: false-positive-block (legitimate work blocked), false-positive-cascade (CI breaks on unrelated PRs), pattern-boundary-bleed (matches substring of unrelated token). Latency: immediate. Visibility: always-visible (loudly blocks). Resilient shapes: anchored-regex (match at line boundaries, with word boundaries), negative-lookahead (exclude known false-positive contexts), context-aware-match (check surrounding lines). Fragile shapes: substring-grep (grep for bare string without anchoring), presence-only-check (any line contains X). Invariants: "Every hook pattern must have a test case for the false-positive scenario it is most likely to produce." Incidents: #871 (MM status false positive), KAIZEN_UNFINISHED false positive, #971 (ambiguous gate messages from 3 matchers). Confidence: 0.85

Category: coverage-without-behavior Recognition signature: any test suite that verifies mocks and structure but does not exercise the real execution path (end-to-end seam). Structural test: "If the implementation were completely wrong but returned the right type, would these tests still pass?" Failure modes: silent-wrong (implementation is incorrect; tests pass; production fails), mock-drift (real API changes; mocked API doesn't; tests pass but nothing works), seam-blindness (the gap between units is untested). Latency: deferred (appears when real system is exercised). Visibility: silent. Resilient shapes: boundary-integration-test (exercises real seam with real dependencies), behavior-verification (test asserts observable output, not internal call sequence). Fragile shapes: mock-heavy-unit (mock all collaborators; verify mock was called), structure-test (assert types and fields; never assert behavior). Invariants: "Every execution path has at least one test that exercises the full seam without mocking the seam itself." Incidents: #896 (review-fix wiring had zero unit test coverage), #928 (hook integration test category-level prevention). Confidence: 0.85

Category: schema-coupling Recognition signature: any design where a producer serializes and a consumer deserializes the same data structure, and schema changes in one are not automatically surfaced to the other. Structural test: "If I add a required field to the producer's output schema, does the build fail, or does it fail silently at runtime?" Failure modes: silent-deserialization-failure (consumer silently ignores unknown fields), required-field-break (new required field → all existing consumers fail at runtime), schema-drift (producer and consumer evolve independently, diverge). Latency: deferred (appears on schema change). Visibility: visible-on-crash. Resilient shapes: shared-schema-definition (single type definition imported by both), version-negotiation (producer includes schema version; consumer validates). Fragile shapes: independent-definition (producer and consumer define the same structure separately), field-ignore-by-convention (consumer ignores unknown fields silently). Invariants: "Shared data structures live in one location; both producer and consumer import from it." Incidents: No direct kaizen incidents yet; derived from r2-staff-engineer-model.md category library. Confidence: 0.50 (theoretical)

Category: late-testability-binding Recognition signature: any implementation where the wiring between components (main(), integration loop, CLI dispatch) is not injectable and therefore cannot be unit-tested without running the full runtime. Structural test: "Can I test this component's logic without starting the real server, real filesystem, or real external service?" Failure modes: test-post-hoc (tests written after bugs are discovered, not before), untestable-seam (wiring cannot be reached by any test), coverage-gap-discovered-in-production. Latency: cumulative (cost grows as bugs accumulate untested). Visibility: visible-on-crash. Resilient shapes: dependency-injection (collaborators passed as parameters), extracted-handler (command dispatch extracted from main(), testable in isolation). Fragile shapes: hardwired-main (main() calls implementations directly with no injection points), god-function (single function that initializes, executes, and coordinates). Invariants: "Every module that handles business logic exports a testable function separate from its entry point." Incidents: #883 (launchFix/prefetch not injectable), #922 (cli-structured-data main() refactor for testability). Confidence: 0.80

Category: skill-as-code-unverified Recognition signature: any change to a SKILL.md, prompt template, or workflow document deployed without behavioral proof (before/after claude -p evidence). Structural test: "Is there a passing test that would have caught the previous version's bad behavior?" Failure modes: silent-skill-regression (skill changes behavior invisibly; no test catches it), instruction-drift (skill instructions diverge from actual behavior; agents follow instructions, not behavior), verification-debt (behavioral proof deferred; never arrives). Latency: deferred. Visibility: silent. Resilient shapes: behavioral-proof-with-pr (synthetic case + before/after evidence ships with the PR), smoke-test-in-ci (CI runs skill smoke test on each PR). Fragile shapes: prose-change-only (skill text updated; no behavioral test), deferred-verification ("we'll test it later"). Invariants: "Skill changes require behavioral proof. Synthetic case + smoke test ship with the PR. Never deferred." Incidents: #963 (policy established), #906 (kaizen-write-pr not triggered). Confidence: 0.90 (explicit policy, critical priority)

Category: enforcement-level-mismatch Recognition signature: any fix applied at Level 1 (instructions/docs) for a failure mode that has already occurred once at Level 1. Structural test: "Has this specific failure mode occurred before? If yes, was the previous fix also at Level 1?" Failure modes: recurrence (same failure happens again because instructions are not followed), false-security (documented policy creates confidence that enforcement doesn't warrant), level-plateau (all fixes stay at L1; escalation never happens). Latency: deferred (appears at next recurrence). Visibility: silent. Resilient shapes: escalation-on-recurrence (second occurrence → L2 hook; third → L3 mechanic), hook-as-fix (enforcement point added, not just instruction updated). Fragile shapes: add-to-claudes-md (update instruction text; declare fixed), comment-the-code (add a comment explaining what not to do). Invariants: "If a failure has happened once, the fix is at minimum L2. If it affects humans, the fix is L3." Incidents: Structural invariant behind multiple kaizen reflections; no single issue but present as meta-pattern. Confidence: 0.90

Category: artifact-lifecycle-gap Recognition signature: any system that produces outputs (review findings, run artifacts, structured data) without a defined consumer, storage location, or lifecycle policy. Structural test: "If I run this 10 times, do the outputs accumulate? Is there a reader? Is there a retention policy?" Failure modes: findings-in-void (review written but never posted to PR), artifact-orphan (output file created but never read), retention-absent (outputs accumulate indefinitely). Latency: cumulative. Visibility: silent. Resilient shapes: write-then-confirm-read (writer verifies consumer exists), lifecycle-policy-at-creation (retention, reader, and format specified when artifact is created). Fragile shapes: produce-and-forget (write output; assume something will read it), lifecycle-deferred (we'll figure out retention later). Invariants: "Every artifact has a defined reader, a defined retention policy, and a defined failure mode if the reader is absent." Incidents: #966 (review findings not posted to PRs), #900 (closed issue with 3 acceptance criteria undelivered). Confidence: 0.75


3. The Recognition Algorithm

Given a new issue description or design spec, the agent determines which categories apply using a three-pass algorithm. Total target time: under 60 seconds.

Pass 1 — Keyword pre-filter (5 seconds)

Scan the issue/spec for terms that appear in any category's surface_features. Build a candidate set. This is deliberately over-inclusive: false positives are cheaper than false negatives here.

Keyword index (maintained in index.json):

{
  "session": ["session-boundary-state", "gate-bypass-via-exit"],
  "state": ["session-boundary-state", "worktree-isolation"],
  "worktree": ["worktree-isolation"],
  "grep": ["gate-false-positive"],
  "regex": ["gate-false-positive"],
  "test": ["coverage-without-behavior", "late-testability-binding"],
  "mock": ["coverage-without-behavior"],
  "schema": ["schema-coupling"],
  "SKILL.md": ["skill-as-code-unverified"],
  "instructions": ["enforcement-level-mismatch"],
  "findings": ["artifact-lifecycle-gap", "session-boundary-state"],
  "hook": ["gate-bypass-via-exit", "gate-false-positive", "worktree-isolation"]
}

Pass 2 — Structural test application (30 seconds)

For each category in the candidate set, apply its structural test as a YES/NO question to the design. A structural test requires reading 1-3 sentences of the design. Categories where the answer is YES are confirmed; others are dropped.

This pass is the discrimination step. It is cheap because structural tests are questions, not analyses. An agent that cannot answer a structural test YES/NO within 10 seconds is either in unknown territory (see Pass 3) or the test is too vague and should be refined.

Pass 3 — Precursor signal scan (15 seconds)

Check for precursor signals in the issue title and first paragraph. Precursor signals are phrases that appear before the problem is fully articulated: "store X after phase Y," "multi-agent with shared," "deferred verification," "later." These catch categories that keyword matching misses because the vocabulary doesn't overlap.

The output of the three passes: a ranked list of confirmed categories (structural test YES) plus precursor-signal candidates (tentative). The agent loads the full category definition for each confirmed category before design evaluation begins. Tentative categories are held for hypothesis formation.


4. The Hypothesis Formation Protocol

A hypothesis has this mandatory structure:

H: Design D will fail at seam S because property P holds in context C.
Falsification: Construct scenario F in which P holds. If D survives F, the hypothesis is refuted.
Minimum experiment: Trace system state after [specific failure condition]. Record what is lost.

A hypothesis is falsifiable if and only if: (1) there exists a specific scenario that, if it occurred, would prove the hypothesis wrong, and (2) that scenario can be constructed without building the full system.

A hypothesis is vague if it can absorb any outcome without updating. "This might have caching issues" is vague. "This cache will serve stale data because invalidation is synchronous and blocks the writer for 200ms, creating a window where nodes B and C read pre-write data" is falsifiable.

Protocol steps:

Step 1 — One hypothesis per candidate design. For each design option on the table, state one hypothesis about its primary failure mode. The failure mode must come from the loaded category's failure topology if the category matched. If no category matched, the failure mode must be derived from first principles.

Step 2 — Falsification attempt. Construct the specific scenario from the hypothesis. Walk the system state after that scenario executes. Ask: what has been written? what has been lost? is the loss acceptable? The scenario must be specific enough to complete the walk in under 3 minutes.

Step 3 — Outcome classification. If the walk shows the system survives: hypothesis refuted, design survives this round. If the walk shows failure: hypothesis confirmed, design is fragile against this failure mode. Fragile designs are not eliminated yet — they are scored against the category's other failure modes.

Step 4 — Rival hypothesis. For the surviving design(s), form one rival hypothesis: the best argument that this design will fail. Attempt to falsify the rival. If you cannot falsify the rival in 3 minutes of walking, treat the rival as a real risk and surface it explicitly in the design choice.

Step 5 — Dominance check. A design that fails more failure modes than its rival, without compensating advantages, is dominated. Dominated designs are eliminated. Only non-dominated designs reach the selection step.

The key constraint: try to prove yourself wrong. A falsification attempt that you approach hoping to confirm your preferred design is not a falsification attempt — it is a rationalization dressed as rigor. The discipline is to seek the failure, not to fail to find it.


5. The Feedback Loop

The feedback loop converts session incidents into category library growth. It has four stages.

Trigger. After any /kaizen-reflect run that files a GitHub issue with label type:bug, type:state-resilience, type:regression, or type:pattern, the reflect skill runs a structured extraction step:

npx tsx src/cli-experience.ts add-entry --from-issue <N>

This is mandatory when the label condition is met, not optional. The extraction fills the FSI schema (from r2-experience-accumulation.md) and appends to index.json.

Data recorded per incident.

{
  "id": "fsi-<N>",
  "github_issue": <N>,
  "date": "<ISO>",
  "matched_category": "<slug>",
  "design_shape_that_failed": "<slug>",
  "design_shape_preferred": "<slug>",
  "seams_involved": ["<seam1>", "<seam2>"],
  "recovery_cost": "low|medium|high",
  "was_category_in_library": true|false,
  "was_category_retrieved_at_design_time": true|false
}

The was_category_retrieved_at_design_time field is the feedback loop's diagnostic instrument. If it is consistently false for incidents that match an existing category, the retrieval mechanism is failing, not the category library.

Processing — from incident to category update.

When a new FSI entry is added and its matched_category exists in the library: increment incidents array, update confidence (confidence = min(0.99, 0.5 + 0.1 * incident_count + 0.05 * recurrence_count)), update last_confirmed.

When the incident's matched_category does not exist in the library: flag for human review with the draft category structure pre-filled from the FSI schema. Human review (Aviad) approves or merges with an existing category.

Threshold for new category creation.

A new category is created when: (1) 3 or more FSI entries share the same design_shape_that_failed but do not match any existing category, AND (2) the failure mode is distinct from all existing categories' failure topologies. The threshold of 3 prevents one-off incidents from fragmenting the library.

Category retirement. A category whose last_confirmed is more than 180 days old and whose confidence has never exceeded 0.70 is flagged as speculative. Speculative categories are excluded from retrieval by default but preserved in the library for audit. This prevents false authority from stale categories.


6. Proportionality

Proportionality is a function of two variables: category match quality and category confidence.

Match quality: How cleanly does the current design map to a single category? Clean match (one category, structural test YES, precursor signals present) = high match quality. Ambiguous match (multiple categories, structural tests uncertain) = low match quality.

Category confidence: The empirical confidence field from the category definition. Derived from incident count, recurrence count, and recency.

The calibration matrix:

Match quality Category confidence Investment level Action
Clean High (>0.80) Near-zero Load preferred shape, log one sentence, proceed
Clean Moderate (0.50–0.80) Low Run one falsification cycle per candidate shape (5 min)
Ambiguous Any Medium Run full hypothesis protocol for each confirmed category (15 min)
No match N/A High Treat as novel territory: full exploration, create draft FSI entry (20-30 min)

The critical property: cost scales with novelty relative to the library, not with the absolute complexity of the problem. A problem that appears complex but maps cleanly to a high-confidence category costs near-zero. A problem that appears simple but has no category match costs full exploration. This is exactly the behavior a staff engineer exhibits — familiar problems are fast, unfamiliar problems are slow, and the boundary shifts as experience grows.

Near-zero cost in practice: When match quality is clean and confidence is high, the agent logs: "Category: session-boundary-state (confidence: 0.87). Shape: accumulate-then-flush is fragile in this category. Proceeding with write-through shape." Total overhead: 15 seconds. No design pause. No falsification cycle. The category library has already done the work.


7. Integration with Kaizen

Where category recognition runs: At the start of /kaizen-implement, after the plan is retrieved but before any implementation begins. The plan text is the input to Pass 1 of the recognition algorithm. Category recognition is not optional for plans that touch state, hooks, tests, or skills — these domains have category coverage above 0.80 and should always trigger retrieval.

New files required:

.claude/kaizen/categories/ — one YAML file per category in the formal schema above. Bootstrap with the 10 categories in Section 2. File naming: cat-<slug>.yaml.

.claude/kaizen/categories/index.json — flattened keyword-to-category map for Pass 1, plus category slugs with confidence scores for DPS Recurrence weight computation.

src/cli-experience.ts — subcommands: recognize --text "<plan excerpt>" (runs Passes 1-2, outputs confirmed categories as markdown); add-entry --from-issue <N> (mandatory in reflect); query --category <slug> (loads full category for injection into agent context); update-confidence --id <slug> (recomputes from current incident count).

Changes to SKILL.md files:

/kaizen-implement — Add step 0 before all others: "Run category recognition: npx tsx src/cli-experience.ts recognize --text '<plan summary>'. Load confirmed categories. If any category matches, load its preferred shape before evaluating design options."

/kaizen-reflect — After gh issue create succeeds with FSI-eligible label: "Run npx tsx src/cli-experience.ts add-entry --from-issue <N>. This is mandatory, not optional. Confirm entry ID is written to index.json."

/kaizen-evaluate — Before scope assessment: "Run category recognition on the issue title and description. If category confidence > 0.80 and preferred shape is non-dominated, note this in the evaluation as prior evidence — do not re-derive what the library already knows."

No new schema is needed for hypotheses. Hypotheses are session-local reasoning artifacts. They are not persisted. The output of hypothesis formation is either a design choice (persisted to the plan) or a new FSI entry (persisted to the category library via reflect). The hypothesis itself is scaffolding that is discarded once it has served its function.


The library grows in one direction: from incidents toward prediction. The categories that matter most are the ones filed in anger at 2 AM, generalized in the post-mortem, and retrieved before the next 2 AM arrives.

Information Architecture for the Issue→Plan Transformation

Round 3 — March 2026


1. The Information Flow Diagram

Current State

I_issue (inputs)                    Transformation          I_plan (outputs)
─────────────────                   ──────────────          ──────────────────
issue title                   ──►   kaizen-evaluate    ──►  task list
issue body                    ──►   (Phase 1-5)        ──►  rough design
labels                        ──►   + admin Q&A        ──►  test plan
linked issue numbers (text)   ──►                           coverage review result

What is actually consumed: The issue body is read once, linearly. The transformation is a conversation with the admin. The output is a structured plan stored via npx tsx src/cli-structured-data.ts store-plan.

What is not consumed:

  • The content of linked issues (numbers appear in the body but are not fetched)
  • Past FSI entries (no retrieval path exists)
  • Codebase patterns in the area being modified (no survey step)
  • Prior PRs that touched the same files (no history query)
  • The admin's reasoning from past similar evaluations (Phase 6 has no implementation)

Target State

I_issue (inputs)                    Retrieval triggers         Augmented context         I_plan (outputs)
─────────────────                   ──────────────────         ─────────────────         ──────────────────
issue title + body            ──►   keyword/category scan ──►  FSI entries (top 3)  ──►  task list (with
labels                        ──►   linked issue fetch    ──►  linked issue bodies        rationale)
linked issue numbers          ──►   codebase survey       ──►  affected file patterns ──►  design (with
CLAUDE.md / policies.md       ──►   past-PR query         ──►  prior solutions            alternatives)
FSI index.json (always read)  ──►   epic parent fetch     ──►  epic methodology      ──►  test plan (with
                                                           ──►  related closed PRs         seam map)
                                                                                      ──►  FSI entries cited
                                                                                      ──►  alternatives
                                                                                           considered

The transformation gains an information retrieval phase (before plan formation) and a structured plan schema (after plan formation) that forces the retrieved information to be visibly incorporated.


2. The Information Gap Analysis

Based on the GitHub evidence that 7 of 8 failures had the needed information in the issue body but it was not retrieved or applied:

Category A: State Resilience failures

Pattern: data accumulated in memory, lost at session boundary or crash

Missing information: FSI entry for "accumulate-then-flush" anti-pattern. The review-findings loss incident was filed but not queryable. The plan proposed the same pattern without knowing that pattern had already failed here. Gap: no retrieval path from proposed design shape to past incident with same shape.

Category B: Cross-worktree contamination

Pattern: state files iterated globally, one worktree reads another's state

Missing information: policies.md rule 5 exists but was not consulted during plan formation. The codebase survey would have shown state-utils.ts already exists as the correct abstraction. Gap: no mandatory codebase survey step before proposing new state management.

Category C: Hook weight violations

Pattern: heavyweight operations (tsc, vitest) placed in Stop hooks

Missing information: docs/hooks-design.md contains explicit anti-patterns for this. The issue body referenced the hook context but the plan did not check the design doc. Gap: no trigger that says "hook changes → read hooks-design.md before planning."

Category D: False-positive regex patterns

Pattern: grep pattern matches unintended strings (KAIZEN_UNFINISHED false positive)

Missing information: The pattern of over-broad regex is documented in closed issues but not indexed for retrieval. A survey of existing hook tests would have shown the boundary test convention. Gap: no adjacent-issue query to surface "similar hook patterns that caused false positives."

Category E: Scope drift / silent narrowing

Pattern: plan addresses symptom rather than root cause; full issue scope not delivered

Missing information: The issue body contained the full scope; the plan silently narrowed it. Phase 5.5 (plan coverage review) exists to catch this, but fires after the plan is already formed — the narrowing happened earlier, during design, not during coverage check. Gap: the issue's success criteria are not extracted explicitly before plan formation begins. The plan is formed first, then checked against the issue — backwards.


3. The Information Sources Inventory

Source Currently Available Retrieval Cost Relevance Frequency Strategy
Issue body Yes — read in Phase 0.5 Free Always Already in use
Issue labels Yes — read in Phase 0 Free Always Already in use
CLAUDE.md / policies.md Yes — in agent context Free (injected at session start) High Already available; not explicitly triggered
FSI index.json No — not yet built ~1s flat file read High for DPS>=1 Build; always read in planning
FSI full entries No — not yet built ~2s (top 3 files) Medium Read on category/shape match
docs/hooks-design.md Yes — exists in repo ~3s (one file read) High when issue touches hooks Trigger: "hook" in labels or title
Linked issue bodies Partial — issue numbers extracted but bodies not fetched ~2s per issue (gh API) High — linked issues are linked for a reason Always fetch, cap at 5
Codebase survey (grep) No — not performed ~5s (targeted grep) High when plan proposes new abstractions Trigger: plan proposes new module or state pattern
Past PRs for related files No — not queried ~8s (gh pr list + grep) Medium Trigger: DPS>=2 or state/hook changes
Epic parent body Conditional — Phase 0.5 checks for Parent ref ~2s Medium — only for sub-issues Already mandated in Phase 0.5; enforce fetch
Admin reasoning from past evals No — Phase 6 has no implementation N/A (not stored) High for recurring issue categories Blocked on FSI + Phase 6 implementation
Experiment results Partial — src/cli-experiment.ts exists ~2s Low-medium Already available; use in Phase 3.5

Key insight: Most high-value sources are already in the repo and require only a targeted read, not new infrastructure. The cost is 5-15 seconds of retrieval for a planning phase that already takes 10-30 minutes. The overhead is invisible.


4. The Retrieval Trigger Design

The goal is selective retrieval: match the information need to the issue's signals. Do not run all retrievals for every issue.

Trigger matrix

Signal in issue Retrieval triggered Command
Any issue (universal) Read FSI index.json if it exists and has >=5 entries cat .claude/kaizen/failure-signatures/index.json
Issue body mentions "hook" OR labels include area:hooks Read docs/hooks-design.md cat docs/hooks-design.md
Issue body mentions "state", "cache", "session", "persist" FSI shape query: accumulate-then-flush, shared-state-no-contract npx tsx src/cli-experience.ts query --shape accumulate-then-flush
Linked issues present (any #N reference in body) Fetch linked issue bodies (cap 5) gh issue view {N} --repo $ISSUES_REPO --json body,title,labels
Labels include type:state-resilience OR type:regression Full FSI category retrieval npx tsx src/cli-experience.ts query --category state-resilience
Issue body mentions "worktree", "branch", "parallel" Read policies.md rule 5 explicitly; FSI query for worktree contamination Direct file read
DPS score >= 2 (high-stakes decision) Past PRs touching related files gh pr list --search "$(extract_keywords)" --state merged --limit 5
Epic parent present (Parent: #N in body) Fetch parent epic body for methodology gh issue view {N} --repo $ISSUES_REPO --json body

Cost-benefit gating

Issue arrives
│
├─ FSI index.json exists and >=5 entries?
│   YES → read index.json (1s), filter by issue keywords → get matching entry IDs
│   NO  → skip FSI, note "store is immature"
│
├─ Linked issues in body?
│   YES, <=5 → fetch all bodies (2s each, parallel)
│   YES, >5  → fetch first 5 by recency
│   NO        → skip
│
├─ Keyword triggers match? (hooks / state / worktree)
│   YES → read targeted docs (3s total, parallel reads)
│   NO  → skip
│
└─ DPS score computed?
    DPS <1 → no further retrieval
    DPS 1-2 → FSI category match only (already done above)
    DPS >2 → add past-PR query (8s)

Anti-explosion rule: Total retrieval budget is 30 seconds. If all triggers fire, cap by priority: FSI entries > linked issues > targeted docs > past PRs. Never retrieve past PRs for DPS<2 issues — they are the most expensive and least relevant for simple work.


5. The Plan Schema

Current plans are unstructured markdown. This allows the planning phase to silently omit the retrieved information. A structured schema forces it to be present and auditable.

Proposed plan schema

---
issue: 904
repo: Garsson-io/kaizen
created: 2026-03-26T10:00:00Z
fsi_entries_consulted: [fsi-042, fsi-017]   # empty list = none found or store immature
linked_issues_fetched: [881, 903]            # issues whose bodies were read
design_alternatives_considered: 2            # count, not prose
dps_score: 1.4
---

## Success Criteria
<!-- Extracted from issue body BEFORE plan formation. What does "done" look like? -->
<!-- Written as verifiable outcomes, not task descriptions. -->

## Information Retrieved
<!-- One line per source consulted. If nothing retrieved, say "FSI store immature — skipped." -->
- FSI: fsi-042 — "accumulate-then-flush" anti-pattern — **adopted preferred pattern (write-through)**
- Linked issue #881: proposed using the same gate manager — **reused pattern**
- docs/hooks-design.md: Stop hooks must be <200ms — **verified: new hook reads flat file only**

## Design Alternatives Considered
<!-- MANDATORY: at least 2 alternatives, with disposition. -->
### Option A: [description] — SELECTED
Reason: ...

### Option B: [description] — REJECTED
Reason: ...

## Tasks
<!-- Ordered, concrete, testable. -->
1. ...

## Seam Map
<!-- Where does new code live? What existing seams does it touch? -->
<!-- Required for any change that touches hooks, state files, or worktree boundaries. -->

## Test Plan
<!-- Per-task test coverage. Must name the test file and describe what is being asserted. -->

## FSI Risk Flags
<!-- If any FSI entry was retrieved, paste its preferred_pattern here. -->
<!-- This makes the warning visible and auditable — it was seen, not ignored. -->

This schema is stored via the existing store-plan command:

npx tsx src/cli-structured-data.ts store-plan --issue 904 --repo "$ISSUES_REPO" --file plan.md

The plan coverage review (Phase 5.5) already runs against the stored plan. The schema change makes the "information consulted" section auditable — a future agent or Aviad can see exactly what was retrieved and whether it was applied.


6. The Feedback Loop

After a plan leads to an outcome (PR merged, or failed mid-implementation):

On success (PR merged without rework)

/kaizen-reflect records:

npx tsx src/cli-experience.ts record-retrieval-outcome \
  --issue 904 \
  --fsi-ids "fsi-042,fsi-017" \
  --outcome preferred_pattern_selected \
  --note "write-through pattern used, no state loss"

This increments confirmed_retrievals on each cited FSI entry, increasing its confidence score. High-confidence entries surface first in future queries.

On failure (multi-PR fix cycle, regression, or explicit post-mortem)

/kaizen-reflect runs the FSI capture step:

npx tsx src/cli-experience.ts add-entry --from-issue {post-mortem-issue-N}

This creates a new FSI entry from the incident. The entry's surface_trigger_keywords are derived from the plan that failed — the keywords that should have triggered retrieval but didn't. This retroactively patches the trigger matrix.

Phase 6 implementation (minimal viable)

Phase 6 currently has no implementation. The minimum viable version: after admin responds in Phase 5, write a structured evaluation note to the issue as an attachment:

npx tsx src/cli-section-editor.ts write-attachment \
  --issue {N} \
  --repo "$ISSUES_REPO" \
  --name eval-lessons \
  --file eval-lessons.md

eval-lessons.md is a templated fill-in-the-blank:

## Evaluation Lessons

**Admin decision:** [GO / NO-GO / MODIFIED]
**Where the spec was wrong:** [one sentence or "accurate"]
**Problem was bigger/smaller than spec implied:** [bigger / smaller / accurate]
**What information, if available earlier, would have changed the plan:** [one sentence]
**FSI entry warranted:** [yes (category: X) / no]

This is 5 structured fields, not open-ended prose. An agent can fill them in 2 minutes. The resulting attachments become the training data for what Phase 6 should eventually do automatically.


7. Minimum Viable Implementation for Kaizen

Given the existing infrastructure, the implementation has three pieces in dependency order:

Piece 1: Build the FSI store (new file: src/cli-experience.ts)

Bootstrap with 5 entries from known incidents. Commands needed:

  • add-entry --from-issue N — reads issue body, fills FSI schema, writes .claude/kaizen/failure-signatures/fsi-{id}.json, updates index.json
  • query --category X --shape Y — reads index.json, filters, reads top-3 full entries, outputs markdown for context injection
  • record-retrieval-outcome --issue N --fsi-ids X,Y --outcome Z — updates confidence fields on cited entries

The index.json schema:

{
  "entries": [
    {
      "id": "fsi-042",
      "failure_category": "state-resilience",
      "design_anti_pattern_shape": "accumulate-then-flush",
      "surface_trigger_keywords": ["store", "findings", "session", "orchestrator"],
      "seams_involved": ["orchestrator-subagent-boundary", "session-lifecycle"],
      "confirmed_recurrences": 1,
      "confidence": 0.8,
      "status": "active"
    }
  ]
}

Piece 2: Add retrieval step to kaizen-evaluate SKILL.md

Insert between Phase 0.5 and Phase 1 — call it Phase 0.7: Information Retrieval.

# Universal: check FSI
FSI_INDEX=".claude/kaizen/failure-signatures/index.json"
if [ -f "$FSI_INDEX" ]; then
  ISSUE_KEYWORDS=$(gh issue view {N} --repo "$ISSUES_REPO" --json title,body \
    --jq '[.title, .body] | join(" ")' | tr ' ' '\n' | sort -u | tr '\n' ',')
  npx tsx src/cli-experience.ts query --keywords "$ISSUE_KEYWORDS" --top 3
fi

# Conditional: fetch linked issue bodies (cap 5)
LINKED=$(gh issue view {N} --repo "$ISSUES_REPO" --json body \
  --jq '.body' | grep -oE '#[0-9]+' | head -5 | tr -d '#')
for ID in $LINKED; do
  gh issue view "$ID" --repo "$ISSUES_REPO" --json title,body,labels
done

# Conditional: hook changes
if echo "$ISSUE_BODY" | grep -qi "hook"; then
  cat docs/hooks-design.md
fi

The agent reads the output, synthesizes it into an "Information Retrieved" section, then begins plan formation with that context active.

Piece 3: Update store-plan to enforce schema

Add schema validation to the existing store-plan path in src/cli-structured-data.ts:

function validatePlanSchema(planText: string): string[] {
  const required = [
    '## Success Criteria',
    '## Information Retrieved',
    '## Design Alternatives Considered',
    '## Tasks',
    '## Test Plan',
  ];
  return required.filter(section => !planText.includes(section));
}

If missing sections are found, store-plan emits warnings (not errors — don't block storage, but surface the gap visibly). The plan coverage review in Phase 5.5 already reads the stored plan; it can check for these sections as an additional dimension.


Synthesis

The issue→plan transformation currently treats the issue body as the only input and produces a plan as the only output. The failure mode is predictable: information that exists elsewhere in the system (FSI, linked issues, design docs, past PRs) is not retrieved because there is no structured retrieval phase and no trigger system.

The fix has three properties:

Selectivity over completeness. Do not retrieve everything — retrieve what the issue's signals suggest is relevant. The trigger matrix turns issue keywords and labels into retrieval instructions. Simple issues (no DPS flags, no linked issues, no hook references) get minimal retrieval. Complex issues get full retrieval. The overhead scales with the complexity.

Forced acknowledgment, not forced compliance. The plan schema requires an "Information Retrieved" section and an "FSI Risk Flags" section. The agent must either cite what it found or declare that nothing was found. This creates an auditable record of whether the retrieval happened — without requiring that every retrieved warning change the plan.

Feedback closure via Phase 6. The evaluation lessons attachment (5 structured fields, 2 minutes to complete) transforms admin reasoning from ephemeral conversation into durable data. After 10-15 evaluations, the patterns in those attachments become the basis for updating the trigger matrix and FSI entries — closing the loop from "this plan failed" to "future plans in this category retrieve the right information automatically."

The FSI is the highest-leverage single addition: it converts past failures from GitHub prose (human-readable, not agent-queryable) into a flat-file index (agent-queryable in under 3 seconds). Every other improvement in this architecture amplifies the FSI's value. Build the FSI first; wire the trigger matrix second; enforce the plan schema third.


Round 3 design. Test against the next 5 evaluations. When the trigger matrix fires a false positive (retrieves irrelevant docs), narrow the trigger condition. When it misses a retrieval that would have helped, add the missing keyword to the FSI entry's surface_trigger_keywords.

Round 3: The Plan Review Battery

Concrete dimensions for evaluating plan quality before implementation starts March 2026


1. Design Rationale

The Analogy and Its Limits

The code review battery exists because code is evidence of past decisions. A reviewer can read the diff, trace logic paths, and determine whether the code does what it claims. The evaluation stance is retrospective: did the implementation satisfy its specification?

A plan review battery requires a different stance. Plans are hypotheses about future behavior. A plan says "if I do X, Y, and Z in that order, the issue's goal will be satisfied." Every statement in a plan is a prediction. The reviewer's job is not to check whether the plan is correct — that's unknowable before implementation — but to check whether the plan is well-formed: whether its predictions are traceable to actual goals, grounded in the actual codebase, and structured so that they could be falsified after shipping.

This distinction matters for calibration. The code battery can catch bugs. The plan battery cannot catch bugs — it can only catch plans that are structurally guaranteed to produce the wrong outcome regardless of how well they are executed. That is a narrower but equally important job.

Why a Separate Battery Is Needed

The existing plan-coverage dimension checks: does the plan address every requirement in the issue? This is necessary but insufficient. It catches plans that miss requirements. It does not catch plans that faithfully address every requirement but address the wrong goal, or plans that address the right goal through a design that was never interrogated, or plans that cannot be verified after shipping because success was never defined.

The five GitHub failure categories show that plan-coverage alone leaves four gaps open:

  1. The plan can pass plan-coverage (all requirements addressed) while building toward the issue's proposed solution rather than the issue's actual goal (#666: schema built, 0 SKILL.md files populated — every requirement addressed, wrong problem solved).
  2. The plan can pass plan-coverage while treating a hypothesis as a specification (#724: proposed fix treated as the answer, not as a conjecture to validate).
  3. The plan can pass plan-coverage while designing a system that already exists in the codebase (#957: custom storage built over an existing tool the plan didn't survey).
  4. The plan can pass plan-coverage while placing implementation in a location where it cannot be tested (#891/#894: logic in main() instead of testable units).

These are not coverage gaps. They are quality gaps in the plan's epistemic structure. The plan battery addresses them.

The Fundamental Difference from Code Review

Code review is falsification of existing claims. Plan review is evaluation of future predictions. The evaluator asks:

  • Are these predictions about the right thing?
  • Are these predictions grounded in actual evidence?
  • Will we be able to tell after the fact whether the predictions came true?

A plan that passes the battery is not guaranteed to succeed. It is guaranteed to have asked the right questions before starting.


2. The Dimensions

Dimension 1: Goal-Traceability

Question: Does the plan trace back to the issue's stated goal, not just its proposed solution?

Failure category: Goal vs. work-item extraction (#666, #957)

How to evaluate:

Read the issue body. Identify the section that answers "why does this matter?" — the motivation, the user problem, the observable failure that prompted the issue. This is the goal. Then read the plan. Ask: if this plan were executed perfectly, would the goal be satisfied? Or would the infrastructure described in the plan exist, while the original observable failure persisted?

The plan must contain at least one sentence that makes this connection explicit: "This addresses the goal because..." or "After this PR ships, the observed failure will no longer occur because..."

PASS: The plan explicitly connects its deliverables to the issue's motivating problem. A reader who knew nothing about the implementation could verify whether the goal was achieved by looking at the plan's success criteria.

FAIL: The plan lists requirements-as-steps without ever asking whether satisfying those requirements solves the problem. The issue says "users can't run skill X" and the plan builds the infrastructure that would allow skill X to run — without specifying that skill X must actually run in the tests.

Example PASS: Issue #891 motivates: "70-line main() blocks testability; skills cannot be unit-tested." Plan states: "After this PR, run_skill() will be importable without side effects, and tests/test_skill_runner.py will cover the three hot paths without mocking main(). The success condition is: pytest tests/test_skill_runner.py passes with no mocks on sys.argv."

Example FAIL (from #666 evidence): Issue motivates: "Skills should auto-populate from SKILL.md metadata." Plan states: "Implement JSON schema for skill metadata. Add validation for schema fields. Wire CLI flag to schema loader." Plan-coverage: DONE on all requirements. Goal-traceability: FAIL — schema can exist with zero SKILL.md files populated. The connection from schema to populated skills was never specified.

False positive risk: Plans for infrastructure work legitimately have a deferred goal connection — the infrastructure itself is the deliverable, and downstream issues will use it. If the issue is explicitly "build the infrastructure layer for X," goal-traceability to the downstream outcome is out of scope. Check the issue framing first.

Prompt language for reviewers:

Read the issue's MOTIVATION section (or equivalent). Extract the observable problem the issue author was experiencing. Then read the plan. Answer this question: "If a non-technical stakeholder were present when this plan was fully executed, what would they see that they couldn't see before?" If that answer does not map back to the observable problem, flag as FAIL with a specific description of the gap.


Dimension 2: Hypothesis-Validation

Question: Does the plan treat the proposed solution as a hypothesis and specify how to validate it?

Failure category: Hypothesis-as-contract (#724)

How to evaluate:

Issues frequently contain a "proposed fix" or "suggested approach" section. This is the issue author's hypothesis about what will work. It is not a specification. A plan that simply decomposes the proposed fix into implementation steps has converted a hypothesis into a contract without validation.

Look for two things: (1) any statement in the plan that questions the proposed approach or acknowledges it could be wrong, and (2) a validation step — a test, a smoke run, a manual verification — that would confirm the hypothesis is actually correct before or immediately after implementation.

PASS: The plan either validates the proposed fix's core assumption before implementing, or includes a post-implementation step that explicitly tests whether the fix achieves the claimed outcome.

FAIL: The plan implements the proposed fix as specified with no statement about what would confirm or disconfirm that the fix worked. The plan treats "proposed fix: change X to Y" as equivalent to "requirement: change X to Y."

Example FAIL (from #724 evidence): Issue proposes: "The hook fires too late because the event order is wrong — swap event A and event B." Plan steps: "(1) reorder event A and B in the registry, (2) update tests to reflect new order, (3) verify tests pass." This plan implements the hypothesis unconditionally. A plan that passes this dimension would add: "First, verify that the current failure reproduces reliably and is caused by event ordering (not a timing issue or a different race). Reproduce the failure in a unit test before touching the registry."

False positive risk: When the proposed fix is trivially verifiable — a typo fix, a constant change — requiring formal hypothesis validation adds overhead with no benefit. Apply this dimension only when the proposed fix touches behavior, not when it touches content.


Dimension 3: Codebase-Survey

Question: Did the plan survey existing tools and infrastructure before designing new ones?

Failure category: Plan before codebase survey (#957)

How to evaluate:

For any plan that introduces a new abstraction, a new storage mechanism, a new utility function, or a new pattern: is there evidence that the plan author searched the existing codebase before designing? The evidence can be explicit ("I found cli-section-editor.ts which does X but doesn't cover Y, so...") or implicit (the plan adopts existing conventions and tools rather than reinventing).

Red flags: custom storage over an existing store, new utility functions that duplicate functionality that can be found by grepping for the type signature, new patterns that contradict existing patterns in adjacent files.

PASS: The plan cites at least one existing mechanism it builds on, or explicitly states it searched and found nothing relevant. For plans introducing genuinely new infrastructure, this is a required statement.

FAIL: The plan designs from scratch in an area where existing infrastructure exists and the plan makes no reference to it. "Build a new section storage system" in a codebase with cli-section-editor.ts is FAIL on this dimension if the plan never mentions the editor.

Example FAIL (from #957 evidence): Plan proposes: "Add a custom key-value store for PR sections, storing data in GitHub PR body comments using a marker syntax." The codebase already has src/section-editor.ts and src/cli-section-editor.ts which do exactly this. The plan built over existing infrastructure it hadn't read.

False positive risk: If the plan is for a new project or domain with no relevant existing code, this dimension cannot fire. Also: if the plan explicitly cites the existing tool and explains why it's insufficient, PASS — the survey happened.

Prompt language for reviewers:

For each new abstraction or utility the plan introduces, run: grep -r "<type or purpose>" src/ --include="*.ts" -l. If matching files exist that the plan doesn't cite, flag as FAIL. The plan should either adopt the existing tool, extend it, or explicitly explain why it's insufficient.


Dimension 4: Design-Alternatives

Question: Were at least two alternative designs considered and explicitly rejected?

Failure category: Single design considered (#966/#970)

How to evaluate:

The plan must contain at least two named designs at the point of highest design risk. "Highest design risk" means the choice that is most irreversible, has the highest blast radius, or maps to a known failure category (state ownership, interface contracts, storage strategy).

Acceptable forms: an explicit "alternatives considered" section, inline text ("I chose X over Y because Z"), or commit-level notes on design decisions. The key requirement: the second design must be named and have a stated failure mode or rejection reason.

PASS: The plan names at least one alternative to the primary design and explains why it was rejected, in terms of failure modes or fitness to existing constraints — not just "option A seemed cleaner."

FAIL: The plan presents a single design as the obvious choice with no alternatives examined. This is especially critical when the plan touches state ownership, persistence strategy, or inter-component interfaces.

Example FAIL (from #966/#970 evidence): Plan for review workflow: "Use orchestrator-batch pattern: orchestrator coordinates all subagents, collects results, writes in a single batch." No mention of agent-stores pattern, no analysis of what happens if the orchestrator crashes between coordination and writing. One design, no rejection reasoning.

Example PASS: Same plan: "Two options: (A) orchestrator-batch — orchestrator writes at the end. Simple but loses all results if orchestrator dies before the write. (B) agent-stores — each agent writes immediately on completion. More complex but survives orchestrator crash. Given that these sessions routinely run >30 minutes and crashes are non-trivial, choosing B. Orchestrator crash recovery is the disqualifying failure for A."

False positive risk: Trivial plans with no irreversible decisions don't require alternatives. A plan to rename a variable, fix a typo in a string, or add a log line is not a design decision. Apply this dimension when the plan introduces architecture, persistence, or inter-component contracts.


Dimension 5: Testability-Preflight

Question: Is the implementation located where it can be tested, and are test locations specified?

Failure category: Testability not assessed (#891/#894)

How to evaluate:

For every new behavior described in the plan: where will the test live? The plan must specify this, even if only in a single sentence. Additionally: is the planned implementation structure compatible with testing? Behaviors embedded in main(), in top-level script execution, in hooks that can't be imported, or in closures over global state are hard or impossible to unit test.

PASS: The plan specifies at least one test file or test location per significant behavior. The implementation structure separates behavior from execution context (e.g., business logic is in importable functions, not wired directly into CLI entry points).

FAIL: The plan describes behavior but says nothing about tests. Alternatively: the plan places the behavior in a location (main(), a hook entry point, a side-effectful module) where it cannot be tested in isolation.

Example FAIL (from #891/#894 evidence): Plan: "Add fallback logic to handle missing config. Implement in the hook's main execution block." The hook entry point cannot be imported without triggering side effects. The fallback logic will never be unit-tested. The plan should specify: "Extract fallback logic into fallbackForMissingConfig(config) in lib/config-fallback.ts, callable without mocking the file system. Test in tests/test_config_fallback.sh."

False positive risk: Plans for documentation changes, config changes, or content-only modifications have no behavior to test. For plans that are purely structural (moving files, renaming functions), testability is implicit in the surrounding test suite.


Dimension 6: Success-Criteria-Definition

Question: Does the plan define what "done" looks like in terms that can be checked after shipping?

Failure category: Goal vs. work-item extraction (secondary); addresses the lesson-evaporation gap in kaizen-evaluate Phase 6.

How to evaluate:

The plan must include at least one concrete success condition: a command that will pass, a behavior that will be observable, a metric that will change. "The feature will work" is not a success condition. "Running ./scripts/smoke.sh produces exit code 0 and logs 'config loaded'" is a success condition.

PASS: The plan has at least one success condition that is checkable without reading the implementation. An external observer could verify it.

FAIL: The plan's deliverables are described entirely in terms of work items ("implement X, add Y, update Z") with no statement of what the world will look like differently after the work is done.

Example PASS: "Success: npm run test:hooks passes with no regressions. claude -p test-skill.md completes without asking 'which PR?'. Response time under 40s on 3 consecutive runs."

Example FAIL: "Implement the review skill. Add dimension support. Wire CLI flags." These are work items. None of them is a success condition.

False positive risk: Very small plans where the success condition is implicit in the issue's acceptance criteria. If the issue says "acceptance: test X passes" and the plan says "make test X pass," the success criterion is inherited. Don't require redundant repetition of what the issue already states clearly.


Dimension 7: Scope-Proportionality

Question: Is the planned scope proportional to the issue's complexity — neither over-engineered nor under-specified?

Failure category: Cross-cutting; catches plans that inflate scope (building infrastructure the issue doesn't need) and plans that under-specify (promising to "implement the feature" without decomposition).

How to evaluate:

Compare the plan's scope to the issue's complexity. A 2-sentence issue should not produce a 500-line plan with 5 PRs. A complex multi-requirement issue should not produce a 3-line plan that says "implement as described."

Two failure modes: (A) Over-engineering — plan introduces more abstraction, infrastructure, or generalization than the issue warrants. (B) Under-specification — plan is so vague that it cannot be reviewed (the plan says "add validation" without saying what will be validated, how, or where).

PASS: The plan's depth matches the issue's depth. Complex issues have decomposed plans. Simple issues have short plans.

FAIL (over-engineering): Plan for a 2-sentence bug report introduces a new abstraction layer, a new data model, and a migration. The issue didn't ask for any of those.

FAIL (under-specification): Plan for a 10-requirement issue says "implement the hook and add tests." A reviewer cannot determine what will and won't be built.

False positive risk: Plans that are legitimately large because the issue is legitimately large. Do not penalize proportionate complexity. The signal is the ratio: does plan complexity scale with issue complexity?


3. The Battery Protocol

When It Runs

The plan battery runs once, after kaizen-implement creates the plan and before any implementation begins. It is triggered by the same mechanism as the code review battery — but the input is the plan document, not the diff.

Concretely: after the agent stores the plan with npx tsx src/cli-structured-data.ts store-plan, the plan battery is invoked. Implementation is blocked until the battery either passes or the agent explicitly overrides with justification.

Who Triggers It

The plan battery is triggered by kaizen-implement as part of its planning phase, or by kaizen-plan for multi-PR decompositions. It can also be triggered manually with /kaizen-review-pr --plan to review a plan on an existing issue.

What Happens on FAIL

A finding of FAIL on any dimension produces a stop gate: the agent must revise the plan and re-run the battery before proceeding. The gate is not advisory.

Fix round limit: 2 revisions. If the plan fails the battery after 2 revision cycles, the agent escalates to the human with a summary of which dimensions are failing and why. The human decides whether to override or require a fundamentally different plan.

What Counts as Blocking

FAIL on any of: goal-traceability, hypothesis-validation, codebase-survey, design-alternatives (when the plan touches irreversible decisions).

PARTIAL does not block. PARTIAL generates a warning that is stored with the plan as a known limitation.

FAIL on testability-preflight or success-criteria-definition blocks implementation of the specific behavior that failed, not the entire plan. The agent may proceed with behaviors that have clear test locations and success criteria while the others are revised.


4. Integration with Existing Gates

What Plan-Coverage Already Checks

Plan-coverage checks: does the plan address every requirement in the issue? It catches omissions — requirements that appear in the issue but not in the plan.

What the Plan Battery Adds

The plan battery does not check coverage. It checks quality of the plan's reasoning. The two dimensions are orthogonal:

  • A plan can have 100% coverage (all requirements addressed) and fail goal-traceability (requirements satisfied, goal not).
  • A plan can have a goal-traceability PASS and a codebase-survey FAIL (right goal, invented the wheel to reach it).
  • A plan can pass both and fail testability-preflight (correct goal, correct tools, untestable implementation location).

The plan battery and plan-coverage are not redundant. Run both.

Can They Be Merged?

No. Plan-coverage has a different evaluation stance (completeness checking) from the plan battery (epistemic quality checking). Merging them into a single prompt would produce a reviewer with confused goals. Keep them separate and run them together.

Sequencing

Plan-coverage runs first (it's simpler and faster). If plan-coverage finds MISSING requirements, fixing those first often resolves plan battery concerns as a side effect. If plan-coverage passes, run the plan battery.


5. Calibration

The battery should block plans that will reliably fail. It should not require perfect plans.

The calibration target is plans that have at least one of these properties that make them structurally guaranteed to fail:

  • The plan solves the wrong problem even when executed correctly.
  • The plan implements a hypothesis without any mechanism to confirm the hypothesis worked.
  • The plan designs infrastructure that already exists.
  • The plan cannot be verified after shipping.

Plans that are imprecise, incomplete, or suboptimal but not structurally broken should receive PARTIAL findings with warnings, not FAIL.

The key question: "If an agent executes this plan faithfully and competently, will the issue's goal be satisfied?" If the answer is "definitely not," block. If the answer is "probably yes but there are risks," warn. If the answer is "yes," pass.

The battery is not a quality bar for plans. It is a failure-mode filter.


This is the R3 synthesis artifact: a concrete plan review battery grounded in the five failure categories from GitHub evidence, the hypothesis-testing discipline from R2's contrarian analysis, and the staff engineer's sequence (categorize → failure topology → option sketch → proportional commitment) from R2's behavioral model. The battery addresses what R1's DPS formula correctly identified as the perception problem — you must name the choice before you can evaluate it — but at the plan level rather than the decision level.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment