Convert the planner's soft comment-text guideline into a plan-write-time HARD GATE. When an acceptance criterion negative-greps for a literal (`grep -c 'LIT' file == 0`) and that same literal appears verbatim in an `<action>` body (JSDoc samples, head-comment references, "what NOT to do" snippets), the executor's commit-time verify gate later fails on the comment echo rather than a real regression — wasting cycles and training the executor to distrust the gate. `verify.plan-structure` (the `validate_plan` step) now scans for this: - confidently-extracted (quoted) negative-grep literal echoed in an <action> → error (valid:false), failing plan creation - unquoted/ambiguous grep target → warning (fallback policy) - `<!-- planner-discipline-allow: LIT -->` escape hatch skips a literal - positive-count gates (`== N`) and `!= 0`/`>= 0` are out of scope Adds the `<comment_text_discipline>` block to gsd-planner.md, the full rules + allowlist example to planner-antipatterns.md, and regression fixtures for downstream incidents 12-04, 11-04, 12-02 (plus a boundary case proving positive-count gate 11-02 is not flagged). Closes #429 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
5.8 KiB
Planner Anti-Patterns and Specificity Examples
Reference file for gsd-planner agent. Loaded on-demand via
@reference. For sub-200K context windows, this content is stripped from the agent prompt and available here for on-demand loading.
Checkpoint Anti-Patterns
Bad — Asking human to automate
<task type="checkpoint:human-action">
<action>Deploy to Vercel</action>
<instructions>Visit vercel.com, import repo, click deploy...</instructions>
</task>
Why bad: Vercel has a CLI. Claude should run vercel --yes. Never ask the user to do what Claude can automate via CLI/API.
Bad — Too many checkpoints
<task type="auto">Create schema</task>
<task type="checkpoint:human-verify">Check schema</task>
<task type="auto">Create API</task>
<task type="checkpoint:human-verify">Check API</task>
Why bad: Verification fatigue. Users should not be asked to verify every small step. Combine into one checkpoint at the end of meaningful work.
Good — Single verification checkpoint
<task type="auto">Create schema</task>
<task type="auto">Create API</task>
<task type="auto">Create UI</task>
<task type="checkpoint:human-verify">
<what-built>Complete auth flow (schema + API + UI)</what-built>
<how-to-verify>Test full flow: register, login, access protected page</how-to-verify>
</task>
Bad — Mixing checkpoints with implementation
A plan should not interleave multiple checkpoint types with implementation tasks. Checkpoints belong at natural verification boundaries, not scattered throughout.
Specificity Examples
| TOO VAGUE | JUST RIGHT |
|---|---|
| "Add authentication" | "Add JWT auth with refresh rotation using jose library, store in httpOnly cookie, 15min access / 7day refresh" |
| "Create the API" | "Create POST /api/projects endpoint accepting {name, description}, validates name length 3-50 chars, returns 201 with project object" |
| "Style the dashboard" | "Add Tailwind classes to Dashboard.tsx: grid layout (3 cols on lg, 1 on mobile), card shadows, hover states on action buttons" |
| "Handle errors" | "Wrap API calls in try/catch, return {error: string} on 4xx/5xx, show toast via sonner on client" |
| "Set up the database" | "Add User and Project models to schema.prisma with UUID ids, email unique constraint, createdAt/updatedAt timestamps, run prisma db push" |
Specificity test: Could a different Claude instance execute the task without asking clarifying questions? If not, add more detail.
Context Section Anti-Patterns
Bad — Reflexive SUMMARY chaining
<context>
@.planning/phases/01-foundation/01-01-SUMMARY.md
@.planning/phases/01-foundation/01-02-SUMMARY.md <!-- Does Plan 02 actually need Plan 01's output? -->
@.planning/phases/01-foundation/01-03-SUMMARY.md <!-- Chain grows, context bloats -->
</context>
Why bad: Plans are often independent. Reflexive chaining (02 refs 01, 03 refs 02...) wastes context. Only reference prior SUMMARY files when the plan genuinely uses types/exports from that prior plan or a decision from it affects the current plan.
Good — Selective context
<context>
@.planning/PROJECT.md
@.planning/STATE.md
@.planning/phases/01-foundation/01-01-SUMMARY.md <!-- Uses User type defined in Plan 01 -->
</context>
Scope Reduction Anti-Patterns
Prohibited language in task actions:
- "v1", "v2", "simplified version", "static for now", "hardcoded for now"
- "future enhancement", "placeholder", "basic version", "minimal implementation"
- "will be wired later", "dynamic in future phase", "skip for now"
If a decision from CONTEXT.md says "display cost calculated from billing table in impulses", the plan must deliver exactly that. Not "static label /min" as a "v1". If the phase is too complex, recommend a phase split instead of silently reducing scope.
Comment-Text Discipline (HARD GATE)
Enforced at plan-write time by
verify.plan-structure(thevalidate_planstep). Issue #429.
When an <acceptance_criteria> or <verify> block uses a negative grep — grep -c 'LITERAL' file == 0, meaning "this literal must NOT appear in the file" — that same LITERAL must not appear verbatim anywhere in an <action> body. Verbatim code blocks, JSDoc samples, head-comment references, and "what NOT to do" illustrations get echoed into the file the executor writes, so the executor's commit-time gate fails on the comment text, not on a real code regression. The work is correct; the gate output is semantically wrong; the executor wastes cycles and learns to distrust the gate.
The gate: plan creation FAILS (error, valid: false) when a confidently-extracted (quoted) negative-grep literal also appears in an <action> block. When the grep literal is unquoted and cannot be extracted unambiguously, the gate WARNS instead of failing (so you still get the plan, with the risk surfaced).
Bad — JSDoc sample echoes the forbidden literal
<task>
<action>
Add a `?from=` query param to the share link. Do NOT reintroduce the old
`?from=` referrer hack the JSDoc warned about. <!-- echoes ?from= -->
</action>
<verify><automated>grep -c '?from=' src/animal-detail.tsx == 0</automated></verify>
</task>
Good — rephrase the comment by concept
<task>
<action>
Add the share-link query param. Do NOT reintroduce the legacy referrer hack.
</action>
<verify><automated>grep -c '?from=' src/animal-detail.tsx == 0</automated></verify>
</task>
Allowlist escape hatch
When the literal MUST appear in the plan body verbatim — e.g. the plan documents the test file that exercises the gate itself, or the literal is part of the verification command's own grep regex — add a marker on its own line so the gate skips that literal:
<!-- planner-discipline-allow: ?from= -->
One marker per literal. The marker exempts only the exact literal it names.