A stub shipped as "done"
桩代码冒充“已完成”
The agent reports the feature complete, but a function body is `pass`, `return null`, or `throw new Error("not implemented")`.
Applies to: Claude Code · Cursor · Codex · Copilot agent
Symptom
The summary says done. The UI or endpoint exists. The core function behind it is a placeholder, a TODO, or returns a hard-coded value.
Why it happens
Agents scaffold first and fill in later. When the session runs long or hits a hard part, the scaffold survives and the final message describes the plan rather than the code. agent-review lists this as AR035 "Incomplete implementation"; the Agentic SDLC handbook describes the related "Almost Done" trap.
Example
From the agent-review taxonomy (AR035).
pythondef refund(order):
"""Issue a full refund to the customer."""
pass # placeholder; not what the diff claims
Copy-paste fixes
Pick the files for the agents you use. Snippets are in English so they work in any repo.
Claude Code: add to CLAUDE.md
CLAUDE.md## No placeholders in "done" work
- Never report a task complete while any changed code contains `pass`, `TODO`, `FIXME`,
`not implemented`, or a hard-coded return standing in for real logic.
- If you cannot finish a part, say so in the first line of your reply and list exactly what is stubbed.
- Before saying done, search the diff: `git diff | grep -nE "TODO|FIXME|not implemented|pass$"`.
Codex, Copilot coding agent and others: add to AGENTS.md
AGENTS.md## No placeholders in "done" work
- Never report a task complete while any changed code contains `pass`, `TODO`, `FIXME`,
`not implemented`, or a hard-coded return standing in for real logic.
- If you cannot finish a part, say so in the first line of your reply and list exactly what is stubbed.
- Before saying done, search the diff: `git diff | grep -nE "TODO|FIXME|not implemented|pass$"`.
Cursor: project rule
.cursor/rules/placeholder-shipped-as-done.mdc---
description: "AgentFails: A stub shipped as 'done'"
alwaysApply: true
---
## No placeholders in "done" work
- Never report a task complete while any changed code contains `pass`, `TODO`, `FIXME`,
`not implemented`, or a hard-coded return standing in for real logic.
- If you cannot finish a part, say so in the first line of your reply and list exactly what is stubbed.
- Before saying done, search the diff: `git diff | grep -nE "TODO|FIXME|not implemented|pass$"`.
CI step: fail if the diff adds placeholders
.github/workflows/no-placeholders.ymlname: no-placeholders
on: pull_request
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Fail on new placeholders
run: |
if git diff "origin/${{ github.base_ref }}...HEAD" -U0 | grep -E '^\+' \
| grep -nE 'TODO|FIXME|not implemented|NotImplementedError|^\+\s*pass\s*$'; then
echo "Placeholder code added. Finish it or open a tracked issue."; exit 1
fi
Shared /verify skill for Claude Code
Commit it to the repo so the whole team (and every agent session) runs the same checks. Replace the commands with your project's real ones.
.claude/skills/verify/SKILL.md---
name: verify
description: Verify the current change before calling it done. Use before every commit, PR, or "done" message.
---
# Verify
Run every step and paste the real output. Never summarize a step you did not run.
1. **Scope**: run `git diff --stat` and `git status --short`. For every changed file, say which part
of the request needs it. Any file you cannot justify gets reverted, or listed under
"Unrequested changes" for the user to decide.
2. **Build and types**: `npm run build` (or the project's build command).
3. **Tests**: `npm test`. Report pass/fail counts. If a test was edited, skipped or deleted,
say so explicitly with the reason.
4. **Behavior**: exercise the changed behavior once for real (a curl, a CLI run, a screenshot)
and show what happened.
5. **Report** in this format:
- Requested: <one line>
- Changed files: <list, each with its reason>
- Checks run: <command -> result>
- Not done / unsure: <list, or "none">