When the gate fails
The gate is tsforge’s pass/fail test. The model edits code; tsforge runs the gate; if anything fails, the model sees a list of real errors and gets another turn.
tsforge never trusts “I fixed it” from the model. It trusts exit code 0 from the acceptance command. See How tsforge builds the gate for how that command is chosen.
What usually runs inside the gate
Section titled “What usually runs inside the gate”For a normal TypeScript repo, the gate combines:
- TypeScript (
tsc --noEmit) catches type errors - ESLint packs catch stack conventions (component layout, logging, Drizzle patterns, etc.)
- Meta-rules catch repo files ESLint does not usually lint (
package.json, CI workflows, missing test siblings) - Format and tests when the gate ladder includes them (common on web apps)
Stack detection picks ESLint packs from your dependencies. tsforge.config.json can turn packs or rules up/down.
The repair loop
Section titled “The repair loop”In short:
gate fails → model edits → gate runs again → repeat until pass or stopThe loop stops when one of these happens:
| Stop reason | Meaning |
|---|---|
| Done | Gate passed. Task complete. |
| Stalled (no progress) | The model keeps failing at the same thing. tsforge stops and hands back a blocker diagnosis. |
| Cap (runaway backstop) | A turn ceiling hit. Rarely reached; the no-progress guard stops first. |
tsforge’s primary stop is lack of progress, not a raw turn count. Two guards detect it:
- Same-error persistence: if one specific error (the same
file+rule) survives 5 consecutive fix cycles, tsforge stops, even if other errors are changing around it. The stop names the blocker:stuck on no-explicit-any in src/views/Foo/index.tsx after 5 attempts (last: …). Interactively, you get that diagnosis and the prompt back. The session stays alive, so you can re-steer. - Whole-set stall: a coarser net. The entire error set unchanged for 6 cycles.
The turn cap is only a runaway backstop now. A single high ceiling (1000 turns) shared by every mode, set far above any converging build. It’s a crash-guard, not a task limit: what actually stops a run is the no-progress guard above, not the turn count.
When the gate fails, tsforge sends structured errors (file, line, rule name, message) back to the model, not a generic failure blob. That is what makes repair workable.
Internal tracking (for debugging)
Section titled “Internal tracking (for debugging)”If you read logs or eval output, you may see:
- Error set: the list of failures from the last gate run
- Diff: which errors were fixed, newly introduced, or still open since the previous run
- Parsers: tsforge reads
tscand ESLint JSON output when available
Most users never touch this. It matters when tuning evals or reading --log JSONL output.