Skip to content
ƒtsforgev0.52.0
19

When the gate fails

3 min read

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.

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.

In short:

gate fails → model edits → gate runs again → repeat until pass or stop

The loop stops when one of these happens:

Stop reasonMeaning
DoneGate 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.

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 tsc and ESLint JSON output when available

Most users never touch this. It matters when tuning evals or reading --log JSONL output.

Big picture · How tsforge builds the gate · Model agent