diff --git a/agents/analyzer.md b/agents/analyzer.md index 739259e..b94d16b 100644 --- a/agents/analyzer.md +++ b/agents/analyzer.md @@ -64,6 +64,12 @@ RISKS: OPEN QUESTIONS: - + +RELATED MEMORY: +- IN FORCE: — <how it constrains this work> [status] (detail each) +- ALREADY SEEN: <BLK-id> — <title> [status] (known cause/fix — don't re-derive) +- NON-BINDING: <c> surfaced, none binding — <bare id refs> (superseded / N-A — counted) +- SELECTION: scanned <N> headings — surfaced <K> = in-force <a> + seen <b> + non-binding <c> ``` Surface discovered patterns and conventions in the analysis output @@ -73,6 +79,26 @@ happens in the main thread via the gated capitalize flow. --- +## RELATED MEMORY (read-before) + +When `.claude/memory/` exists and holds entries, run the memory-relevance scan per +`$HOME/.claude/lib/analyze-before-plan.md` (PASS 1: grep `## <PREFIX>-` headings → +select on titles → PASS 2: full-read only the selected bodies) and populate the +RELATED MEMORY section of OUTPUT. + +The contract is DISPOSITION, not retrieval: every surfaced ID gets a verdict — +in-force / already-seen / non-binding. Detail the binding ones (IN FORCE, ALREADY SEEN — +they constrain the work); COUNT the non-binding (superseded / N-A) as one line with bare +refs — a per-entry paragraph on a non-binding match dilutes the in-force ones that bite. +A surfaced ID left undisposed is the gap this closes. You judge bearing; the main-thread +plan decides what to DO. + +Read-only here too: reading registries is within Read/Grep; the "Do not modify files" rule +still forbids any write — Index backfill or new entries are never your job. Empty or absent +registries → omit the section (no-op). + +--- + ## EDGE CASES | Situation | Action | diff --git a/agents/bugfixer.md b/agents/bugfixer.md index 9b9ddb0..30c1d2f 100644 --- a/agents/bugfixer.md +++ b/agents/bugfixer.md @@ -60,6 +60,18 @@ Trace the bug from symptom to root cause: # grep for the same pattern to assess blast radius ``` +## STEP 2.5 — MEMORY READ-BEFORE (blockers-first) + +Run the scan per `$HOME/.claude/lib/analyze-before-plan.md`, blockers-weighted: a resolved +BLK may already name THIS exact root cause; an in-force BDR may constrain the fix. Emit +RELATED MEMORY. Consumption is NATURAL — the agent emitting this IS the one writing STEP 3's +diagnosis (reader = planner, no external skill to inject into). + +TEETH: STEP 3's DIAGNOSIS must name any binding prior (`PRIOR: BLK-xxx — known cause/fix`, +or `honors BDR-xxx`) OR the RELATED MEMORY line states none bears. Reading blockers then +diagnosing without naming a match is the read-then-ignore failure this prevents. +`.claude/memory/` absent → guarded no-op, proceed. + ## STEP 3 — HYPOTHESIZE + PLAN Present findings before fixing: diff --git a/agents/feater.md b/agents/feater.md index a4af640..d84c69c 100644 --- a/agents/feater.md +++ b/agents/feater.md @@ -58,6 +58,13 @@ Follow `$HOME/.claude/lib/design-gate.md`: tell the user to run `/profile design` before proceeding. - If no signals → skip (zero overhead). +## STEP 0.6 — MEMORY READ-BEFORE (decisions-first) + +Run the scan per `$HOME/.claude/lib/analyze-before-plan.md`, decisions-weighted: a BDR may +already constrain or forbid the approach; an LRN may name a gotcha to apply. Emit RELATED +MEMORY; feed STEP 1 MINI-PLAN. Inline consumption — reader = planner, no injection. +`.claude/memory/` absent → guarded no-op (zero overhead on a memory-less repo). + ## STEP 1 — MINI-PLAN Quick mental model, not a formal plan document: @@ -66,6 +73,9 @@ Quick mental model, not a formal plan document: 2. Describe the approach in 2-5 bullet points. 3. Note any edge cases to handle. 4. If tests exist for the area, note which tests to add/update. +5. Disposition (from STEP 0.6): name each in-force BDR/LRN this plan honors + (`honors BDR-xxx by …`), or state `no in-force decision constrains this feature`. + A plan with neither = read-then-ignore; the disposition must surface as a trace. Print the plan as a compact checklist: ``` diff --git a/agents/hotfixer.md b/agents/hotfixer.md index 2d914cd..46025a9 100644 --- a/agents/hotfixer.md +++ b/agents/hotfixer.md @@ -31,6 +31,14 @@ git log --oneline -3 "This looks deeper than a hotfix. Load `$HOME/.claude/agents/bugfixer.md` and run the BUGFIXER agent on this target." +OPTIONAL — memory check (exempt by default; hotfix = obvious fix, mirror of its capitalize +skip). For a RECURRING or urgent bug only, a quick blockers-only glance may save time: + + [ -d .claude/memory ] && grep -nE '^## BLK-' .claude/memory/blockers.md # "déjà vu ?" + +If a prior BLK names this bug, jump to its solution. Not mandatory; no RELATED MEMORY +disposition required at hotfix weight. + ## STEP 1.5 — DESIGN GATE Follow `$HOME/.claude/lib/design-gate.md`: diff --git a/lib/analyze-before-plan.md b/lib/analyze-before-plan.md new file mode 100644 index 0000000..7be44a4 --- /dev/null +++ b/lib/analyze-before-plan.md @@ -0,0 +1,123 @@ +# ANALYZE-BEFORE-PLAN — couple the memory read to the dev flow + +Inline snippet. Include at the START of a dev flow, BEFORE the plan forms. This is the +HEAD of analyze; `capitalize-commit.md` is the TAIL. Read-before / write-after — the two +ends of one bookend: a flow consults the decisions / blockers / learnings it will later add to. + +It does NOT decide the plan. It hands the planner a DISPOSED list of prior entries that +bear on the work, so the plan cannot form blind to a decision already in force or a +blocker already solved. + +## WHEN TO RUN + +- Inline flow (feat / bugfix): main thread, AFTER the related code is read, BEFORE the + mini-plan / diagnosis. PASS 2 is bounded-tiny (selection narrows it); no subagent — + preserves stay-light. +- Orchestrator with a code-analysis step (ship-feature / init-project): run it INSIDE the + analyzer subagent. Its fresh context reads code + memory with zero redundancy against + the main thread; only its compact digest returns. The analyzer's RELATED MEMORY output + section IS this snippet's OUTPUT. +- hotfix: not wired by default (mirror of its capitalize skip). Available opt-in for a + blockers-only quick check ("urgent bug déjà vu ?"). + +## DO + +1. PRECONDITION — NO-OP unless `.claude/memory/` exists AND holds at least one registry + file. TESTED reality: a bare `grep … .claude/memory/*.md` on an ABSENT dir (or a dir + with no `.md`) does NOT no-op — the unmatched glob is passed literally and grep ERRORS + (`No such file or directory`, exit 2). So the GUARD makes "absent → no-op", never the + grep. This is init-project's STEP 2 reality: the registries are created at STEP 5, so at + analyze time they are ABSENT — the guard must fire on absent, not merely on empty. + +2. PASS 1 — list every entry, drift-proof, GUARDED: + + [ -d .claude/memory ] && ls .claude/memory/*.md >/dev/null 2>&1 \ + && grep -nE '^## (BDR|LRN|BLK|EVAL)-[0-9]+' .claude/memory/*.md + + One `ID — title` line per entry, for 100% of entries by construction (an entry IS its + heading). A present-but-template-empty registry (header only) → grep exits 1 (clean + no-match) → no-op naturally; only the ABSENT / no-file case needed the guard. The REGEX + is the filter, not the file list: the glob also reads journal.md (date-keyed + `## YYYY-MM-DD`) and any non-entry file, which contribute zero matches — no need to + exclude them. Do NOT read the `## Index` table: it drifts (entries land in the body, the + manual Index update lapses — measured 32-40% missing on a mature repo); headings cannot. + +3. SELECT — IDs whose title bears on $REQUEST. Judge on titles (one dense line each), not + on bodies. Over-inclusion is SAFE: an entry that proves non-binding costs one bare ref + in OUTPUT, not a paragraph (see DISPOSITION) — so err toward including on a dense cluster. + +4. PASS 2 — full-read ONLY the selected bodies (heading to next `##`) to recover + status / why / solution / alternatives. Unconditional for the selected set — see THE + INVARIANT for why there is no "skip if already in context" branch. + +5. DISPOSE — emit RELATED MEMORY (OUTPUT). Every surfaced ID gets a verdict. + +## OUTPUT — RELATED MEMORY (disposition, not a dump) + + RELATED MEMORY (read-before): + IN FORCE — must constrain this work (detail each — they bite): + - BDR-026 — <title> — <how it constrains> [accepted] + - LRN-050 — <title> — <how it applies> + ALREADY SEEN — known cause/fix, don't re-derive (detail each): + - BLK-009 — <title> [upstream] + NON-BINDING — superseded / N-A, COUNTED not detailed: + - <c> surfaced, none binding — BDR-013, LRN-022, … (bare refs, one line) + SELECTION: scanned <N> headings / 4 registries — + surfaced <K> = in-force <a> + seen <b> + non-binding <c>. + +- Disposition rule: DETAIL what binds (IN FORCE, ALREADY SEEN); COUNT what doesn't + (NON-BINDING) as one line + bare refs. The collective verdict still disposes each + non-binding ID — its bare ref under "none binding" IS its disposition — but a per-entry + paragraph on a non-binding match dilutes the in-force ones that bite. On a dense cluster + (K up to ~14) this is what keeps the 3 that matter from drowning under 11 that don't. +- Compact even for binding ones: ID + title + one-clause bearing. Bodies were read to + JUDGE; only the disposition persists — never paste bodies into the plan. +- a + b + c = K: every surfaced ID is accounted for. An unaccounted surfaced ID is the gap + this prevents. +- Nothing bears → `RELATED MEMORY: none of <N> entries bears on this task` (still proves + PASS 1 ran — LRN-048). + +## THE INVARIANT — disposition, not reading + +The guarantee is NOT "the agent read the memory" (an act, unverifiable after the fact). +It is "the plan disposed of every relevant prior entry" (a list, verifiable in OUTPUT). +LRN-048 one step further: the teeth are "did it STATE a verdict on each surfaced ID?", +not "did it look?". + +So there is no "skip PASS 2 if already in context" branch. "Already in context" has no +deterministic oracle: self-judgment is the rejected behavioral guard (LRN-046); a session +marker records "was read", not "still present", so it false-skips after a compaction (and +is the marker cost BDR-033 priced); the agent cannot grep its own window. PASS 2 reads the +selected set unconditionally — cheap by construction — and the invariant bites on the +disposition, which holds whether a body was freshly read or recalled. + +A decision WRITTEN earlier in the same conversation (ship-feature posts BDR-035, then a +bugfix runs) MUST still be surfaced and disposed as in-force: content sitting in context +is not the current flow having TREATED it as a constraint. Re-surfacing is the feature. + +## HARD RULE — read-only + +Touches nothing. No write, no Index update, no memory mutation. Symmetric to +capitalize-commit's surgical scope (there: stage ONLY memory; here: stage NOTHING). Index +backfill, if ever wanted, is `/prune-memory` passe D — never this snippet. + +## ORDERING (orchestrators only) + +`superpowers:brainstorming` / `writing-plans` are external skills — we cannot make them +read our registries. So this runs BEFORE them, pre-loading the disposition into the plan +they form. Mirror of capitalize-commit running BEFORE finishing-a-development-branch: there +the memory commit must precede integration; here the memory read must precede planning. + +## NO-OP / IDEMPOTENT + +Empty or absent registries → silent no-op (greenfield init-project; onboard, which CREATES +memory and has none prior). Pure read → safe to run twice; naturally idempotent. + +## WHAT THIS DOES NOT DO + +- Does NOT read the related CODE — the flow does that (feat STEP 0, bugfix STEP 2). Sole + exception: ship-feature, where the analyzer subagent this runs in reads code too (Gap A). +- Does NOT decide the plan — it primes it with a disposed list of constraints. +- Does NOT write or mutate memory — read-only; capitalize (write-after) is the other bookend. +- Does NOT depend on the `## Index` table — keys off `## <PREFIX>-` headings (drift-immune). +- Does NOT skip PASS 2 on an "already in context" guess — no oracle for it; the read is cheap. diff --git a/skills/ship-feature/SKILL.md b/skills/ship-feature/SKILL.md index 3fff59d..5c26ae0 100644 --- a/skills/ship-feature/SKILL.md +++ b/skills/ship-feature/SKILL.md @@ -60,19 +60,64 @@ During implementation (STEP 4), when making decisions about fast-lib APIs: - Read the relevant `.ctx7-cache/<lib>.md` file before writing code. - This avoids repeated ctx7 calls and keeps docs available without context cost. +## STEP 0d — ANALYZE BEFORE PLAN (code + memory, read-before) +Dispatch the analyzer subagent (fresh context) on the request — it produces the +read-before digest the plan must not form without: + + Agent(subagent_type="analyzer", description="ship-feature — read-before", prompt=""" + Read-only analysis to PRIME a feature plan (NOT debug). REQUEST: <$ARGUMENTS>. + 1. CODE — locate the zone the feature touches: grep/glob the request's nouns / + identifiers across the tree → candidate files (PASS 1), read them (PASS 2). + >50 candidates → scoped sweep per your EDGE CASES, list zones, don't read all. + Too vague to locate → report ambiguous zones, do NOT block. + 2. MEMORY — run the scan per $HOME/.claude/lib/analyze-before-plan.md, emit + RELATED MEMORY (disposed). + Output your standard ANALYSIS + RELATED MEMORY. No solutions.""") + +The returned digest (ANALYSIS + RELATED MEMORY) stays in the orchestrator's context — it +is FED to STEP 1 and STEP 2 and reconciled at STEP 3. Degradation: request too vague → +analyzer flags ambiguous zones, does not block (STEP 1 refines). `.claude/memory/` empty or +absent → analyzer omits RELATED MEMORY (no-op); the step still returns the code ANALYSIS. +Additive — distinct from STEP 5 ANALYZE (post-impl regression) and STEP 4b DEBUG. + ## STEP 1 — BRAINSTORM -Invoke `superpowers:brainstorming`. Refine request into validated design via Socratic questioning. Don't proceed until design approved. +Invoke `superpowers:brainstorming` — but FEED it the STEP 0d digest as binding context, +not the raw request alone: + "Feature request: <$ARGUMENTS>. + In-force constraints (must hold): <only the IN-FORCE + ALREADY-SEEN items from 0d's + RELATED MEMORY, detailed>. + Existing code that bears: <ANALYSIS CONTEXT / KEY COMPONENTS from 0d>. + Brainstorm WITHIN these — don't re-explore a direction an in-force BDR rejects or a + BLK already closed." +Inject ONLY what constrains: the NON-BINDING count does NOT enter the brainstorm input +(the injection inherits the OUTPUT filter — detail what binds, drop what doesn't). +Consumption = INPUT INJECTION (we can't modify the external skill; we control its input). +Refine request into validated design via Socratic questioning. Don't proceed until design approved. ## STEP 2 — PLAN -Invoke `superpowers:writing-plans`. Break design into tasks (2-5 min each). Each task: exact file paths, full code, verification steps. +Invoke `superpowers:writing-plans` with the validated design AND the 0d digest: every task +must be consistent with the in-force constraints; where a task implements or affects one, +note the ID inline. Break design into tasks (2-5 min each). Each task: exact file paths, full code, verification steps. ## STEP 3 — VALIDATION GATE ★ MANDATORY STOP ``` SHIP FEATURE — VALIDATION GATE FEATURE: <n> | TASKS: <count> <numbered task list> + +RELATED MEMORY — disposition CLAIMED by this plan (review each): + - BDR-026 [in force] — plan honors it by: <how> + - LRN-050 [in force] — plan applies it by: <how> + - BLK-009 [already seen] — <how avoided / why N-A> + +Review the claims above — flag any item the plan does NOT actually honor. Approve and execute? (yes / request changes) ``` +This block EXPOSES each in-force item with the plan's CLAIMED disposition, for human +review — it does NOT auto-detect conflicts. An agent blind to a conflict won't list it; +forcing a per-item claim is what gives the reviewer the surface to catch it. A display, +never a guarantee (same discipline as the memory-commit `✅<hash>`: show what's true, never +assert a check not performed). No RELATED MEMORY from 0d → omit the block. Changes → back to STEP 2. Approved → continue. ## STEP 4 — IMPLEMENT @@ -175,6 +220,8 @@ The pipeline must handle these without aborting silently: |---|---| | STEP 0b — `CLAUDE.md` missing | STOP with the printed message ("Run `/onboard` first…"). Do not proceed. | | STEP 0c — `ctx7` not installed but fast-libs detected | Skip pre-fetch silently. During STEP 4, log `📚 ctx7 cache miss for <lib>` and continue with vanilla model knowledge. | +| STEP 0d — request too vague for analyzer to locate a code zone | analyzer reports ambiguous zones and does NOT block; STEP 1 brainstorm refines scope. The memory disposition is still produced. | +| STEP 0d — `.claude/memory/` absent (project not yet onboarded) | analyzer's guarded scan no-ops (the `[ -d ]` guard fires — a bare glob would error); proceed with the code ANALYSIS alone. STEP 7 creates registries later. | | STEP 1 — brainstorming returns "design unclear" twice | Escalate: ask user "Switch to /init-project (greenfield-style design) or refine the feature request?" | | STEP 3 — user replies "request changes" | Loop back to STEP 2 with user's notes. Cap at 3 iterations; on the 3rd "request changes" without approval, ask "Pause and rescope this feature?" | | STEP 4 — subagent crashes (tool error, not test failure) | Treat as STEP 4b error path, present hypothesis-led gate. |