| name | batch-fix |
| description | Corrective batch of multiple small independent fixes (apply review findings, several/multiple fixes across >3 files, no new contract or architecture) — broader than fix, lighter than implement. |
| type | skill |
| user-invocable | false |
MTK Batch-Fix — Corrective Batch Loop
MTK File Resolution
MTK skills and shared references live either in the project (local install) or the plugin cache (marketplace install). Resolve once:
- If
$CLAUDE_PLUGIN_ROOT is set, prefix .claude/skills/ and .claude/references/ reads with it.
- Otherwise, if
.claude/skills/context-engineering/SKILL.md exists locally → project-relative paths work as-is.
- Otherwise, fall back to
find ~/.claude/plugins -maxdepth 8 -name "SKILL.md" -path "*/mtk/*/context-engineering/*" -type f 2>/dev/null | sort -V | tail -1 | sed 's|/.claude/skills/context-engineering/SKILL.md||'. If empty, MTK skills are unavailable — warn the engineer and proceed with CLAUDE.md only.
Always project-relative (never prefixed): CLAUDE.md, .claude/tech-stack, .claude/rules/, tasks/, docs/, .claude/references/architecture-principles.md, .claude/references/pre-commit-review-list.md, .mtk/ (workflow state). Resolve skills and scripts from the same root: a split (skills from a local dev checkout, scripts from the plugin cache) risks version drift — anchor both the same way.
Overview
A lightweight loop for a corrective batch — several small, INDEPENDENT fixes applied together (e.g. "apply these 5 review findings", "fix these things"). It sits between fix (1-3 files, one coherent change) and implement (new behavior, public contract, or architecture).
batch-fix is deliberately NOT an implement rigor tier: implement makes the full spec/plan/approval apparatus mandatory at every level, so lightweight mode lives here as a sibling. The lightness is fixed: a short findings-list spec stub + tasks/todo.md, one approval gate on the list, inline execution (no subagent-per-batch), no docs/plans/ plan file, and proportional review. Any finding that grows into a new slice, contract, or re-planning escalates that finding to implement (see Scope Guard).
Source of truth for the composed workflow:
.claude/skills/context-engineering/SKILL.md
.claude/skills/debugging-and-error-recovery/SKILL.md — for each behavioral finding
.claude/skills/test-driven-development/SKILL.md — when a finding changes behavior
.claude/skills/source-driven-development/SKILL.md — when framework behavior is uncertain
.claude/skills/security-and-hardening/SKILL.md — when a finding touches auth, audited state, secrets, or infra
.claude/skills/pre-commit-review/SKILL.md — always, before completion
.claude/skills/verification-before-completion/SKILL.md — before reporting done
.claude/skills/tech-stack-{stack}/SKILL.md — loaded based on .claude/tech-stack
When To Use
- Applying a batch of review findings ("apply these findings", "apply the review comments")
- A corrective batch of several distinct, unrelated small fixes ("fix these 5 things")
- Cleanup sweeps that touch more than 3 files but introduce no new behavior
Use batch-fix when the work is more than fix (>3 files OR multiple distinct fixes) but less than implement (introduces NO new public contract and needs NO architectural re-planning).
Proportionality check. If, after enumeration, the batch is almost entirely mechanical (renames, formatting, i18n/string deletes, comment moves) with no behavioral or boundary-crossing finding, the stub + gate scaffolding buys little. Prefer plain fix for the low-risk cosmetic sweep even if it stretches past 3 files, or keep the stub to one line per finding — the full ceremony earns its keep on batches where at least one finding is behavioral or crosses a boundary.
Do NOT use it when:
- The whole change is one coherent edit in 1-3 files → that's
fix.
- Any finding needs a new handler/entity/slice, a new/changed public contract, or architectural re-planning → that finding goes to
implement (Scope Guard).
- Findings are actually interdependent steps of one feature → that's
implement.
Workflow
Follow the phases in order. Each phase loads only what it needs.
Decision Graph
The per-finding scope guard is the part that gets skipped most. This graph makes escalation explicit — any yes on a red diamond means STOP editing that finding and escalate it, not "expand quietly."
digraph batch_fix_flow {
rankdir=TB;
node [shape=box, style=rounded, fontname="Helvetica"];
edge [fontname="Helvetica", fontsize=10];
start [label="batch requested"];
load [label="load context\n(CLAUDE.md, tech stack,\nrelevant refs only)"];
enum [label="enumerate INDEPENDENT\nfindings (numbered list)"];
single [label="really one\ncoherent 1-3 file\nchange?", shape=diamond];
tofix [label="hand to fix\n(Skill 'mtk', '<desc>')",
style="rounded,filled", fillcolor="#e8f0ff"];
stub [label="write findings spec stub\n+ tasks/todo.md"];
gate [label="ONE approval gate\n(AskUserQuestion;\nAUTO_PROCEED-eligible)"];
pick [label="next finding"];
contract [label="new slice /\ncontract /\nre-planning?", shape=diamond, style="filled", fillcolor="#ffe0e0"];
esc [label="STOP this finding — escalate:\nSkill('mtk', '<finding> —\nescalated from batch-fix: <reason>')",
style="rounded,filled", fillcolor="#ff9090"];
mech [label="mechanical\n(rename/format/\ncomment)?", shape=diamond];
tdd [label="add/update failing test\nfirst (TDD)"];
editf [label="edit in scope;\nmatch codebase pattern"];
more [label="more\nfindings?", shape=diamond];
review [label="proportional review\n(pre-commit-review always;\nspecialized reviewers if a\nfinding crossed a boundary)"];
verify [label="verify: build +\ntargeted tests\n(verification-before-completion)"];
report [label="final report:\nfindings, files, tests,\nescalations, build/test result",
style="rounded,filled", fillcolor="#e0f0e0"];
start -> load -> enum -> single;
single -> tofix [label="yes"];
single -> stub [label="no — multiple / >3 files"];
stub -> gate -> pick -> contract;
contract -> esc [label="yes"];
contract -> mech [label="no"];
esc -> more;
mech -> editf [label="yes — skip TDD"];
mech -> tdd [label="no"];
tdd -> editf -> more;
more -> pick [label="yes"];
more -> review [label="no"];
review -> verify -> report;
}
Red flags inside the loop:
| Rationalization | Reality |
|---|
| "It's just another small fix, I'll add the new endpoint too" | A new contract/slice is not a fix. Escalate THAT finding to implement. |
| "The batch is approved, I'll add finding #6 I just noticed" | The gate approved a list. New findings re-open the list — amend the stub + todo, don't smuggle. |
| "This finding changes behavior but it's tiny, skip the test" | Only mechanical findings (rename/format/comment) skip TDD. Behavioral findings get a failing test first. |
| "All of these actually need contracts" | Then it's not a corrective batch — route the whole thing to implement. |
| "It's really one change in two files" | Then it's fix, not batch-fix. Hand it down. |
Op-cost: each behavioral finding is roughly 3 tool ops (read test → edit test → edit source). Batches beyond ~5 findings can exhaust a context or op budget mid-loop — group trivially-related edits where safe, and if a budget warning fires, checkpoint with handoff and resume rather than pushing through a degraded window.
Phase 1: Load Context (Progressive Disclosure)
- Follow
.claude/skills/context-engineering/SKILL.md.
- Read
CLAUDE.md. If missing, stop and tell the engineer to run /mtk-setup.
- Load the active tech stack: read
.claude/tech-stack and .claude/skills/tech-stack-{stack}/SKILL.md (build/test commands, stack reference paths). If .claude/tech-stack is missing, do not halt: infer via bash scripts/setup-detect.sh --json (read-only) and load the matching tech-stack-{stack} skill; if inference is empty, announce degraded mode (stack build/test commands unavailable) and proceed with CLAUDE.md-only context.
- Read only what the batch needs — the coding guidelines always; the ORM checklist if a finding touches the data layer;
.claude/references/security-checklist.md if a finding touches auth/secrets/financial; .claude/references/testing-patterns.md when adding tests; .claude/references/pre-commit-review-list.md before commit. Resolve each via the File Resolution block (project → $CLAUDE_PLUGIN_ROOT → plugin cache); if a needed reference resolves nowhere, announce degraded mode for that check rather than skipping it silently — in particular, if pre-commit-review-list.md is absent the pre-commit gate still runs, AI-review-only (degraded), and must say so.
- Resolve and scan relevant lessons. Prefer the structured query when present:
LS="scripts/learnings.sh"; [ -f "$LS" ] || LS="${CLAUDE_PLUGIN_ROOT:-.}/scripts/learnings.sh"
bash "$LS" query --phase implement --files "<all target file paths>" --max 8
Falls back to scanning tasks/lessons.md when the script is absent.
Parallel loading: independent reference reads go out in one message. See docs/parallelism-patterns.md.
Phase 2: Enumerate Findings + Write Stub
- Triage the batch into a numbered list of independent findings. Each finding gets: a one-line description, the file(s) it touches, whether it is behavioral or mechanical, and whether it crosses a boundary (auth/secrets/data-layer/public surface). Independence is a precondition — if findings are ordered steps of one change, this is
implement, not a batch.
- If the whole thing collapses to one coherent 1-3 file change, hand it to
fix instead (Skill(skill: "mtk", args: "<desc>")) and stop.
- Resolve the batch slug — resumed vs. new. The SessionStart recovery pointer and any existing
docs/specs/*-batch.md may belong to a prior batch on the same feature, not this one. Before writing anything, list existing docs/specs/*-batch.md stubs; if one exists, compare its enumerated findings to this batch's. If they match, resume it (reuse the slug, reconcile tasks/todo.md). If they differ, this is a new batch — mint a distinct slug (<slug>-batch-2, -batch-3, …) and do not overwrite the prior stub. Never silently adopt a recovered spec whose findings you did not just enumerate.
- Write a short findings-list spec stub to
docs/specs/YYYY-MM-DD-<slug>-batch.md: the enumerated findings and a one-line scope note (no new public contract; no architectural change). This is a stub, not a full executable feature spec — no change_manifest apparatus, no batches, no docs/plans/ file, and no JSON sidecar. Then drop a scope-guard skip pointer so the PreToolUse guard does not fire on every edit (batch-fix scopes by the findings list, not a file manifest):
D="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
mkdir -p "$D/.mtk" && printf '%s\n' "batch-fix: <slug> — scopes by findings list, not a file manifest" > "$D/.mtk/scope-guard-skip"
If a shell-permission classifier blocks the > redirect, run just mkdir -p "$D/.mtk"; echo "$D" (no redirect) and write the same one-line content to <printed $D>/.mtk/scope-guard-skip with the Write tool. The guard reads the pointer's content and location, not the mechanism that produced it — so the Write-tool path is equivalent, and it still anchors under the resolved $D (not a bare project-relative path, which would miss the target from a worktree cwd).
Anchor .mtk/ the same way workflow state does ($CLAUDE_PROJECT_DIR → git top-level → cwd) so the guard hook finds the pointer even from a worktree or sub-dir cwd. scope-guard.sh no-ops while this pointer is fresh, regardless of which spec sidecar is newest. (An earlier version wrote a "freshest sidecar" JSON marker instead; that mis-fired on every edit whenever a concurrent feature spec was newer than the batch stub, so the guard anchored to the wrong spec's manifest.) Remove the pointer in the Final Report; it also ages out on its own (4h) if the run is interrupted.
- Write
tasks/todo.md: one checkable item per finding, plus post-batch review/verify items.
Baseline the working tree before editing. Snapshot what is already dirty so review can tell your work apart from it: run git status --porcelain (read-only) and record the files already modified/untracked that this batch does not touch. When the tree starts dirty, git diff HEAD is not "this batch's changes" — pre-existing edits would otherwise be attributed to the batch and draw false "missing test / missing X" findings for code you did not write (Phase 5 scopes review to the batch's own files using this baseline).
Phase 3: Single Approval Gate (STOP HERE)
Exactly one gate for the whole batch. Render the findings list and tasks/todo.md inline in the terminal (don't just cite paths), then ask via AskUserQuestion:
- Question: "Batch of N independent fixes enumerated. Proceed?"
- Options:
Approve & run until done / Approve (interactive) / Edit first / Revise.
Cite the stub and tasks/todo.md paths at the end as bare repo-relative paths.
MTK_AUTO_PROCEED opt-in. If MTK_AUTO_PROCEED=1 is set, the recommended option may be defaulted without an AskUserQuestion round-trip only when the findings list has zero open decisions and zero unresolved [ASSUMED] claims, and no finding is flagged as boundary-crossing/escalation-bound. Otherwise fall back to AskUserQuestion.
Gate already satisfied by an explicit directive. The gate approves this enumerated list. If the engineer has already given an explicit, unambiguous go-ahead on the exact list this batch enumerated — e.g. they said "do all N in one commit" in direct response to the same numbered findings (whether you wrote them to the stub this phase or enumerated them in a prior turn the engineer then approved) — the gate is satisfied, not skipped. Record it satisfied in the stub/todo (cite the approving message) and proceed to Phase 4 without re-firing AskUserQuestion; re-asking re-litigates a decision the engineer already made. This is narrow: it holds only when the approval names or directly answers the same findings list. A vague "just fix it" on the original request is not approval of a list you produced afterward — that still gets the gate. (Inferring approval from a vague request is what Critical Rule 1 forbids; honoring an explicit directive on the enumerated list is not that.)
If AskUserQuestion is deferred, load it with ToolSearch select:AskUserQuestion. If the harness does not expose it, stop and print the four options as one line and wait. Until the engineer answers: read-only Bash only, no edits. Do not start Phase 4 until an approval answer.
Phase 4: Execute Per Finding (Inline)
Work the findings inline in the main context — no subagent-per-batch. batch-fix does not assume a frozen tree: re-read each target file immediately before editing it — a concurrent human edit, or an earlier finding touching the same file, may have moved it since Phase 2. For each finding, in order:
- Scope Guard first (see below). If the finding needs a new slice/contract/re-planning, escalate it and move on — do not edit it here.
- Behavioral finding: follow
.claude/skills/debugging-and-error-recovery/SKILL.md to confirm the cause, then .claude/skills/test-driven-development/SKILL.md — write the failing test first, then the fix.
- Mechanical finding (rename, format, comment, dead-code removal with no behavior change): skip TDD; make the edit and rely on the existing suite + build.
- Match the local codebase pattern; do not gold-plate unrelated code.
- Check the finding off in
tasks/todo.md.
Phase 5: Proportional Review
Scale review to what the batch actually touched:
- Always: run
.claude/skills/pre-commit-review/SKILL.md (or the .claude/references/pre-commit-review-list.md gate) over the files this batch changed — the enumerated findings' files plus any touched during the run — not a blanket git diff HEAD.
test-reviewer — only if a finding introduced or changed public behavior.
architecture-reviewer — only if a finding crossed a boundary/slice (rare in a batch; a finding that genuinely needs one should already have escalated).
security-and-hardening — only if a finding touched auth, audited state, secrets, or infra.
A pure mechanical batch (renames/formatting) gets the pre-commit gate and nothing heavier.
Scope reviewers to this batch's changes. Hand every reviewer the batch's own changed-file set (the findings' files + any edited during the run) and exclude the Phase 2 pre-existing-dirty baseline. When the tree started dirty, a reviewer given git diff HEAD will attribute pre-existing edits to this batch — e.g. a "missing test" finding for code you never wrote. State the in-scope files explicitly in each reviewer's prompt.
Scope Guard
For each finding, if any of these become true, escalate THAT finding to /mtk implement — do not expand it in place:
- it needs a new handler/entity/slice
- it adds or changes a public contract
- it requires architectural re-planning
Per-finding escalation procedure:
- Summarize the finding, files identified, and why it exceeds a batch fix.
- Invoke the router with the finding description plus the discovered scope, using the marker the router catches:
Skill(skill: "mtk", args: "<finding description> — escalated from batch-fix: <short reason>")
- Do NOT edit that finding here. Record it in the stub/todo as
escalated → implement and continue the remaining findings.
- If MOST findings need contracts/slices, the work is not a corrective batch — escalate the whole batch to implement rather than piecemeal.
De-escalation: if the batch collapses to one coherent 1-3 file change, hand it to fix instead (see Phase 2 step 2).
Phase 6: Verify
Follow .claude/skills/verification-before-completion/SKILL.md. Using the active tech stack's commands:
- run the build command
- run the tests for every changed area (fresh execution evidence, not a claim)
- remove the scope-guard skip pointer now that edits are done:
rm -f "${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.mtk/scope-guard-skip"
Final Report
Report briefly:
- the numbered findings and their disposition (fixed / escalated → implement)
- files changed
- tests added or updated
- review performed (which reviewers ran and why)
- build + test result
Critical Rules
- One approval gate on the findings list before any edit — never infer approval from a vague request. The gate is satisfied (not skipped) only when the engineer has already given an explicit go-ahead on the exact enumerated list; see Phase 3.
- Findings must be INDEPENDENT; interdependent steps of one feature are
implement.
- A finding that needs a new slice/contract/re-planning escalates to implement — it is never "just another small fix."
- Behavioral findings get a failing test first; only mechanical findings skip TDD.
- Read before editing; match the codebase pattern; do not gold-plate.
- No
docs/plans/ plan file and no subagent-per-batch — that ceremony belongs to implement.
Verification