Architecture

State machine

State transitions

The lifecycle is:

discover -> understand -> validate -> plan -> execute -> verify -> review
                                      ^                    |
                                      └────── repair ──────┘

LifecycleMachine in packages/agent holds the transition table and refuses any move it does not define, so an implementation mistake becomes a thrown error rather than an unverified result that looks finished. Notably, executing cannot reach completed without passing through verifying, and planning cannot skip to reviewing. Every non-terminal state can reach failed.

Each stage owns one decision:

  • discover collects the checkout, its working-tree or pull-request base-to-head diff, and its native check commands through the runner.
  • understand asks the model to interpret untrusted feedback, proactively inspect the complete diff, or interpret an issue task in repository context.
  • validate decides the verdict from repository evidence, never from the reviewer's or the model's assertion.
  • plan records the plan and resolves authorization. Each refusal is a distinct reportable outcome rather than a silent downgrade.
  • execute applies changes restricted to the validated scope, through the runner.
  • verify runs the repository's own checks and captures their output.
  • review inspects the resulting diff before a run may call itself complete.

Repair re-enters plan with the failing output as context, until agent.maxAttempts is spent.

Proactive review is repository opt-in. Its model decision carries severity, confidence, cited evidence, affected files, and a change-risk classification. The runtime validates the evidence independently, then requires confidence and repository policy to allow the risk class. High-impact changes always stop at needs-human; proactive or autonomous writes use an isolated runner when policy requires it.

Verdicts and evidence

Validation lives in packages/agent/src/validation.ts and is independent of any provider. It rejects a claim that cites no evidence, names no existing file, or quotes repository content that is not there; it reports a supported but low-confidence claim as inconclusive. Rejection reasons are collected in full rather than short-circuiting on the first, because the report is the product.

TaskResult.verified is derived in exactly one place, at the point a run produces its terminal result: it requires a completed state, an applied change, and every executed check passing. No branch can assert verification it did not earn, which is what makes "a failed verification is never presented as success" a property of the code rather than a convention.

EvidenceBundle and its Markdown renderer live in packages/shared because both the source-control adapters and the CLI consume them, and because rendering is a pure function over contracts with no I/O. Terminal states map deterministically onto a provider-neutral run outcome in packages/source-control, which each provider adapter translates into its own status vocabulary — explicitly noting any conclusion the platform cannot express (see docs/source-control-providers.md).

Copyright © 2026