Docs

Executing

Pauses and rulings

Every pause names who acts (the controller itself, the Operator, the Planner or the Principal) and is durably recorded. It continues only through a ruling consumed once, or through a bounded self-recovery.

Pause reasons
Rulings
Resume
Cancel

About pauses

A pause is a durable interrupt of a run that is not the end of it. It is never an error string: the pause, its escalation record and its comment are written before anything is reported. A pause continues by resume or ends by cancel.

Pauses fall into four classes, by who resolves them:

Who resolves itCauseHow it ends
The controller itselfRate limit, CI pending, reviewer not staged, a transient outageRetries with backoff within its bound and resumes; no ruling
The OperatorSomething repairable in the environment: infrastructure, credentials, network, sandboxThe Operator repairs and resumes
The PlannerA required source cannot be prepared (missing_source)The Planner corrects the source
The PrincipalEscalation, scope exception, round cap, a finding open two roundsThe Principal rules; the ruling is a new Intent version
RoleIn pauses
ControllerRaises every pause, writes its records, retries automatic-recovery pauses, consumes resolutions
OperatorReads and presents the pause packet (task_escalation_read); requests resume or cancel; never rules
PrincipalPosts the ruling with vinaya pr rule, or writes the ruling marker on the Issue
PlannerCorrects sources (missing_source), or widens the Surface by superseding the brief (widen_surface)

How it works

  1. 1

    The run pauses

    The controller writes the held state, an escalation record and a pause comment that says who must act and how to continue.

  2. 2

    Someone acts

    The controller retries by itself, the Operator repairs, the Planner corrects the brief, or the Principal posts a ruling.

  3. 3

    The run continues or ends

    A resume runs the loop again from the held round. A cancel stops the run. Each pause is resumed or cancelled, once.

Answer a pause with a ruling

When a pause is addressed to you, the Principal, the pause comment tells you what to decide, the ruling marker to use, and the ordinal your ruling must exceed.

Write your ruling in a file and post it:

vinaya pr rule <pr> --file <ruling.md>

Then continue the run: the watching controller picks up the ruling by itself, or the Operator calls task_resume, or you run the task run or --resume command the pause comment names. To stop instead, cancel (see Cancel).

Pause reasons

Each reason is addressed to one authority (requestedAuthority).

ReasonAddressed toRaised when
escalationprincipalA reviewer returns ESCALATE, or the Developer posts a stop comment before any push
approved_repairs_spentprincipalVersion 2 ran its ten approved repair turns and CI never turned green
ready_push_failedprincipalThree consecutive ready pushes failed after approval; the detail names the failing test files
max_roundsprincipalA non-green round reaches reviewPolicy.maxRounds
repeat_findingprincipalA blocking finding is open in two consecutive reviewed rounds
reappearanceprincipalA resolved finding reappears
confidenceprincipalConfidence is absent after one re-ask, or below 50 after the extra turn
time_budgetprincipalActive time exceeds reviewPolicy.maxTaskMinutes
no_progressprincipalA second attach finds the same unchanged head after findings were delivered again
repeat_failureoperatorThe same normalized mechanical failure twice, with a finished Developer attempt between
infrastructureoperatorAny error thrown on the controller's path, a reviewer missing twice, a red or conflicting head that stays stalled, a corrupt control record, an unresolvable base
no_pushoperatorUnpushed work survives the one dedicated resume
sandbox_refusedoperatorThe Linux sandbox probe refuses on this host
missing_sourceplannerA required source cannot be prepared
objectives_changedselfObjectives changed while reviewers ran
ruling_postedselfA ruling landed while reviewers ran
brief_supersededselfA Planner superseded the brief mid-round
policy_changedselfPolicy changed mid-round
stale_driverselfThe controller failed to restart itself over its own changed code

Waits that are not pauses

  • A GitHub rate limit waits for the reset and retries the round.
  • A host that cannot reach GitHub waits for the connection.
  • An agent usage limit pauses with its reset time and resumes by itself.
  • A sign-in refusal pauses without spending retry budget.

Automatic recovery

infrastructure and stale_driver pauses resume on the bare command, with no ruling, up to 5 times per task. Past that bound, a ruling is required.

sandbox_refused is never watched or retried. Once the host is repaired, it continues by an ordinary start, with no ruling.

The pause record

At a pause, the controller writes three things:

RecordWhereContents
Held statecontrol/pause-state.jsonTask, round, judged head, branch, pull request number (null before a pull request), reason, reason-specific detail, agent and model, and a snapshot of infrastructureRetries
Escalation recordThe control store, keyed escalationId = <task>-<round>-<head>Run identity (runId, pid, host), round, head, branch, pull request, input versions (briefHash, objectivesVersion, rulingOrdinal, policyDigest), reason and detail, evidence (the last round's review context), attemptedRecovery and recipient, and agent
Pause commentOn the pull request, or on the task Issue before one existsThe reason, what the person must decide or repair, the ruling marker and the ordinal to exceed, and how to continue (task_resume, or the task run or --resume command)

Writing the escalation record is best-effort. If it fails, the pause is raised again on the next controller start. The Operator reads all of this as one packet with task_escalation_read (see The Operator).

The ruling record

A ruling is a principal-authored comment with <!-- aeg:principal:ruling:<ref>-<ordinal> --> on its first line, and an ordinal strictly newer than the one the pause was raised under. vinaya pr rule posts it on a pull request. For a pause with no pull request, the Principal writes the marker on the Issue; the pause comment names it.

PropertyRule
AuthenticationThe author is checked against principals from the default branch. Issue and pull-request comments count on identical terms.
BindingThe resolution's authenticatedBy and authenticatedFrom (<pr>-<k>, or issue-<n>-<ordinal>) name the ruling's author and marker, never the caller
ChoiceResume or cancel is the caller's command, never derived from the ruling's prose
One-time useThe resolution is claimed exclusively and atomically. A second attempt on the same escalationId, with the same or a different decision, is refused as replayed.
Other refusalsStale (no record, or one that cannot be parsed) and wrong target (the recorded pull request differs)
TransitionOn success, the pause moves from paused to resumed or cancelled, under the same epoch
Self-recoveryAn automatic-recovery resume is still consumed once, authenticated as driver-self
Intent versionThe ruling ordinal is part of the review-input manifest, so a new ruling invalidates verdicts bound to the old ordinal. After a pause resolved by a ruling, the manifest's pauseBoundRuling binds the ruling identity, the pause identity and the SHA-256 of that ruling's text.

Resume

PathHow
The watching controllerPolls. A ruling newer than the ordinal captured at pause time runs the loop again in the --resume shape.
vinaya dev-review-loop --resume <pr>Reads Closes #N and the held pause, and requires a fresh ruling (unless it is automatic recovery within the bound, or sandbox_refused). If the head is unchanged, it re-enters the held round with the ruling as context. If the head moved, the move is accepted, the round counter restarts at the ruling ordinal, and the run goes straight to the gate and reviewers.
task_resumeChecks the pause, escalation, resolution and driver lock, and requires a ruling. Launches --resume in the background, guarded by a claim keyed by the escalation identity. The ruling is consumed inside the continuation.
task_resume, pause before any pull requestConsumes the ruling itself, then relaunches through vinaya task run. That continuation does not carry the ruling text to the Developer.
task_startContinues a pause whose ruling is posted, by handing it to task_resume's authentication

A pause raised before its pull request existed is bound to the open pull request on its own branch; the escalation keeps pr: null. An explicit --agent different from the paused run's continues on that agent.

Cancel

vinaya dev-review-loop --cancel <pr> --agent <vendor>

The Operator's task_cancel does the same. A cancel:

  1. authenticates a ruling exactly as resume does, and refuses a wrong-target, stale or replayed resolution;
  2. checks the escalation's recorded agent;
  3. stops the in-flight role through the identity-checked shutdown path;
  4. marks every effect still started as uncertain.

A pause is either resumed or cancelled, never both, and never either one twice.

task_cancel outcomes are confirmed, pending (the driver is on another host) and uncertain (an effect was marked uncertain). A repeated cancel reports confirmed again.

Gates

Pauses run no registry check. Their guards are the authentication and one-time-use rules above.

The merge gate respects rulings: a ruling posted after a clean verdict voids that verdict until the reviewers judge again against the new ordinal (review-gate). See Gates.

Configuration

KeyEffect
reviewPolicy.maxRounds, reviewPolicy.maxTaskMinutesWhen max_rounds and time_budget fire
principalsWhose rulings authenticate
VINAYA_DEV_REVIEW_LOOP_WATCH_POLL_MS, VINAYA_DEV_REVIEW_LOOP_WATCH_INFRA_BACKOFF_MSWatcher poll (30 s) and automatic-recovery backoff (60 s)

Fixed, not settings: at most 5 infrastructure retries, at most 2 automatic rate-limit resumes, and the two-round finding rule.

What gets logged

WhenEvents
Pause raisedhandoff · raised; dev_review_loop · stop_condition_met, paused, round_ended, journal_finalized{result:'stopped'}
Pause comment postedAn effect sequence, with the same meta.lineage.run as the paused event
Resolvedhandoff · resolved; dev_review_loop · resumed (by: 'driver' or 'principal') or cancelled (by: 'principal')
Automatic retrydev_review_loop · infrastructure_retry
Operator requestoperation · completed

Pauses the controller decides that are not round-assessment observations reuse the existing principal_stop condition and principal_item paused reason in their events; they add no new schema value. See Events.

What's next