verify.sh — The Missing Feedback Loop for AI IDEs

Published September 4, 2026

Every AI IDE — Cursor, Claude Code, Copilot, Windsurf, Aider — has the same failure mode. You ask it to add a feature. It writes code. It says “done, I’ve added deleteUser to the users service.” You go to use it. TypeScript screams. Import missing. Wrong parameter type. Test file broken.

Then you paste the error back. It fixes one thing, breaks another. You iterate 4-5 rounds until the code actually works.

The problem is not the model. It’s the missing feedback loop. The model has no way to know its code doesn’t compile. It just trusts its own output.

The fix (two parts)

Part 1: verify.sh at the project root

#!/usr/bin/env bash
set +e
pnpm typecheck && pnpm lint && pnpm test --run

Swap the commands for your stack:

That’s it. A single command that runs your project’s actual checks and returns exit code 0 (green) or non-zero (something’s broken).

Part 2: One rule in .cursorrules / CLAUDE.md

## Verification protocol (closed-loop feedback)
- After ANY code change, run `./verify.sh` from the project root.
- If exit code is non-zero, read the output top to bottom.
- Fix the FIRST error only — do not attempt to fix everything at once.
- Re-run `./verify.sh`.
- Repeat until exit code is 0.
- Do NOT declare the task complete until `./verify.sh` returns 0.
- Never silence errors (`#[allow]`, `// eslint-disable`, `# type: ignore`) to make verify pass unless the error is a genuinely false positive AND you note WHY in a comment.

Why this works

Three things happen when you add this pattern:

  1. The AI gets ground truth. Before this, “done” was a model’s own claim. Now “done” is what the compiler says. Big difference.
  2. The AI iterates on real errors, not guessed ones. Instead of pattern-matching what a fix might look like, the model reads the actual TypeScript error and addresses it specifically.
  3. The AI stops declaring tasks complete prematurely. The rule “do NOT declare complete until exit 0” removes the escape hatch of “I think this should work.”

What it does NOT fix

Being honest — this isn’t magic. verify.sh doesn’t help with:

Time savings we’ve measured

Rough numbers from our own use (NJPTEAM):

Per-stack templates

We ship per-stack verify.sh scripts (Next.js, FastAPI, Rust+Axum) as part of our €15 Stack Pack. Each one is configured with the right toolchain for the stack and includes better error output formatting for AI consumption.

But the pattern above is 90% of the value. If you’re only using one stack, copy the 5-line version and go.

FAQ

Does this work with Cursor?

Yes. Cursor reads .cursorrules at the project root and applies it as a system prompt. Add the verification protocol block there.

Does this work with Claude Code?

Yes. Claude Code reads CLAUDE.md at the project root at every session start. Add the same block there.

Does this work with Copilot / Aider / Windsurf?

Partial. Aider reads a similar CONVENTIONS.md. Copilot’s rules support is more limited (.github/copilot-instructions.md). Windsurf has its own .windsurfrules file. The pattern still works, but adjust the file name for the tool.

What if my project doesn’t have a linter?

Comment out that line. verify.sh degrades gracefully — even just running the typechecker + tests catches most silent failures.

Won’t the AI just skip running the script?

Some models will. That’s why the rule is explicit: “Do NOT declare complete until exit 0.” Well-primed models (Claude Sonnet 4.6+, GPT-5-class) follow this reliably. If yours doesn’t, escalate the rule wording to be more emphatic.

Where to go next