Goal-backward verification. Start from what the phase SHOULD deliver, verify it actually exists and works in the codebase.
Critical mindset: Do NOT trust SUMMARY.md claims. SUMMARYs document what Claude SAID it did. You verify what ACTUALLY exists in the code. These often differ.
Task completion ≠ Goal achievementA task “create chat component” can be marked complete when the component is a placeholder. The task was done — a file was created — but the goal “working chat interface” was not achieved.Goal-backward verification starts from the outcome and works backwards:
What must be TRUE for the goal to be achieved?
What must EXIST for those truths to hold?
What must be WIRED for those artifacts to function?
Then verify each level against the actual codebase.
Option A: Must-haves in PLAN frontmatterExtract from plans:
Copy
Ask AI
must_haves: truths: - "User can see existing messages" - "User can send a message" artifacts: - path: "src/components/Chat.tsx" provides: "Message list rendering" key_links: - from: "Chat.tsx" to: "api/chat" via: "fetch in useEffect"
Option B: Use Success Criteria from ROADMAP.mdIf no must_haves in frontmatter, check for Success Criteria:
Use each Success Criterion directly as a truth
Derive artifacts: For each truth, “What must EXIST?”
Derive key links: For each artifact, “What must be CONNECTED?”
Option C: Derive from phase goal (fallback)If no must_haves AND no Success Criteria:
State the goal from ROADMAP.md
Derive truths: “What must be TRUE?” (3-7 observable behaviors)
Check for orphaned requirements:If REQUIREMENTS.md maps additional IDs to this phase that don’t appear in ANY plan’s requirements field, flag as ORPHANED.
Status: passed — All truths VERIFIED, all artifacts pass levels 1-3, all key links WIRED, no blocker anti-patterns.Status: gaps_found — One or more truths FAILED, artifacts MISSING/STUB, key links NOT_WIRED, or blocker anti-patterns found.Status: human_needed — All automated checks pass but items flagged for human verification.Score:verified_truths / total_truths
// RED FLAGS:export async function POST() { return Response.json({ message: "Not implemented" });}export async function GET() { return Response.json([]); // Empty array with no DB query}
// Fetch exists but response ignored:fetch('/api/messages') // No await, no .then, no assignment// Query exists but result not returned:await prisma.message.findMany()return Response.json({ ok: true }) // Returns static, not query result// Handler only prevents default:onSubmit={(e) => e.preventDefault()}// State exists but not rendered:const [messages, setMessages] = useState([])return <div>No messages</div> // Always shows "no messages"