Skip to main content
Let a command’s exit code decide what happens next, with no model in between. Use it for the steps that already have a right answer: a test suite, a build, a lint, a check against a deployed system. Where the judgement needs a reader, use a writer and a reviewer. A command stage is a code eval: it costs no tokens, and its result can do two things a model’s report can’t be trusted to do alone. It can carry the evidence to the step that owns the fix, and it can choose which path the run takes.

Shape

The two commands

The tests run as a command with a target. When the command exits non-zero, the step returns a revision request aimed at implement, with the captured output as the finding:
examples/command-kickback.ts (excerpt)
Pass a command as an array when an argument has spaces or quotes of its own. Each entry is one argument. The string form splits on spaces and refuses a quote. The graph then puts a second command, size, between the test and two reviews, and each review carries a when:
examples/command-kickback.ts (excerpt)
passed('size') runs one review when the command exited 0, failed('size') the other when it didn’t. Both read the named dependency’s outcome through ctx.needs; a name the node doesn’t need is a configuration error, not a quiet false. The deciding node is optional: true, so its red result blocks nothing and the branches decide. Without that, a red result would block every step that needs it and the failed branch could never run; the dag refuses that shape and says why. The step whose condition isn’t met is skipped and recorded as skipped. Leave when off any branch that must run, because a skip doesn’t fail the run. failed means the command ran and failed; a dependency that never got to decide meets neither condition. maxKickbacks limits how many times the writer runs again. A number is one count for the whole graph; a map gives each target its own. When a count is spent, the run fails with the last output as its reason instead of looping.

What the run did

The writer here is a function that gets add wrong once, so the red test has something to carry. Run it with npx tsx command-kickback.ts:
Output
implement ran twice: the first attempt subtracted, the test exited 1, and its output went to implement, which ran again with it in hand. The change was small, so size exited 0 and only quick-review ran. No agent read the test output to decide any of that. The exit code did.
examples/command-kickback.ts

Next steps

  • Feature delivery: a test command as one stage of a nine-stage team, aimed at the implementer.
  • Feedback loops: the limit, and the other shapes a check can take.
  • Runtime: commandJob, passed, failed and dag.