Pipeline Steps
This is the per-step reference. For the overview and rationale, see Pipeline. For the fix loop, see Auto-Fix Loop.
intent → rebase → review → test → document → lint → push → pr → ciEach step can produce findings, request approval, trigger auto-fix, or apply safe fixes during its own pass. Steps that encounter fatal errors stop the pipeline. Every step that scopes its work to the branch’s changes (Review, Test, Document, Lint, PR drafting, CI repair, and repository gate fixes) first fetches the base branch’s live remote tip and computes the branch base against it; if that fetch fails, the step fails instead of falling back to a possibly stale cached base ref. Review, Test, Document, Lint, and repository gate fixes use the same effective PR base as Rebase and PR drafting: the run’s recorded --base-branch override wins over pr.base_branch, with the repository’s forge default branch as the fallback. Their scope is the full branch delta from the merge-base with that target, not the last pushed delta or integration-only changes relative to the default branch. Review and Document prompts name that effective base branch. Steps can also be pre-skipped when starting a run, skipped by the user, or skipped automatically by the pipeline.
Pipeline steps do not treat missing, malformed, or semantically incomplete structured analyzer output as a clean result. Such output never creates a gate that unattended AXI mode can accept.
The executor records requested step skips before executing any step. Those skips survive approval parks, daemon recovery, and revalidation. If recording a skip fails, no step executes.
When a step stops on an error that an agent invocation itself returned while its context was still live - a provider, transport, or agent-process failure - the executor records that cause on the failed step. A timeout, a cancellation, and a step’s own verdict on a successful invocation record none. Only that recorded cause makes a run that failed in Test or an earlier step eligible for no-mistakes axi retry, which carries the steps before the failed one into a new run and restarts the failed step from the beginning with round trigger retry.
The Test evidence analyzer first returns the validation errors to the agent for a bounded correction, and Review runs a fresh review up to three times in total when no-mistakes rejects the reviewer’s final output; exhausting either bound, and every other step’s invalid analyzer output, still stops the affected step.
Beyond these core steps, a repository can declare extra checks that run immediately after one of them. gates owns their placement, failure handling, and limits.
See TUI yolo mode for automatic gate handling and its exceptions.
Every pipeline agent invocation is prompt-steered to keep intentional writes inside the run worktree and avoid mutating system state outside it.
This is a soft boundary, not OS-level sandbox enforcement.
The steering still allows requested test evidence under the run’s managed evidence directory, plus incidental temp or cache writes from normal development tools.
Configured shell commands and one-shot agent subprocesses are scoped to their step: when the invocation exits, fails, or is cancelled, no-mistakes terminates remaining child processes it spawned so background workers do not outlive the run.
When configured Test, Lint, or repository gate command output exceeds 64 KiB, the complete output remains in the authoritative step log while findings, IPC responses, and repair prompts receive a valid-UTF-8 head-and-tail projection capped at 64 KiB. The truncation marker reports the exact original and omitted byte counts and points to no-mistakes axi logs --step <step> --full for the complete output.
Commits created by the shared Review, Test, Document, Lint, and operator-authorized repository gate fix path, plus CI repair commits, use the configurable commit.fix_message template, followed by any commit.trailers rendered for the agent invocation that made the change.
Correction and CI repair handoffs inspect the staged index after staging. An empty index succeeds without creating a commit, even if an earlier worktree status reported changes; a real git commit failure still fails the attempt. Staging never records a submodule pointer move: a populated submodule whose checkout no longer matches the recorded commit (for example after a rebase onto a base that bumped it) is left unstaged and logged as left submodule pointers as recorded, so a correction cannot revert the submodule bump. A populated checkout whose gitlink the head no longer records is left unstaged the same way unless the staged .gitmodules registers it, so a correction cannot re-add a submodule the base removed. If the agent already advanced HEAD, the handoff still records or publishes that head through the existing review and publication guards. Private mirror reconciliation owns the shared-ref preservation rules for that recording.
Review, Test, Lint, and operator-authorized repository gate repair agents, including Lint’s safe-fix pass when no command is configured, share a removal-first rule with the CI repair agent: when a problem can be resolved by removing a code path the intent does not strictly require, they remove it instead of validating, hardening, or documenting it. They judge necessity against user intent when present and otherwise against the change’s stated purpose, subject to the later human decisions described under Finding decision history.
Pipeline agent prompts prohibit independently initiated edits to AGENTS.md and CLAUDE.md, not review of memory-file changes already in the diff. Reviewers assess those changes like any other file: being changed is not itself a finding, but inaccurate content can be. A fix agent may edit memory-file content in the change under validation to address a finding or recorded human fix decision, without making unrelated edits. This is a prompt contract, not a file guard or a restriction on configured shell commands. Rebase and merge conflict resolvers may resolve conflicts in those files but make no unrelated edits; the document step’s own documentation work has the correction-only rule described under document.instructions.
The shared correction commits, and the Push step’s commit of leftover changes from a pipeline agent or formatter, are machine-authored records of pipeline output. Each is created with the complete local commit-hook family suppressed by combining --no-verify with an empty temporary core.hooksPath for that invocation, so pre-commit, prepare-commit-msg, commit-msg, and post-commit do not run. This lets a disposable run worktree commit a correction even when a tracked hook depends on generated untracked runtime files that do not exist there - the canonical case is core.hooksPath=.husky with a tracked hook that sources the absent .husky/_/husky.sh.
The suppression is limited to those correction-commit invocations. It does not change the repository, Git configuration, or daemon environment; CI repair commits and all other commit paths keep normal hook behavior. Pipeline gates remain authoritative; whether a CI repair returns through the local gates before publication is controlled by ci.revalidate_repairs.
Agent roles that can write, repair, or review tests reject tests whose only evidence is matching implementation source text, tokens, syntax, or incidental snapshots.
They instead require an executable interface or a typed or normalized semantic model that proves observable behavior.
Reading a file remains valid when that file is itself an owned output or data contract, and deterministic tests may inspect the final emitted agent prompt as a generated interface; model interpretation is reserved for development-only evaluation.
Tests must also use an independent oracle: an expected result from a specification, worked example, published constant, external contract, or independently justified property rather than the code under test. Agents should name the public behavior, the oracle’s source, and a plausible wrong behavior the test would reject. Tests that only check their own mocks, compare a result to itself, or copy the implementation’s expected-value rule can let a shared mistake pass. External-boundary mocks, constants, snapshots, and computed expectations remain legitimate when they check an independent contract.
Review flags every newly added source-content-only violation and requires same-pattern tests encountered directly in the accepted change’s scope to be removed or made semantic. Within that same scope, it also flags newly added or changed tests that lack an independent oracle and names the behavior, oracle source, and wrong behavior an in-scope replacement would reject, without expanding the change into a repository-wide test cleanup.
Finding decision history
When an operator resolves a findings gate with Approve, Skip, or Abort, or the axi run --yes loop approves it without selecting a fix, no-mistakes records that the round’s findings were declined. A gate with no findings records no decision. A fix response must follow the explicit accounting rules of axi respond --action fix. The decline set is derived from the complement of the recorded selection, excluding findings an earlier user round of the same step chose to fix; it has no storage of its own. A fix response that explicitly ignores findings without selecting any fixes records an empty selection, so those declines remain in the history. A finding previously decided in the same step keeps that decision when omitted; a later response cannot decline a finding previously chosen to fix. Findings merely left out by automatic filtering remain undecided.
Earlier decisions match finding content, not reusable finding IDs. An unrelated finding that inherits an old ID needs its own decision. Selected user-added findings participate in the same history and may be omitted at a later gate to retain their earlier decision.
Every gate response records caller-supplied origin metadata, independently of telemetry: auto_yes for the axi run --yes loop, interactive for explicit CLI and TUI responses, and unknown for older clients that omit it and historical rows. Fix responses with a selection store the origin beside the round’s selection; other responses (including Approve, Skip, and Abort) store it on the step. A fix response with nothing to select also stores its origin on the step. Origin is audit metadata only: it does not change gate behavior or selection_source semantics.
Before acknowledging a fix response, the daemon persists its selection, including normalized added findings and per-finding instructions. A failed write leaves the gate parked. If recovery parks the same round again, a subsequent fix response restores previously recorded selections, their instructions, and added findings into the fix dispatch, even when omitted from the new response. Restating an added finding restores it once under its recorded ID. New instructions for a restored finding supersede its recorded instructions.
Review, Test, Document, Lint, CI, and repository gate fix agent prompts receive a sanitized history containing the current step’s earlier rounds, decisions from other steps in the same run, and a bounded window of decisions from earlier runs on the same branch. Earlier fixes are resolved from the complete step history before that window is rendered, so a fix outside the window still prevents a later omission from appearing as a decline. A recorded decision takes precedence over conflicting user-intent wording, and later decisions about the same concern supersede earlier ones. The history never renders a finding an earlier round of the same step chose to fix under user_chose_to_ignore, whether the later round omitted it or resolved its gate with Approve, Skip, or Abort, so a later round cannot present one finding as both fixed and declined. The history instructs agents to respect every recorded decision - never implementing what a human declined and never reverting, undoing, or working around what a human chose to fix - and to report a genuine conflict rather than resolve it themselves. Completing Review does not clear branch decisions. Fix guidance from axi respond --instructions is recorded as driver_instructions, not as user instructions: the current step’s history shows it labelled as the driving agent’s guidance for that one fix round, and the decisions carried to other steps and later runs omit it, so no agent cites it as the user’s choice.
Current-step and uncertified previous-run history sections each have a 16 KiB prompt budget, including their warnings and omission notices. Oversized sections shorten or remove fix summaries before reducing finding descriptions and, if necessary, retaining only finding identifiers. When those reductions are insufficient, older finding lines and then other older lines are omitted, with explicit counts. This reduces prompt metadata only. It does not clear outstanding findings or certify prior fixer claims. The previous-run section retains its warning that the fixer commits were never certified.
This context is advisory and fails open. It tells agents not to implement or re-report a declined finding unless the current code introduces a materially different problem, but it does not block a step or commit and is not a reversion detector. Rebase fix prompts do not receive this decision history.
Intent
Uses explicit intent when a run provides it, including exact explicit intent inherited by a rerun, otherwise infers the author’s intent from recent local Claude Code, Codex, OpenCode, Rovo Dev, Pi, or GitHub Copilot CLI transcripts. This is best-effort context, and when available it is included in rebase fixes, review checks and fixes, test detection, evidence validation, and fixes, documentation checks and fixes, lint detection and fixes, CI auto-fixes, and PR drafting.
Behavior:
- Treats newly supplied explicit intent (
agent) and exact inherited rerun intent (rerun) as authoritative acceptance criteria, while preserving their distinct sources, and skips transcript-based inference even whenintent.enabledis false - Runs transcript-based inference only when
intent.enabledis true - Matches local agent transcripts against non-deleted changed files when present, falling back to all changed files for all-deletion diffs, may use the configured pipeline agent to disambiguate plausible matches, and summarizes the likely author intent with that agent
- Stores the derived summary, source, session ID, and match score on the run
- Logs accepted candidate diagnostics, including source, session, CWD, score, confidence, overlap, decision, and acceptance reason
- Logs the matched source, score, and sanitized inferred intent when a transcript matches
- Skips instead of failing when disabled, no matching transcript is found, the diff is empty, extraction errors, or persistence fails
This step does not block the pipeline for missing transcripts, summarization that exceeds the five-minute extraction cap, or other extraction failures, which are reported as skipped outcomes. It can fail the run only if cleanup fails after the disambiguation agent leaves worktree side effects.
Rebase
Fetches the latest authoritative remote state, fetches the configured pushed-branch target, and integrates your branch with those refs - by rebasing onto them, or by merging them in when rebase.strategy: merge is configured.
The integration branch used below is the effective PR base described in the scope rules at the top of this page.
Behavior:
- Fetches
origin/<PR base branch>from the remote into the worktree, and also fetches the pushed branch for non-base branches unless the push rewrote branch history. A failed base-branch fetch fails the step before any rebase or head update, rather than integrating against a possibly stale cachedorigin/<PR base branch>; the post-integration empty-diff check reuses that same fetched ref - Without fork routing, the pushed-branch target is
origin/<branch> - With GitHub fork routing, the pushed-branch target is the fork branch fetched into
refs/remotes/no-mistakes-push/<branch> - If the branch is not the PR base branch, tries rebasing onto the pushed-branch target first, then
origin/<PR base branch> - If the push rewrote branch history, skips the pushed-branch rebase target so prior remote autofix commits do not get reintroduced
- If the push rewrote the PR base branch and
origin/<PR base branch>advanced after that rewrite, pauses for manual approval before updating the branch - If the local default tip equals the branch
HEAD, treats those local-only commits as the intended delivery work and continues - If the local default tip is a strict ancestor of the branch
HEAD, pauses with anask-userfinding instead of silently bundling potentially unrelated local work into the PR - The local-default check is best-effort and only fires when the local default tip is ahead of
origin/<PR base branch>and a strict ancestor of the branchHEAD - Skips targets that don’t exist or are already ancestors
- If a fast-forward is possible, does a hard-reset instead of a rebase
- If the diff against the PR base branch is empty after rebase, completes rebase and skips all remaining pipeline steps
- On conflict: records conflicting files, aborts the rebase, and reports findings
- Under
rebase.strategy: merge, every step above is unchanged except the integration itself: each target is merged withgit merge --no-ffrather than rebased onto, so the head the pipeline reviewed stays on the branch as the merge commit’s first parent, publication is a fast-forward rather than a force-push, and the merge commit’s two parents keep the conflict resolution auditable afterwards. The skip, fast-forward, force-push and bundled-local-commit rules are the same in both strategies - Under
rebase.strategy: merge, a repaired conflict must leave the merge behind: after the agent finishes, the step checks that the branch moved and that both the head it started from and the fetched target are in the resulting history, and fails the step if the agent ended the conflict any other way - leaving the merge unconcluded, leaving a rebase in progress, aborting it, or replacing it with a rebase or a reset onto the target - rather than recording an integration that did not happen. A rejected shape is not left checked out: the worktree is reset back to the head that integration started from before the failure is reported, and a restore that does not land exactly there, or that leaves an interrupted rebase underneath it, is reported inside the same error rather than described as recovered - Bounds the conflict-repair agent with
agent_timeout: a silent turn is cancelled at that stall budget, a still-working one continues past that budget only when its still-working cap is set, and cancellation fails the step with a timeout diagnostic rather than leaving the run active indefinitely
Auto-fix: when enabled, the agent resolves conflict markers, stages files, and runs git rebase --continue (or, under rebase.strategy: merge, git commit --no-edit) in a non-interactive Git environment so Git accepts the existing commit message instead of opening an editor. The prompt includes user intent when available. Manual fix rounds also include any per-conflict user notes, any selected user-authored findings from the TUI or AXI interface, and sanitized prior-round history in the prompt. The Rebase step does not synthesize a fix commit subject; git rebase --continue preserves the rebased commits’ subjects. Under rebase.strategy: merge the resolver prompt additionally requires an additive resolution - keep both sides’ introduced content, never delete what one side introduced merely to make the merge apply - because the resulting merge commit is what makes that claim checkable afterwards. The step does not synthesize a merge subject either; --no-edit keeps Git’s own Merge remote-tracking branch ... message.
Default auto-fix limit: 3.
Review
AI code review of your diff. This is probabilistic evidence, not a security or compliance certification, and does not replace deterministic repository-owned authorization and privacy tests, static analysis, threat modeling, or human security review.
Behavior:
- Diffs the base commit against head
- Filters out files matching
ignore_patternsfrom the repo config - Sends the filtered diff to the agent with structured review instructions and a structured output schema
- Requires the
reviewed_pathscoverage record before a clean review can certify the head: the exact changed files the reviewer actually read and judged. On a clean review it must exactly cover the trusted reviewable set computed from the current diff (the paths that surviveignore_patterns); omitted, empty, partial, fabricated, out-of-scope, or mixed coverage parks the round for approval instead of certifying the whole head, and the step log names the files left unverified. The field is optional in the output schema only so an older payload still parses; an absent record is treated as no coverage, never as a pass. During carry-forward verification, omitted or invalid coverage likewise cannot clear an outstanding selected finding. - Before parking a clean round on an incomplete coverage record, runs one focused coverage completion turn over exactly the uncovered files and merges its findings and coverage record into the round’s. The completion prompt is the full review prompt plus the uncovered-file list, so the merge preserves the strict coverage rule: whatever is still uncovered (and any fabricated or invalid entry from either turn) parks with the remainder named. The turn is skipped for rounds that already carry blocking findings or an already-invalid coverage entry, is auditable under the
review-coveragepurpose, and any failure of the turn parks on the incomplete record rather than failing the run. It can still raise a review question (see the review conversation, below) or, when it reports a finding of its own, a risk assessment at least as high as the round it completes; either overrides the round’s own record instead of being lost, while a completion turn with no remaining finding never raises the round’s risk. Branch-controlled filenames in both coverage prompts are escaped so they cannot be read as extra review instructions.eval replayskips the turn: it scores findings against captured gold and never consumes the coverage certification, so the replay runs exactly the captured review pass. - When the reviewer’s final output is rejected, reruns the review with the same prompt plus a note quoting the validation error, up to three attempts in total; the rerun is a fresh, session-free review, except in a pass that holds a reviewer session, where it stays in that session, which already holds the work it just did
It reruns when the agent adapter marks that output as rejected against the schema, which covers Pi when its final JSON fails validation (for example a missing required
risk_level, ortestedgiven as a boolean) or, on its strict output-tool path, when the run ends without calling that tool (see Pi) and opencode once its own internal StructuredOutput retries run out, and when Review’s own checks refuse the output. Claude Code’s--json-schemare-prompts within its own limit and reports exhaustion aserror_max_structured_output_retries; that result is not marked as a rejection, so it fails the step as before. Findings come only from the attempt that validates, and nothing else from a rejected attempt carries over. Exhausting the attempts fails the step as a parse failure, so an unreadable review is never treated as a pass. When the adapter’s schema validator attributed the rejected attempts to at least two distinct schema fields (array indices ignored, so the same field failing on different findings counts once), the error says the output kept failing validation across distinct fields, names those fields, and lists each attempt’s error, so a reviewer and model combination that cannot produce schema-valid output is distinguishable from a one-off slip. The same applies to every post-fix rereview, while the fixer’s own turn is not retried. The review analyzer’s own formatting retries do not retry ordinary agent failures (exit, timeout, cancellation); when globalagent_crash_retriesis enabled, the executor may retry an eligible agent process exit or default invocation-timeout cut for this step under that setting’s same-run proofs - Appends the
review.path_instructionsblocks whose glob matches at least one changed file, in configured order, each labelled with its ownpathand the files it matched so a scoped rule cannot read as a repository-wide instruction; a change that matches nothing, or a repo with none configured, gets the prompt unchanged. Machine-local rules from the operator’s global config are matched the same way and appended first, each source under its own labeled heading - Selects those blocks against the complete changed-file list rather than the
ignore_patterns-filtered one, so a pushed-branch ignore entry cannot suppress a rule, and reads the repository’s blocks from the trusted default-branch config copy regardless ofallow_repo_commands - Logs which of those rules it applied and which matched no changed path
- Includes user intent when the run has supplied intent or transcript matching found a relevant local agent session; the detailed provenance semantics are documented in Intent extraction
- Treats authoritative intent as enforceable for source-verifiable acceptance criteria, except where a later recorded human decision supersedes conflicting original intent; it does not report the absence of a remote branch, push, pull request, or CI state that this run’s later Push, PR, or CI step owns
- Treats conformance with those criteria as necessary, not sufficient: an authoritative intent obliges flagging contradictions but never substitutes for checking that the algorithm is correct
- Removes any returned finding whose sole claim is that one of those same-run delivery outcomes is not present yet, while keeping findings about pre-existing or external pull requests, third-party artifacts, and lifecycle state that the current run does not own
- Keeps the later Push, PR, and CI steps responsible for strictly validating their own outcomes after review completes
- For any new or changed logic, constructs at least one concrete input or state and traces it, looking for a case that produces a wrong result without erroring; a computation that returns a wrong value, label, or set without failing is in scope
- When changed behavior processes potentially protected resources or user data, traces identity and unauthenticated reachability, authorization at the earliest shared boundary used by every caller, ownership/role/tenant/organization/admin scope and alternate call paths, public serialization, secondary disclosures through projections, caches, logs, telemetry, errors, exports, or generated artifacts, and fail-open, missing-context, preview/bypass, or stale-authorization behavior
- Reports an authorization or privacy finding only from a concrete source-backed operation or disclosure path, naming the protected resource or field, missing control, and unauthorized impact; equivalent controls and intentionally public data are accepted, and the absence of middleware, a specifically named authorization call, or an auth-related test is not evidence by itself
- Leaves access policy to repository project instructions and trusted
review.path_instructionsrather than inventing it; when changed behavior introduces a concrete material operation or disclosure involving potentially protected resources or user data whose permissibility is not established, it must use the existingask-useraction and name the missing policy decision, while immaterial or pre-existing ambiguity is not reported and a source-proven routine defect retains the existingauto-fixsemantics - For changes that claim a durable bug fix, reconstructs the concrete failing sequence and required invariant, inspects relevant sibling paths and shared state transitions, and reports an inadequate fix only when source evidence proves the same authorized failure remains reachable; the recommendation targets the earliest supported shared boundary
- Does not treat code shape or duplication alone as evidence of a systemic defect, demand speculative redesign, block explicitly authorized short-term containment merely because a later durable fix is possible, expand the user’s scope, or promote optional improvements into blockers
- Reports a finding only from a concrete sequence that occurs during the change’s intended usage; a rare but real sequence under that usage still qualifies, while a hypothetical unused path that intended callers, the public API, or documented usage never take does not
- Runs a dedicated Simplification pass over every component the change introduced (a new branch, acceptance or matching path, fallback, alias, mode, flag, option, second definition of a concept the code already defines once, or parallel copy of a rule) and judges each against the stated intent, or the change’s own stated purpose when no intent is available; a component not strictly required to satisfy that intent is reported as a
warningwith actionask-userthat names the component and recommends removing it, never hardening, validating, or documenting it, and a defect finding located inside such a component names removal as its remedy. Refactor-only simplification opportunities (deduplication, clearer control flow) keep their existing meaning and never remove features - Agent returns findings with severity (
error,warning,info), file location, description, and anaction(no-op,auto-fix,ask-user) - Reports a defect class once: a defect finding is anchored to one primary file and line and enumerates, in that same description, every other place in the changed code where the same invariant is violated or must hold (another axis, direction, or representation; a sibling call path, command, action, or state transition; another consumer of the same input, field, or record), each as file:line, and an incomplete-validation finding lists every consumed field still unvalidated, so one fix round can close the class instead of the next review reporting the sibling the fix left behind
- Also returns a
risk_level(low,medium,high) andrisk_rationale; when findings carry forward across Review rounds, the PR’s Risk Assessment follows the latest round that reports arisk_level, and a round that reports none keeps the stored one - Keeps an append-only outstanding finding set across Review fix rounds. Selecting a finding for a fix hands it to the fixer but does not remove it; it clears only when a later rereview lists that finding’s file in
reviewed_paths, the path belongs to the trusted reviewable set for that round, and the rereview reports neither the finding nor any other finding in that file, or when the operator explicitly approves, skips, or aborts the gate. Silence, missing or out-of-scope coverage, a rereview of another file, and a shifted or reworded report in the same file all remain outstanding. One compat carve-out: a selected finding with no file anchor can never match a coverage record, so it clears on a positive verification round whosereviewed_pathscovers every reviewable path and that no longer reports it - upgrade compatibility for a pre-removal recorded-decision finding synthesized from a source finding without a file anchor. In addition, any file-less finding in the current rereview blocks positive verification of every selected finding in that round. This applies to user-authored findings too, and a selectedinfofinding can therefore keep the gate parked even though an unselected informational finding is non-blocking - Runs every review turn - the initial review and every full rereview - as a fresh, session-free invocation, unless trusted
review.conversation: trueandsession_reuseare both on, where one session spans exactly one review PASS (the asking turn plus the finalize turn that receives its answers) and never a code change: either way the rereview that certifies a fix round never resumes the session whose findings prescribed those fixes, because the session identity is dropped before any fix round, and the finalize prompt is the complete review prompt plus the answers so a failed resume degrades to a cold review rather than a meaningless one; the rereview prompt additionally reframes fix-round changes as pipeline-authored code to review under the same adversarial standard as the author’s changes, with prior findings, fix summaries, and same-round tests treated as claims rather than evidence; and when a defect it reports is in code a prior fix round changed, or is a sibling site of an invariant a prior fix round addressed, it says so in the description, naming the round and whether that fix introduced the defect, left the sibling behind, or moved the defect, and lists every remaining sibling site; when the defects it finds sit in prior-fix-round code that exceeds what the original finding required, it reports a singleask-userfinding recommending that round be reverted to the minimal fix instead of filing further repairs on that code - With trusted
review.conversation: true- off by default, and the gate for every conversation bullet that follows - emits each substantiated larger question to the run’s review conversation the moment it has one, keeps reviewing other areas while a question is open, and re-reads answers at its own checkpoints so an answer that arrives mid-pass can redirect the rest of it; minor questions it decides itself, as before - Parks in waiting-on-answers when a pass ends with an unanswered question, carrying one
ask-userwarning per open question; the agent turn has already ended, so the parked wait consumes noreview_agent_timeoutand is accounted as park time.no-mistakes axi answer --question <id> --answer "..."records each answer, and once none are open the reviewer is resumed - in that pass’s own session when one is held, cold with the answers in the prompt otherwise - to finish the pass; approve and fix are not how a question is answered - Emits a single
ask-userwarning (review-questions-unreadable) INSTEAD of any per-question row when the reviewer’s question history could not be read in full, whatever remains open, because answers are refused for such a conversation and every per-question row would instruct anaxi answerthe daemon is guaranteed to refuse; the warning names the ids and count of the questions that were still open in the part of the history that was read and points at the run’squestions.ndjsonas the full record. It deliberately carries no review-question category, so nothing resumes a reviewer off a history it could not read, and both automatic resolvers (axi --yesand the TUI’s yolo) stand aside on it by finding ID while a human’s own approve, fix, skip, or abort stays allowed - Records every answered question per branch, so a later cold reviewer - including one in the run an author’s own fix push started - receives them as a settled-questions section it must not re-raise, rendered separately from the acceptance criteria
- Receives the most recent other run’s review rounds on the same branch as a superseded-rounds section, so a run that replaced a parked one still sees what was found and answered; it carries no pipeline-authored provenance framing and does not characterise who wrote those commits, because that framing is only ever added when this run is itself a fix round or carries an uncertified range, and the selector is unfiltered by run status - a previous run that took a fix round and completed leaves the fixer’s commits inside this run’s scope
- Enforces no limit on review fix rounds, by design
- When a review-step fixer round commits and its re-review does not complete, persists that branch’s uncertified commit range (lint and document fixer commits do not); the next run’s initial review of that range receives the same pipeline-authored provenance framing so the replacement reviewer is not cold. A later rebase remaps the persisted SHAs onto the rewritten head. The range is cleared only after a completed review whose approved head equals or descends from the range tip; parked, failed, skipped, and aborted reviews leave it in place
- With the default
session_reuse: true, Claude, Codex, Grok, Pi, and Antigravity reuse one durable fixer session across review-fix turns when every configured fixer for the run supports resume. A later-round fixer that cannot resume makes all fixer turns cold; a resume failure retries the same fix turn in a fresh fixer session, and unsupported agents run cold. The global configuration reference owns the role-selection rules. - Bounds each agent turn independently with
review_agent_timeout: the optional fixer, its fresh session-free rereviewer, and a finalize turn resumed after a park each receive the full stall budget, as do every later fixer and rereviewer; a silent turn is cancelled there, while a still-working turn continues past that budget only when its still-working cap is set, and cancellation fails the step with measured last-activity or no-output evidence rather than leaving the run active indefinitely - Atomically records the exact commit examined when a full review completes successfully; a parked review retains its candidate only for recovery, while failed, skipped, superseded, and legacy reviews grant no inferred approval authority
Approval: required if any finding has severity error or warning, any finding has action: ask-user, or a Review fix round leaves a selected finding outstanding. The last condition applies regardless of severity; an unselected info finding remains non-blocking. Findings with action: ask-user pause for approval instead of entering the normal auto-fix loop. This is for findings that challenge the author’s intent, or whose smallest honest remedy would extend the change rather than correct it, not routine correctness, reliability, or security fixes that may need to re-add a small amount of deleted logic. With the default auto_fix.review: 0, blocking review findings park for approval even when their action is auto-fix; setting repo or global auto_fix.review above 0 re-enables the automatic review fix loop for eligible auto-fix findings. Findings with action: no-op are informational only. The shared finding-action model owns the behavior for a missing action.
Auto-fix: the agent receives the selected previous findings plus any per-finding user notes, any selected user-authored findings from the TUI or AXI interface, and the shared finding decision history, including earlier fix summaries for this step.
The fixer’s unit of work is the invariant a finding violates, not the reported instance: before editing it states that invariant and enumerates every place in the changed area where it must hold (every axis, direction, and representation; every sibling call path, command, action, and state transition; every consumer of the same input, field, or record), then closes all of them in the same round with the same small correction or at the one shared boundary that makes them all hold, because a fix that closes only the reported site and leaves a sibling reachable is reported again by the next review.
Closing sibling sites is the fix; the fixer does not grow it into machinery, preferring to address a deeper architectural reason and simplify it over adding handling, state, fallbacks, retries, or a subsystem for the symptoms.
After applying its fixes and before verifying, it re-traces each finding’s failing sequence through the edited code and the ordinary path through every changed function and its callers, and removes any alias, branch, parameter, or helper the fix made unreachable.
It follows the shared removal-first rule described above; the Review-specific guard against reverting the author’s intentional code protects only code the intent requires, while genuine doubt about whether the intent requires a path leaves it in place and reports the finding unresolved.
It applies all selected fixes before running one focused verification limited to the changed area, and it is instructed not to run the complete repository test or lint suite during the fix round.
The dedicated Test and Lint steps after review remain the authoritative gates, although their coverage may be focused when commands are unconfigured.
If the tool for that verification is missing, the fixer still applies every fix, skips the verification, and reports it in an optional verification_blocked result field; the step log then records either fixes committed unverified, verification blocked: ... (also when the fixer ran git commit itself) or, when the round left HEAD and the worktree unchanged, agent made no edits and reported verification blocked: ..., which is distinct from the ordinary “no agent changes to commit”. The fixer has no prepare hook: commands.prepare is the shared hook for the configured Test, Lint, and format commands, test.prepare is only the Test opt-in that also prepares agent-only Test, and review.path_instructions reach the reviewer, so a toolchain the fixer needs but the run worktree lacks has to be named in the repository’s project instruction file or in a fix-request note.
Follow-up review passes use the history to avoid re-reporting user-ignored findings unless the code now has a materially different problem.
Default auto-fix limit: 0.
Coverage continuation
A Review gate with findings: [] and valid, incomplete stored coverage accepts axi respond --action complete-coverage.
The action starts a fresh static review turn over only the missing paths and merges its coverage with the retained record.
It records a coverage round without a finding selection, code fix, approval, or question answer.
The remaining pipeline gates still apply.
The request binds to the observed run head, step result, and latest round. The stored record must also match the run ID, recorded run base, resolved review base, trusted configuration SHA, and complete reviewable path set. The worktree must be clean at the bound head. The pipeline checks the stored source before and after the reviewer turn. Stale or concurrent requests cannot dispatch another turn against the same gate. Legacy records without pipeline-written provenance remain ineligible and receive no new coverage credit.
Invalid coverage, source changes, or an incomplete union keep Review gated. New findings and questions follow the ordinary Review gate rules. A still-valid partial result can support another explicit coverage continuation when its findings remain empty. Fully recorded parked gates can use the same action after daemon recovery only when their source bindings still match. Existing evidence remains in place.
Pipeline HEAD continuity
At entry to every repository gate and every core step from Test through CI, no-mistakes compares the live worktree HEAD with the pipeline-recorded head. An equal head or a pipeline-descendant commit continues. A backward reset, divergent sibling, or unverifiable relationship fails the run before that step performs work, including for steps that would not create a commit.
Test
Runs targeted local validation of the change and requested intent, then gathers evidence for that intent.
Local Test is never a repository-wide regression-suite substitute; broad regression is owned by remote CI and remains mandatory before a PR is ready.
commands.test owns the configuration contract for any explicit baseline command.
Behavior:
- Before a configured test command, runs
commands.prepareonce for the isolated worktree if configured; later configured lint/format commands share that successful preparation. With trustedtest.prepare: true, agent-only Test also prepares before its first agent turn, including repairs. This opt-in is eager (even if Test later reports no surface); default agent-only behavior is unchanged. - If
commands.testis set in repo config, runs it first as a baseline via the platform shell (sh -con POSIX,cmd.exe /con Windows) and captures output. Non-zero exit produceserrorfindings and parks the Test step. Approving that gate records an explicit override on the step (step_results.override_reason) and copies it onto the PR attestation assteps[].override_reason; therequire-no-mistakescheck treats that as non-compliant unless trustedtest.allow_approve_over_failureis set. Configure a targeted command here (see repo-config); do not treat this field as CI-parity complete-suite configuration. - Machine-local
repository_overrides.commands.test.additionalchecks run after the baseline, each in its own shell; a failing one produces anerrorfinding naming that local check, never the committed command. The global reference owns their priority and declaration rules. - With trusted
test.base_attribution: true, a failingcommands.testis re-run on the run’s base commit in a disposable clone, and the findings summary and evidence prompt name which failures the change introduced and which already fail on the base. The finding, its severity, and the override recorded on approval are unchanged. - After the baseline passes, fails, or is absent, always invokes the evidence agent. The agent derives a proportionate list of named end-user scenarios from user intent and the change, stands up the real product, and drives each scenario end to end. Repository-specific startup guidance can be supplied through trusted
test.instructions, and a matching machine-localrepository_overridesentry can add the operator’s own in a separate labeled section before it. The repair prompt carries both sections in the same order. - When a live scenario drives a TUI through a pseudo-terminal, that pty must receive a non-zero window size (
TIOCSWINSZ) before the TUI’s first grid read, and the master must be drained.script(1)and a bareforkpty()/pty.fork()from a non-tty parent otherwise yield a 0x0 grid; the TUI then exits immediately with a symptom such asterminal reported a zero-sized gridand never registers, so a live UI check silently becomes a fake while the pipeline still passes. - Each scenario records
name,result(pass,fail, oruntested),live,evidence, andreason; the overallverdictisgo,no-go,inconclusive, orno-surface.liveis true only when that scenario was driven against the real running product in this run: it describes execution, not tenant or production liveness. An actually executed local product or real SDK against a localhost emulator with synthetic inputs counts as live for that local scenario, not as tenant, mailbox, Graph, or production validation. Name the local surface and synthetic inputs in the scenario name and reason, and cite what actually ran in evidence. An unexecuted tenant or production scenario remainsuntestedwithlive: falseand a reason, even when a local scenario passed. Bothpassandfailrequirelive: trueand evidence. Unit tests, stubs, mocks, recorded fixtures, and code inspection are not live. The agent owns getting each scenario live: when a scenario needs an environment, instance, account, or data that is not provided, it is instructed to build a disposable one itself and point the real product at it, keeping everything it builds isolated from the operator’s real data, configuration, and shared services. If a needed tool is missing fromPATH, the agent can obtain, install, or build it inside the disposable workspace and use it there, but cannot search the host for it or install it system-wide or globally. A scenario isuntestedonly when live validation is truly impossible after those workarounds, including workspace-local tool setup, with a reason stating what was tried and why it cannot work (or, underno-surface, why there is no live product surface), never a guessed pass.no-surfaceis for a change with no runtime product no-mistakes can drive live (CI-workflow-only, docs-only, a pure non-runtime refactor, or anything else with no live-exercisable scenario); every scenario must be untested and not live, or the payload is rejected rather than treated as a skipped live validation. - The evidence payload must contain a non-empty scenario list and a verdict, use the exact lowercase vocabulary above, include every declared scenario field, provide evidence for each pass or fail, and avoid a fail scenario with a verdict other than
no-go. Findings from runs recorded before this contract remain readable and render without scenario details. - Evidence and repair agents must await required builds, browser runs, and other checks, retain their evidence, and read their actual exit statuses. They must stop disposable browser and server children before returning success. An early structured result or background launch does not establish completion. Missing completion or evidence requires an
inconclusiveevidence verdict, while a known failure requiresno-go. - Native Test invocations reject an otherwise successful agent exit if its owned POSIX process group or Windows Job Object still contains children. They also reject success when that completion check cannot be verified, before terminating remaining children. This check does not establish exit statuses for detached checks or change the agent’s schema-enforcement mechanism.
- An evidence payload that violates that contract is rejected. A fresh, correction-only analyzer invocation receives the rejected payload and specific validation errors as untrusted data; it cannot use tools, rerun scenarios, or perform external operations. It preserves supported observations and downgrades unsupported pass or fail claims to
untestedrather than inventing evidence. The step allows two extra correction attempts after the first invalid payload. Exhausting that bound fails the step; the contract itself is not relaxed. A valid payload, including a mix of live passes and untested scenarios that carry a reason, is accepted on the first attempt. - Bounds those agent turns with
test_agent_timeout: each evidence-gathering or Test-repair invocation gets its own stall budget. A silent turn is cancelled there; a still-working turn continues past that budget only when its still-working cap is set. Cancellation parks for a decision with an ask-user finding rather than failing the run as a code defect. The configured baseline’s result from the same execution stays on that park; after a cut repair turn the baseline runs once more first, so the park reports the state the cut left. A cut fix round also keeps the findings of the gate it was answering, selected or not, and the last completed evidence turn’s verdict and scenarios. A late successful return after the deadline is not used as a Test pass. A commit the timed-out agent already made is recorded locally for custody, unless an unfinished rebase or merge leaves only a partial HEAD. While the worktree holds uncommitted changes or commits past the head the last completed evidence turn saw (before one completes, past the head the first cut measured from, which each later park carries forward and measures again), the park names them with the commands to inspect them and refuses approval, because Document and Push would commit and publish them; a fix response validates them. Otherwise approving that park is a Test exception, not a silent green pass. A fix response whose selection holds only the budget-cut findings re-runs validation without a repair turn. Raisetest_agent_timeoutwhen targeted tests or evidence gathering routinely stay quiet longer than the stall budget, and settest_agent_working_timeoutwhen a turn that was still working is cut there; the shipped default is not raised. - The agent may use an existing automated test only when it drives the scenario end to end. It must not run the complete repository suite; when it cannot establish the intent with a targeted check or manual verification, it reports what is missing rather than treating broad regression or a unit test as live evidence.
- The analyzer result also includes
tested,testing_summary, andartifacts;testedrecords exact tests and checks,testing_summaryis a short natural-language account of the result, andartifactsholds reviewer-visible evidence.pathartifacts may be repository-relative paths or absolute paths under the run’s evidence directory,urlartifacts must be externally visible, andcontentartifacts should be short logs or command output shown directly in the PR. - Evidence is always collected under the run’s evidence directory (
<NM_HOME>/evidence/<run-id>by default, seetest.evidence), outside the worktree, so artifacts never enter the branch being validated. On GitHub.com/GHEC,test.evidence.attach_media(default true) uploads supported image and video artifacts to GitHub user-attachments at PR render time.test.evidence.store_in_repo: truealso publishes that directory to the push-target repository’s orphan evidence branch under<test.evidence.dir>/<branch-slug>and links the artifacts from the PR body. The config reference owns provider support and fail-closed behavior. - Before finishing, test agents are instructed to remove transient working-tree artifacts they created, such as downloaded models, caches, build outputs, large binaries, or generated data directories, while preserving intentional source or test-file changes and evidence files under the dedicated evidence directory.
- Missing evidence for user intent can be reported as a warning with
action: ask-user. When a host capability or OS permission is unavailable to the agent process, the agent is instructed to name the specific capability or permission and explain how to grant it before the test is rerun. - If the agent creates new test files (detected via
git status --porcelain), they are recorded as informationalno-opfindings and do not require approval when tests pass.
Approval: a no-go verdict adds an auto-fixable error and parks the step; an inconclusive verdict adds an ask-user warning and parks for a decision; a no-surface verdict adds an ask-user warning asking whether to proceed without live validation and parks for a decision. An expired test_agent_timeout parks as an ask-user budget cut rather than failing the run as a code defect; approving that park is a Test exception, and approval is refused while the park reports work no Test turn validated. An untested scenario never parks by itself. Other test findings park when their severity is error or warning, or their action is ask-user. Only auto-fix findings enter the automatic fix loop. An info finding with no-op is informational only.
Auto-fix: the agent receives the previous test findings plus any per-finding user notes, any selected user-authored findings from the TUI or AXI interface, and the shared finding decision history, including earlier fix summaries for this step. Repair mode reproduces the specific failure, applies a root-cause fix, and re-runs only focused verification - not a complete-suite confirmation - then the step’s configured baseline (if any) and evidence path run again.
An automatic fix round preserves every unselected finding whose action is ask-user or whose severity is error or warning. A clean rerun cannot clear these undecided observations. A rerun replaces a deferred finding only when both its content and effective action match. If the action changes, the original finding remains alongside the new report. Changing ask-user to no-op or auto-fix cannot erase the pending decision.
The finding-action model owns the default for an omitted action. Step-owned verdict, configured-command, and budget-cut findings are excluded from this carry rule. A fix started by an operator or the axi run --yes loop does not carry declined findings, because the fix response already accounts for them.
Default auto-fix limit: 3.
Document
Updates matching documentation for code changes and reports only unresolved gaps.
Behavior:
- Diffs the base commit against head and skips the step if there are no non-ignored changed files to document
- Asks the agent to find every documentation gap, update docs or doc comments for all gaps it can resolve, verify its edits, and commit any documentation changes under the placement policy
- The placement policy gives each fact one authoritative owner, prefers removing stale duplicates or replacing them with pointers, avoids new documentation surfaces for perceived gaps, and keeps durable incident lessons near their owner instead of in
AGENTS.md document.instructionscan add trusted default-branch ownership rules for the repository, and a matching machine-localrepository_overridesentry can add the operator’s own, in a separate labeled section- When
commands.lintis empty, performs documentation and agent-driven lint in one combined housekeeping invocation, categorizing findings for the document or lint gate; if that pass is skipped, its structured output is unusable, or a daemon restart loses the in-memory result, lint runs its own agent pass instead - Includes user intent when available
- Returns findings only for unresolved documentation gaps or human judgment calls
- Requires approval whenever any unresolved documentation finding is returned, including
infofindings - Bounds the documentation (and combined housekeeping) agent with
agent_timeout: a silent turn is cancelled at that stall budget, a still-working one continues past that budget only when its still-working cap is set, and, without an eligibleagent_crash_retriesretry, cancellation fails the step with a timeout diagnostic rather than leaving the run active indefinitely
Auto-fix: documentation fixes happen during the initial document pass. Unresolved findings pause for approval instead of starting another automatic document/fix loop. If you manually trigger a fix from the TUI or AXI interface, the agent receives the selected previous findings plus any per-finding user notes, any selected user-authored findings, and the shared finding decision history.
Default auto-fix limit: not used for automatic document follow-up loops.
Lint
Runs linters and static analysis.
Behavior:
- If
commands.lintis set: ensurescommands.preparehas succeeded once for the isolated worktree, then runs lint via the platform shell (sh -con POSIX,cmd.exe /con Windows). Non-zero exit produceswarningfindings. - If
commands.lintis empty: consumes lint-category findings from the document step’s combined housekeeping pass, avoiding a second cold agent invocation. If no usable combined result exists, the lint step detects appropriate linters/formatters, applies safe fixes, reruns the relevant checks, commits any agent changes, and returns structured findings only for unresolved issues. - Machine-local
repository_overrides.commands.lint.additionalchecks run after either lint path above; a failing one adds an auto-fixableerrorfinding naming that local check and parks the step. - Bounds those agent turns, including a configured-lint repair turn, with
agent_timeout: a silent turn is cancelled at that stall budget, a still-working one continues past that budget only when its still-working cap is set, and, without an eligibleagent_crash_retriesretry, cancellation fails the step with a timeout diagnostic rather than leaving the run active indefinitely
Approval: lint findings with action: ask-user pause for approval.
action: auto-fix findings stay eligible for the fix loop when commands.lint is configured.
action: no-op findings are informational only.
Combined-pass lint findings use the same gate: error and warning findings pause for a decision, while info findings do not.
Auto-fix: when commands.lint is configured, the lint step follows the same pattern as test - the agent fixes action: auto-fix issues using the previous findings plus any per-finding user notes, any selected user-authored findings from the TUI or AXI interface, and the shared finding decision history, including earlier fix summaries for this step, then lint re-runs.
When commands.lint is empty, unresolved findings from the combined pass pause for approval instead of starting another automatic lint/fix loop, because the agent already attempted safe fixes during housekeeping.
Default auto-fix limit: 3.
Push
Pushes the validated branch to the configured push target.
Behavior:
- If
commands.formatis set, ensurescommands.preparehas succeeded once for the isolated worktree, then runs the formatter - Commits any uncommitted changes left by pipeline agents or the formatter with message
no-mistakes: apply agent fixes, or through a non-defaultcommit.fix_messagewith steppushand summaryapply agent fixes - Without fork routing, successful run-start validation selects the upstream URL from the working clone; when it matches the gate worktree’s
origin, the worktree URL is used so embedded credentials retained outside the database can authenticate. If validation fails, the run continues with its prior routing. - With GitHub fork routing, the push target is
repos.fork_url - Immediately before remote mutation, reloads the durable review-approved commit and refuses to push when that binding is missing, malformed, or unreachable
- Requires the commit proposed for push to equal or descend from the review-approved commit, allowing commits made by later pipeline steps without authorizing unrelated history
- Re-reads the push target via
git ls-remotebefore pushing - Before pushing a non-base branch, refreshes an existing PR attestation using the pre-push attestation protocol, which owns provider coverage, snapshot selection, and failure behavior.
- For existing branches, refuses to force-push when the live remote carries commits the pipeline has not incorporated by patch-id
- Fails closed when the remote safety check cannot verify whether the push would discard existing remote work
- Uses
--force-with-lease=<ref>:<sha>with an explicit SHA anchor for allowed existing-branch rewrites - Pushes the exact verified commit SHA instead of mutable worktree
HEAD - Treats the branch as already pushed when the remote already points at that verified commit
- Uses regular push for new branches, and for an append-only update to an existing branch - one whose remote head is already an ancestor of the head being pushed, so nothing can be discarded and the remote itself, rather than a lease anchor, enforces the no-rewrite property an open PR’s head depends on. This holds under either
rebase.strategy, since it is a property of the update rather than of how the update was produced - When the local gate mirror exists, applies the private mirror reconciliation contract, including Decision 41-A for the run’s exact submitted or last pushed head, while preserving newer descendants through shared worktree refs; skips a missing mirror
- Only after the remote and gate mirror settle, atomically records the exact delivered commit as both the run head and successful-push binding; until that database write succeeds, the durable database head and binding remain unchanged, so a partial failure records nothing and is safe to re-enter
A remote branch can move without being rejected when all remote commits are already represented in the validated head, or when a run is intentionally rewriting history it already knew about. Any other out-of-band commit stops the push instead of being overwritten. Pre-skipping or later skipping Review leaves no approval binding, so Push fails closed unless Push is also skipped. With Push skipped, Test, Document, Lint, and PR can still run.
This step never requires approval - it runs automatically after review, test, document, and lint pass.
PR
Creates or updates a pull request.
Skipped when:
- The branch is the effective PR base described in the scope rules at the top of this page
- The upstream host is not GitHub, GitLab, Forgejo, Bitbucket Cloud (
bitbucket.org), Azure DevOps (dev.azure.com/*.visualstudio.com), Gitea, or Cursor Origin (origin.cursor.com), and no provider plugin claims it - A provider plugin claims the host but its command cannot be found, or its
statushandshake exits non-zero (for example, not authenticated). A handshake that breaks the protocol (unreadable output, an unsupported protocol version, a timeout) fails the step instead of skipping - The provider CLI (
gh,glab,forgejo-axi,tea, ororigin) is not installed for GitHub, GitLab, Forgejo, Gitea, or Cursor Origin (GitHub also skips whenghis missing fromPATH) - The provider CLI is not authenticated for GitHub, GitLab, Forgejo, Gitea, or Cursor Origin (GitHub reports a timed-out or interrupted
gh auth statusseparately from auth failure; either still skips) - Bitbucket Cloud credentials are missing (
NO_MISTAKES_BITBUCKET_EMAILorNO_MISTAKES_BITBUCKET_API_TOKEN) - The
azCLI with theazure-devopsextension is not installed or not authenticated for Azure DevOps - A legacy or manually edited non-GitHub repo record has
fork_urlset, because fork MR/PR routing is currently GitHub-only (this includes provider-plugin hosts)
Behavior:
- Checks for an existing PR on the branch, matching by branch alone rather than filtering by base, so a still-open PR against a since-changed
pr.base_branchis found and updated instead of orphaned behind a duplicate - If one exists, updates it. If not, creates a new one against the configured base branch, or the per-run
--base-branchoverride when set. - A per-run
--base-branchthat disagrees with an existing PR’s live forge base retargets that PR (GitHubgh pr edit --base, GitLabglab mr update --target-branch, Giteatea apiPATCH, Cursor Originorigin pr edit --base, or a provider plugin’spr retargetsubcommand when it advertisesset_pr_base_branch) before updating title and body, but only the run’s persisted PR URL or number afterGetPRStateproves it is still open. A sibling first-list-hit is ignored in favor of that identity; a closed or merged persisted identity, a run with no persisted identity, or a provider that cannot retarget, fails closed instead of moving another review object. A rerun inherits the selected run’s PR URL only when that PR is not already merged or closed. A repo-configpr.base_branchchange still does not retarget. - If existing-PR discovery fails or its provider response cannot be decoded and validated as a PR listing for the configured repository, stops instead of treating the result as no PR and creating a duplicate.
- Uses
ghfor GitHub,glabfor GitLab,forgejo-axifor Forgejo,teafor Gitea, the Bitbucket API for Bitbucket Cloud,azfor Azure DevOps,originfor Cursor Origin, and the configured executable for a host claimed by a provider plugin. A provider plugin takes precedence over forge profiles and built-in host detection for the hosts it claims. - For GitHub fork routing, keeps
gh --repopointed at the parent repository fromorigin, checks existing PRs with the bare branch name, filters matching PRs by head owner, and creates PRs with--head <fork-owner>:<branch> - Honors the configured draft setting. The global and per-repo config references own provider support and create-versus-update behavior.
- PR title: agent-generated from the final branch delta with user intent when available. With no
pr.title_format, it uses conventional commit format (type(scope): descriptionortype: description); user-facing product impact should usefeatorfixso release automation can pick it up; when a scope is used, it should be the primary affected real module/package from the changed paths and kept broad rather than file-level. A configuredpr.title_formatreplaces that default and can use{{.Branch}}and{{.Title}}to apply repository-specific conventions after the agent returns only the bare title text. If drafting fails, the ordinary fallback ischore: update pull request; with a configured format, the formatter receivesupdate pull requestas its{{.Title}}value. - Bounds the PR-drafting agent with
agent_timeout: a silent turn is cancelled at that stall budget and a still-working one continues past that budget only when its still-working cap is set; ordinary drafting uses that same fallback, while template drafting fails as described below. A late successful title after cancellation is not used - For repository-owned headings and author-preserving regeneration, see
pr.template. That mode replaces the default narrative and fails on drafting or body-budget errors rather than applying the ordinary fallback/truncation below. - The PR stage exclusively owns the complete branch-scope description. It drafts
## What Changedfrom the actual final diff after local mutating stages finish, and its fallback lists the final changed paths and statuses. - Ordinary PR, configured-title, and template drafting share a compact path/status inventory when the full inventory exceeds 8 KiB. It includes change totals, top-level group and directory counts, omission counts, and bounded samples. It prioritizes status/rename, modified-file, and sparse-group anchors within the same budget. Group metadata is limited to 2 KiB, with exact aggregate counts for groups that cannot be named inline. The summary omits entries for length without classifying them as generated. It supplies safely quoted POSIX commands for the complete inventory and diff at immutable base/head commits. Drafting must inspect that complete delta when the summary is insufficient. Ordinary drafting also replaces a diff stat above 8 KiB with a short stat. These summaries do not reduce Review’s changed-file coverage.
- By default, the PR body includes a
## Intentsection when user intent is available andpr.publish_intentpermits it and the run recorded no caller-side omission (axi run --no-publish-intentor the globalintent.publish_intentdefault), the final-diff## What Changed, and regenerated## Risk Assessment,## Testing, and## Pipelinesections from recorded step results and rounds.pr.appendixcontrols the presentation of that recorded tail. Only## What Changeddescribes the complete final branch scope; when published, the deterministic sections remain evidence for the commit each step inspected. In the full appendix, auto-fix results in## Pipelinerender as an issue -> fix -> verification narrative using recorded outcomes: applied changes, a confirmed no-change attempt, or an attempt whose result was not recorded. Test details show the live-validation verdict, scenario table, and recorded commands in both## Testingand the relevant Pipeline rounds. - Explicit
axi run --closesreferences render last, oneClosesline each, in a stable## Issuessection (GitHub and GitLab declare the closing-reference capability; another forge fails the step rather than publish a reference it would ignore). A reference qualified with the PR’s own repository (owner/repo#95, or a GitLab subgroup path that resolves to the PR’s own project) is the same issue as95, so it renders once; a subgroup path on a provider other than GitLab fails the step, since that provider would read it as a different repository’s reference. A run carrying--closesfails the step instead of skipping it when the PR host is unavailable or unauthenticated, or when the pushed branch is the PR base branch, and a run whose PR step is skipped (for example with ano-mistakes.skip=prpush option, or because the diff is empty after rebase) fails there too. Closure is never inferred from intent, commits, branch names, or linked issues, so without--closesno section is added. Text the pipeline generates (the Intent section, the drafted narrative and its fallback, and the Risk Assessment, Testing, and Pipeline sections, in ordinary bodies andpr.templateappendices alike) has closing references neutralized using the supported forge grammar: on GitHub a reference such asFixes #12, or a keyword before an issue or pull request URL, is published with the reference in an inline code span (Fixes `#12`), which GitHub ignores, so only the## Issueslines in the body close anything; on GitLab, whose closing pattern is matched against the raw merge-request description and therefore also inside code blocks, the first space after the closing keyword is replaced with (Implements #12,Closes group/subgroup/project#12,Closes https://gitlab.example.com/group/project/-/issues/12). Breaking that separator protects the complete closing statement, including every reference in a list, without depending on the reference shape. This protection and the closing-line verifier use GitLab’s default pattern. no-mistakes does not fetch a self-managed instance’s custom pattern or query which issues the server interprets as closing. With a custom pattern, generated text may still match and requestedCloseslines may not; live readback verifies the body, not the server’s interpretation. An ordinary update regenerates the whole body, so a run without--closespublishes no## Issuessection. PR titles and commit messages are not rewritten, so a squash merge can still close an issue named after a closing keyword there. The section’s room is reserved before the rest of the body is fitted to the size cap. An author-preservingpr.templatebody keeps its author text verbatim and adds only the requested references that text does not already close (standalone closing lines in the forge’s own grammar:Closes/Fixes/Resolveson GitHub, plus the gerunds andImplementson GitLab), inside its generated appendix. Before the body is published, and again on the live PR afterwards, the step verifies that every requested reference is present, and fails instead of reporting success when one is missing. - In
fullandcollapsedmodes,## Pipelinekeeps the existing human-readable signature and includes the stable structured step attestation documented below. Inminimalmode, only the attestation is retained. Ordinary Bitbucket Cloud PR descriptions omit HTML-only features (<details>,<code>,<video>, and the attestation comment) because Cloud renders Python-Markdown and escapes raw HTML. Owned templates retain the Markdown evidence skin and add the exact attestation as visible text;pr.templateowns the provider caveats. - Generated PR bodies are capped at 63,488 bytes, leaving a 2 KB safety buffer below GitHub’s 65,536-character body limit.
- In the default
fullmode, when an ordinary, non-template body would exceed that cap, the PR step first omits older## Pipelineupdate rounds at clean update boundaries, keeps the newest rounds when possible, and points reviewers to the run log for the full pipeline history. Seepr.appendixfor the other modes. - In
fullmode, ordinary, unowned bodies keep Intent,## What Changed, risk, and testing sections ahead of pipeline history; if those sections or the newest pipeline update are still too large, the PR step truncates at line or section boundaries and adds an explicit marker. - The regenerated
## Testingsection prefers the recordedtesting_summaryas prose, uses a compact recorded-check count when no summary is available, includes produced evidence artifacts frompath,url, orcontentfields when available, and only adds an outcome with run count and total duration when it is failed or needed as a fallback - Evidence artifacts render compactly in PR bodies: repository-relative
pathartifacts andurlartifacts becomeEvidencelinks,contentartifacts appear in collapsible details blocks, GitHub PRs convert repository-relative paths to blob URLs and published evidence to commit-pinned blob or raw URLs, readable UTF-8 text files from the run’s evidence directory are embedded inline with truncation for large files, and binary, visual, or over-budget local artifacts render as non-link local file references - Before the PR is created or updated, the assembled title and body pass through a final home-directory redaction. The home-directory portion of any absolute path - the operator’s own home, and
/home/<user>,/Users/<user>, orC:\Users\<user>generally - is rewritten to~while the rest of the path survives, so the run’s evidence and worktree locations, captured command output, artifact paths, and agent prose cannot publish the operator’s account name. Before that, run worktree paths are rewritten the same way published evidence text is: the run’s worktree root, and any path into a run worktree under<NM_HOME>/worktrees/<repo-id>/<run-id>, becomes{run-worktree}(a bare<NM_HOME>/worktreesor<NM_HOME>/worktrees/<repo-id>becomes{worktrees}), so evidence text inlined in the body cannot publish the checkout-path hash or anNM_HOMEoutside the home directory. Redaction is unconditional. Ordinary bodies apply it after length caps; author-preserving template composition applies it before stamping its integrity guard and enforcing its non-truncating budget. - The same final pass strips terminal escape sequences (ANSI colour and cursor codes, OSC sequences) from the title and body and replaces any other control character that
ghrewrites on read with U+FFFD.gh pr view --jsonturns those characters, and the literal text\u001b, into caret notation, so a body that kept them would never read back as sent: the created-body check and the appendix digest would fail with a false “edited” refusal. Literal\u00XXescapes of those characters are written as\u{00XX}. An appendix that an earlier version stamped over raw control bytes is still recognised whenghhas rewritten it, as long as restoring the caret notation reproduces the stamped digest, and the next update writes it back in the cleaned form. - For ordinary, unowned Azure DevOps PRs (and provider plugins declaring a positive
max_pr_body_chars), the description is capped at that limit (4,000 characters for Azure DevOps, counted in UTF-16 code units): the agent is told about the cap and asked to keep the## What Changedsection compact. Infullmode, if the assembled body still overruns,## Testingis dropped first because it can embed artifact and log content, preferentially preserving Intent, What Changed, Risk Assessment, and Pipeline; a final connector-level clamp truncates with a visible marker as a last-resort backstop. Other modes followpr.appendix.
Stores the PR URL in the database and streams it to the TUI.
Pipeline step attestation
Immediately after the existing Updates from [git push no-mistakes](https://github.com/kunchenguid/no-mistakes) signature, no-mistakes writes one stable HTML comment:
<!-- no-mistakes-pipeline-attestation:v1 {"head_sha":"0123456789abcdef0123456789abcdef01234567","steps":[{"step":"review","status":"completed"}]} -->The v1 payload is compact JSON with these required fields:
-
head_sha: the exact git commit SHA recorded for the run when no-mistakes writes the PR body -
steps: the ordered pipeline step snapshot; every item has the required fields below and may carry the optional Test override field described afterward -
step: the raw pipeline step name, such asintent,rebase,review,test,document,lint,push,pr, orci; a repository-declared gate appears asgate.<anchor>.<name> -
status: the raw step status recorded for that step, such ascompleted,skipped, orfailed
When the Test step validated the same head_sha, the payload also includes live_validation with verdict, live (the number of scenarios driven live), and total. The field is omitted for pre-contract findings and whenever a later Document, Lint, Push, or repair commit changes the head without validating that new commit. Consumers therefore never receive a previous head’s live-validation verdict as a claim about the current head.
Two additional fields are additive and omitted when empty, so older attestations remain valid:
steps[].override_reason: present on a Test step that was approved over a failing configuredcommands.test. Absent on an ordinary green completion and on pre-field attestations, which must be read as not approved over failure.allow_test_command_override: the trustedtest.allow_approve_over_failurereason copied at PR-write time, including a CI-repair restamp, from the current trusted config rather than a previous attestation, and only when the Test step carriesoverride_reason. The required check refuses a Testoverride_reasonunless this field is a non-empty recorded reason.
A CI step approved over still-failing checks also stores override_reason on the step result (and surfaces as passed-with-override in AXI), but that CI mark is not copied onto the attestation.
Items follow pipeline order, with each repository gate immediately after its anchor. They represent the exact database snapshot when no-mistakes creates or updates the PR body. The attestation includes pr and ci records even though their human-readable details are not shown in ## Pipeline; at the normal PR write point those records are commonly running and pending. The head_sha binds that snapshot to the commit it describes, so consumers can reject a stale comment after the PR head changes.
For an existing PR on a non-base branch on any supported provider, every later no-mistakes publication rewrites an existing attestation for the proposed head before pushing the branch. This ordering ensures a synchronize check cannot observe a newly pushed legitimate head with the old binding. The ordinary Push step replaces the step list with its own current snapshot; a continuity-proven CI repair published without revalidation keeps the prior attested statuses because Review, Test, and Document did not run for that repair. A PR that never carried an attestation remains unchanged by the Push step. If its body has no attestation when the branch is pushed, the first synchronize run of Require no-mistakes can be red, with either This PR was not raised through no-mistakes. or This PR is missing structured pipeline step attestation. The run reads the live PR body when it executes, so it is red when it reads the body before the PR step writes the attestation. The PR step of the same run then writes the full body with the attestation, and Require no-mistakes passes on that body edit, or at once when the run reads the body after that write. In the recorded cases the write came 19 to 40 seconds after the push. No action is needed for that red run. Each skip writes a skipping attestation ... step-log line that names its reason, including skipping attestation write: no pull request found for this branch when no PR exists yet and skipping attestation rebind: pull request body carries no attestation marker when the body has no attestation. A rewrite error aborts before branch mutation, while an unavailable SCM host skips the rewrite and leaves strict head equality to reject any stale binding. If the attestation write succeeds but the subsequent git push fails, the body can temporarily point ahead of the PR head, and the Push step reports the failure.
The comment is intentionally data only. It does not declare any step required, passed for a policy, compliant, or mergeable. Consumers can parse the versioned JSON without scraping prose and apply their own policy. In full and collapsed modes, the comment stays with the Pipeline header when no-mistakes truncates older human-readable update details to fit a PR-body limit; minimal retains the marker without the header. It is omitted on ordinary, unowned Bitbucket Cloud descriptions. For owned template bodies, see the provider caveats.
CI
Monitors PR health after creation and auto-fixes CI failures. Mergeability polling and merge-conflict handling apply to GitHub, GitLab, Forgejo, Azure DevOps, Cursor Origin, and provider plugins declaring mergeable_state.
Active for GitHub, GitLab, Forgejo, Bitbucket Cloud (bitbucket.org), Azure DevOps (dev.azure.com / *.visualstudio.com), Gitea, Cursor Origin, and any host claimed by a provider plugin.
- GitHub requires
ghCLI, installed and authenticated, version >= 2.50 (older versions reject thegh pr checks --jsoncall the monitor reads checks with). - GitLab requires
glabCLI, installed and authenticated. - Forgejo requires
forgejo-axi, installed and authenticated. - Bitbucket Cloud requires
NO_MISTAKES_BITBUCKET_EMAILandNO_MISTAKES_BITBUCKET_API_TOKEN. - Azure DevOps requires the
azCLI with theazure-devopsextension, authenticated with a PAT. - Gitea requires
teaCLI, installed with a login configured for the instance. - Cursor Origin requires the
originCLI, installed and authenticated. - A provider-plugin host requires the configured plugin executable, which owns authentication; mergeability polling applies when the plugin advertises
mergeable_state, and failed-job log reads when it advertisesfailed_check_logs.
Behavior:
- Polls provider CI status at increasing intervals: every 30s for the first 5 minutes, every 60s for 5-15 minutes, every 120s after that
- Keeps monitoring after readiness until the PR is merged, closed, or declined, and re-arms its idle timeout whenever the base branch advances
- Under
ci.release_after_checks_pass, finishes at the checks verdict instead: once every check is green (or a trustedno_ci: truedeclaration covers zero registered checks) and known mergeability is clear, it records readiness and completes, so the run ends aschecks_passedand releases its worktree instead of holding it for a human merge. Either mode parks at an approval gate when the configuredci_timeoutidle window elapses before the checks settle - If the provider check read keeps failing (6 consecutive polls while the PR is still open), parks at an ask-user approval gate instead of spinning invisibly to
ci_timeout; the provider-neutral finding names the provider CLI or credentials and includes the underlying error (for GitHub,gh< 2.50 rejectinggh pr checks --json), and the streak resets as soon as one read succeeds - The
ci_timeoutreference owns idle re-arming, unlimited monitoring, and fail-closed reconciliation while that gate is parked - On GitHub, GitLab, Forgejo, Azure DevOps, Cursor Origin, and provider plugins declaring
mergeable_state, polls provider mergeability alongside CI checks while the PR remains open - On GitHub, combines the exact current PR head commit’s check rollup with Actions workflow runs for that same commit, so a workflow rejected during validation before it creates a job or check-run still blocks readiness
- On GitLab, binds checks and selected failure logs to the exact source commit the run is delivering. The merge request’s source revision must match that commit. Its head pipeline must run at that commit, or use this merge request’s merged-results or merge-train ref with the delivered commit listed as a direct parent of the temporary commit by the repository API. After listing jobs or fetching selected traces, re-reads the merge request and rejects the results if its source revision no longer matches. During monitoring, a head-binding refusal clears readiness and logs “waiting for the delivered commit’s own checks” without counting toward the consecutive-read-error limit; polling continues until valid checks arrive or the idle timeout parks the run. Missing required identity fields and failed provider reads remain read errors. An explicit approval over a head-binding refusal records the mismatch as an override
- On GitLab, reads the merge request’s head pipeline as a whole rather than only its job list: the pipeline’s ordinary jobs, its trigger jobs (the jobs that start downstream pipelines, which the job route omits), and the external status checks the instance licenses. Everything the observation carries is taken from the pipeline and merge request identified above. The pipeline’s own aggregate status becomes a check whenever no job or trigger job already reports it, so a failed pipeline that created no job, or one still running after its last job finished, is never hidden behind green jobs or an empty job list. An instance that does not serve external status checks (an Ultimate capability) is read as having none - never as an unreadable check - while any other read failure still fails the read
- On GitHub, collapses repeated same-name runs of one workflow to the newest run that provider timestamps or Actions run identity can order. Independent workflows and same-named commit status contexts remain separate requirements, and check runs whose order cannot be established remain visible so readiness fails closed. A cancelled workflow run that never started a job is dropped only when a later run of the same workflow at that head is represented by its job checks; a later run with no job checks does not replace it, so the cancellation stays visible
- On GitLab, unreadable or wrong-shaped job responses, including any invalid page in a paginated listing or a job missing its name or status, count as check-read errors and block readiness even with
no_ci: true. Both the commit-bound check reader and the merge-request fallback treat missing pipeline metadata or a pipeline without a positive numeric ID as a read error. An explicithead_pipeline: nullmeans no pipeline; an empty job array or a pipeline object’s explicitjobs: nullmeans no jobs. These valid empty results follow the empty-check-list rule below - On GitLab, a job reported as
failedwithallow_failure: trueis non-blocking and is excluded from failed-job log lookup; failed jobs without that flag remain blocking and eligible for log lookup - On GitLab, a blocking manual job (
when: manualwithallow_failure: false) is a named human wait that clears readiness: the pipeline is stopped at the job’s stage until a person runs it, so it is never read as a skip, never reported as a failure the fix agent repairs, and is named in the step log. An optional manual job (allow_failure: true) stays a skip, matching GitLab’s own rule that the pipeline need not wait for it. The same wait marks a trigger job whose own status ismanual, and a pipeline whose own status ismanualwhen no job row already reports it - Once CI readiness is established and known mergeability is clear,
no-mistakes axireturnsoutcome: checks-passedwith successful-output reporting instructions so agents can summarize the run, ask the user to review and merge, and list any pipeline fixes instead of waiting. By default the run stays active and the TUI and terminal title show a liveChecks passedpanel while the monitor keeps watching. Underci.release_after_checks_passthat outcome is the run’s own recorded verdict instead: the run is terminal withstatus: checks_passed, the TUI reports✓ Checks passed - PR ready to merge, and nothing is left monitoring the PR, so a merge that happens later never changes this run’s outcome - An empty forge check list is never treated as green unless the trusted default-branch config declares
no_ci: true. That declaration is positive durable evidence the repository intentionally has no CI; absence means CI is expected and delayed registration stays not-ready. If checks still appear on a declared no-CI repo, their actual states are honored - If the PR base branch moves after
checks-passed, keeps watching the same PR; a clean behind PR needs no action, except that one GitHub reportsBEHINDwaits for a human to bring it up to date unlessci.rebase_behindis on (below), while an actual merge conflict (on GitHub, GitLab, Forgejo, Azure DevOps, Cursor Origin, or a provider plugin declaringmergeable_state) is auto-fixed by rebasing onto the PR base branch and re-pushing through the force-push safety guard. Underci.release_after_checks_passthe run has already released the branch, so a later base-branch move or conflict is the operator’s to act on with a fresh run on the updated head, not this run’s to repair - Once the PR exists, its actual forge base branch (read live from the provider) takes precedence over the configured
pr.base_branchfor merge-conflict repair and base-branch tip monitoring, so a resumed run is not misled by a base-branch config change made after the PR was created - The ready signal clears if checks start running again, new failures appear, workflow-run discovery fails or reports an unknown state, provider state otherwise becomes uncertain, or the PR is merged, closed, or declined
- If CI failures or a merge conflict (on supported providers) are already known while other checks are still pending: waits for all checks to finish before attempting an auto-fix. On GitHub, a workflow run held for maintainer approval is not waited on here, and on GitLab neither is a blocking manual job;
ci.rerun_transientowns how a hold is handled - Once every check has finished, classifies each terminally failed check by the provider’s own reported outcome before anything escalates;
ci.rerun_transientowns which outcomes count as the provider reporting itself - On GitHub, a positive transient rerun budget also enables structural detection of jobs that failed before any repository step ran because their setup/action-resolution phase failed, such as during a “Failed to resolve action download info” / HTTP 503 action-download outage. Detection reads the job’s own setup-step conclusion (never log text) and fails closed, so an unreadable job or a real test or lint failure remains a genuine failure
- On GitHub, when the configured budget authorizes a rerun, re-runs such a check for the same commit instead of escalating it, targeting the job identified by a job link or the whole workflow identified by a cancelled run link, and naming each rerun in the step log so a run waiting on one is visible in the TUI and
axi - Escalates every other failure, and any merge conflict, on its first observation with no added latency, and waits out the poll or two a provider can take to publish an accepted rerun rather than escalating the outcome that rerun was meant to replace
- When a provider-attributed failure is the only remaining issue, pauses for user approval without spending an auto-fix attempt if no rerun is going to replace it. This includes a check cancelled again after its rerun and a detected GitHub setup failure that persists after its budget. On the default budget of
0, once the budget is spent, or on a provider with no rerun API, a cancelled or stopped check itself reaches that gate. These outcomes are terminal and will not resolve on their own, there is nothing for the fix agent to repair, and the PR must not look green either - Keeps waiting, rather than pausing, while any check can still finish on its own, so a cancellation observed alongside a running check is decided only once the rollup has stopped moving
- Never re-runs checks across a head change: if the published branch head no longer equals the commit the run delivered, the step clears any ready-to-merge signal and pauses for user approval with the expected and observed commits, because re-running checks would certify a revision this run never produced
- Once every check has settled and every authorized rerun is spent, reports each remaining issue as one finding carrying an
action, and hands those findings to the executor’s shared auto-fix machinery - the same loop the review step uses. A failing check the provider attributes to the job itself, and a merge conflict, areauto-fixerrors that enter theauto_fix.ciloop as fix rounds; a provider-attributed outcome no rerun will replace is anask-userwarning; a red check published by a supported review bot (Greptile or CodeRabbit, identified on GitHub by the check publisher, never by the check name) becomes oneask-userwarning per unresolved review comment, anchored to the comment’s file and line, so it never spends an auto-fix attempt and a human chooses which comments a fix round addresses. Each finding names its check, so a fix round - automatic or answered at the gate withfix- repairs exactly the findings selected for it, with any notes attached at the gate - Before repairing selected failing checks, refreshes the provider’s check observation and requires every selected failing-check target to still report a terminal failure. An empty or partial observation preserves the previous completion evidence and removes all selected failing-check findings from that repair’s input. Selected merge-conflict, behind-base, and review-bot comment findings can still be repaired independently, including comments from a green bot check. A stale-head snapshot waits instead of authorizing check repair or surfacing a read error, even when discovery fails after observing the different head; plugin protocol violations still fail the step. Head binding uses only heads the adapter actually observed: GitHub reports the live PR head separately from the requested head, GitLab rejects pipelines for other commits, and provider plugins follow the check head-binding contract. Gitea, Forgejo, Azure DevOps, and Cursor Origin retain their existing behavior without observed-head binding
- On CI failure: fetches failed job logs only for the selected checks (GitHub via
gh run view --log-failed, GitLab viaglab ci tracewith the source-commit binding described above, Forgejo via the exact native check target plusforgejo-axi run view --log-failedwhen runtime routes are available, Bitbucket Cloud via failed pipeline step logs, provider plugins viapr check-logswhenfailed_check_logsis declared; Azure DevOps and Cursor Origin have no first-class build-log command, so the agent fixes from the failing-check list without logs), sends them to the agent with user intent when available, and, if the agent produces changes, commits them withcommit.fix_message. Target-aware providers return each selected check’s evidence separately; the prompt labels it with the check and provider identity, shares a 32 KiB budget across all selected targets, and marks per-target truncation or incomplete retrieval explicitly so one check cannot hide another’s missing evidence. Every CI repair path, including a merge-conflict-only repair, follows the same invariant-complete discipline as the Review fixer (state the invariant the failure exposes, close it at every sibling site in the changed area in the same round with the same small correction or one shared boundary, prefer a deeper root cause and simplification over machinery for the symptoms, and re-trace the failing sequence and the ordinary path through every changed function before verifying), and follows the shared removal-first rule described above; it may conclude that no code change is warranted when the red check is not caused by the PR’s code (a stale run, an infrastructure or attestation check such asPR must be raised via no-mistakesthat fails only because a later pipeline push moved the head, or any failure external to the code). What happens next follows one rule on every CI-fix path: a repair is published without revalidating only when its continuity with the reviewed, published head can be proven, meaning the repaired head is the run’s review-approved commit or a descendant of it. A provable repair is published immediately through the Push step’s own guarded publication path, using that step’s own remote-safety decision, and the monitor keeps watching the same run; on supported providers, an existing pipeline attestation is rebound to the proposed head before the branch push, and failure to settle that rewrite after retries reports the repair as unsettled without pushing. An unavailable SCM host skips the rewrite, while strict attestation head equality keeps any stale binding fail-closed. Anything else is held locally, the run’s review approval is revoked, and validation restarts from Review so Push republishes it only after Review approves it.ci.revalidate_repairssets the intent identically on every path:false(default) publishes when it is provable,truerevalidates outright. A merge-conflict repair rebases, so its continuity is never provable and it always revalidates. Forgejo status gating remains active when logs are unsupported or unavailable - Under
ci.review_bot_comments: always, a supported review bot whose check completed green, or that has not registered a check on the head yet once every other check is green (including a trustedno_cihead with no checks at all), is read the same way: its unresolved comments become the sameask-userfindings, and the step parks instead of reportingchecks-passed. The default,on_failure, reads them only for a red bot check - On GitHub, a red check from a supported review bot (Greptile, or CodeRabbit only when
ci.required_reviewsnamescoderabbit; otherwise its check is an ordinary CI check and its comments are ignored) parks asask-userfindings that carry the bot’s unresolved review-thread comments (at most 50 per gate, each bounded, with a count of any omitted); when a fix round starts, the same comments are also included in the repair prompt, framed as untrusted external data and capped at 32 KiB - On GitLab the same comments come from the merge request’s unresolved discussion notes, read through
glab apiagainst the project’s discussions endpoint and matched to a registered review bot by the comment author’s login (Greptile posts there asgreptileai, not as the GitHub App’sgreptile-apps[bot]). A note counts only when it is resolvable, unresolved, not a system note, and authored by a registered bot, so an ordinary MR comment is never a finding. Because GitLab job objects name no publishing application, no GitLab check can ever be attributed to a bot: there is no red bot check for the defaulton_failureto key on, and the comments are read on the green or not-yet-registered path thatci.review_bot_comments: alwaysenables - Optional
ci.required_reviewsadds an exact-head external-review requirement to readiness; incomplete terminal reviews park asask-userfindings - CodeRabbit publisher identity follows the immutable app requirement in
ci.required_reviews; comments require bot account ID136622811and either logincoderabbitai(GitHub GraphQL) orcoderabbitai[bot](REST). Greptile retains its existing app-slug and login matching. Resolved threads and unrelated authors are excluded; malformed or unreadable comments cannot clear an opted-in completion requirement - After a repair is published, the step reports that it is monitoring again, so its status returns from
fixingtorunningandchecks-passedcan surface for the repaired head - States the configured repair policy in the step log before the first poll, so a run’s log says which of the two paths a repair would take without cross-referencing the config in force at the time
- Settles the local gate mirror before atomically recording the published head and push binding, so a publication that stalls part way records nothing: the run stays on its pre-repair head and the next fix attempt re-enters the same path, finds the remote already at that commit, and completes it
- Whenever a repair revalidates - either because the setting requires it or because continuity cannot be proven - restarts at Review only: Intent and Rebase keep their results, steps already skipped for the run stay skipped, the run id is unchanged, and the durable auto-fix attempt count carries across. Earlier cycles remain in the run’s round history; the step’s own status shows the latest cycle. The fresh Review cycle does not inherit the superseded cycle’s outstanding finding carry set
- The one exception is opt-in
ci.carry_review_on_replay: a rebase repair proven to be a pure replay of the reviewed commits onto the base tip it targeted keeps the review approval, moved to the replayed head, and restarts at Test instead; the step log names the reviewed head, the base, and the replayed head, or, when the proof fails, why - Bounds that CI-fix agent with
agent_timeout: a silent turn is cancelled at that stall budget, a still-working one continues past that budget only when its still-working cap is set, cancellation fails the attempt with a timeout diagnostic rather than leaving the run active indefinitely, and a late successful return after cancellation is not committed - If the CI-fix agent exhausts that budget, pauses for user approval instead of re-issuing the same request on the next poll. A budget burn is not transient - repeating it costs another full budget - so the remaining auto-fix attempts are left for the user to spend deliberately with a fix response. The finding carries the measured timeout diagnostic. When the timed-out agent left uncommitted work in the run worktree, the finding names that worktree’s path. When the agent already committed a repair, that head is recorded locally for custody and is not published; an unfinished rebase or merge is reported instead of recorded. A later fix round that adds nothing to a recorded repair still routes it through the repair rule above, so it revalidates from Review rather than staying behind the published head. Ordinary (non-timeout) fix failures keep retrying as before
- On a merge conflict (GitHub, GitLab, Forgejo, Azure DevOps, Cursor Origin, or a provider plugin declaring
mergeable_state): asks the agent to rebase onto the latest PR base branch tip and make the smallest correct root-cause fix for the conflicts, using user intent when available - On GitHub, with
ci.rebase_behindon, when every check is green but the PR ismergeStateStatus: BEHIND- GitHub reports that only when the base requires branches to be up to date, so the merge is refused; a behind PR on a base without that rule readsCLEAN- withholdschecks-passed, waits one poll and, if the PR is still green andBEHINDat the same base tip, reports oneauto-fixfinding that takes the merge-conflict repair: rebase onto the PR base, revalidate from Review (or from Test underci.carry_review_on_replaywhen the rebase is a proven pure replay), and re-push through Push. A base that moves between those polls keeps the monitor waiting instead - If both CI failures and a merge conflict are present: fixes both in the same attempt
- If a fix attempt produces no changes: a trusted conclusion that the failure is not caused by the PR’s code parks the selected findings as
ask-userimmediately and reports the agent’s summary. Otherwise the step re-observes the settled checks and reports the same findings again, so the executor retries whileauto_fix.ciattempts remain and parks when they are spent - the same follow-up every other step’s fix round gets - The executor counts each automatic fix attempt durably in the step’s round history when it starts, so revalidation or a daemon restart cannot reset the configured limit
- Exits cleanly when the PR is merged, closed, or declined, or at readiness under
ci.release_after_checks_pass - If the idle timeout is reached while the PR is still open: pauses for user approval, even when CI checks are currently healthy
- If the idle timeout is reached while CI failures or a merge conflict are still known: pauses for user approval with findings for the remaining issues
- If the idle timeout is reached while provider PR mergeability is still unresolved: pauses for user approval with a finding describing the unresolved mergeability state
- If CI failures or a merge conflict persist after the auto-fix limit: pauses for user approval with findings listing each failing check and/or the merge conflict
- Discards remembered timeout failures when the provider reports stale-head evidence (
ErrHeadChanged) or actually observes a different head, including when a later check read fails. A failed head lookup alone preserves the latest remembered evidence; the requested head is never treated as an observation
Default auto-fix limit: 3 total CI auto-fix attempts.
Default transient rerun budget: 0 reruns per provider-attributed check per run. GitHub pre-run failure detection is disabled at this value.
Step statuses
Each step progresses through these statuses:
| Status | Meaning |
|---|---|
pending | Not yet started |
running | Currently executing |
fixing | Agent is auto-fixing issues |
awaiting_approval | Paused, waiting for user action |
fix_review | Paused after a fix cycle, showing results for review |
completed | Finished successfully |
skipped | Pre-skipped for the run, skipped by the user, or skipped automatically by the pipeline |
failed | Step failed; the step log includes the returned error message so command stderr and provider errors are visible in the per-step log, not only in the daemon log |
When a non-terminal run has a step in awaiting_approval or fix_review, AXI run objects also expose awaiting_agent: parked <duration> as a run-level observability signal.
The signal clears as soon as the approval wait ends, including axi respond and cancellation, and does not change how gates resolve.
For the active_steps status fields, including active-round timing and compatibility with older runs, see axi status.
If the latest activity is older than step_quiet_warning, AXI prefixes it with quiet to make possible wedges visible without changing the run state.
Step logs also record native subprocess start, exit, and retry lifecycle lines plus explicit auto-fix and user-fix round markers.