Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Completion workflow contract

Status: implementation contract for backlog item 2. The service never runs Git or accepts a caller-authored assertion that a required check passed. Every mutation uses an idempotency key and rechecks authority after obtaining SQLite’s writer lock. Replaying a durable result does not renew a lease or refresh any authority field in the saved response.

Policy and immutable submissions

GET /api/v1/projects/{project}/workflow-policy returns {project_id, revision, canonical_repository_key, required_checks}. A required check is {identity, version, environment}; all three strings are stable exact-match identities. A human changes the roster with PUT /api/v1/projects/{project}/workflow-policy and {expected_revision, required_checks}. Normal setup derives the shared repository identity from the project’s URL and reuses an existing saved identity. Public GitHub HTTPS, [email protected]:owner/repo and ssh://[email protected]/owner/repo forms share an identity, including optional .git suffixes and trailing slashes. GitHub owner/repository case is ignored. Other URLs have distinct exact-URL identities; custom SSH hosts are not guessed to be equivalent.

The optional canonical_repository_key is an advanced administrator alias configuration. Existing clients may resend an unchanged saved key. An administrator can assign the same key to a custom alias before it has workflow evidence, after verifying both URLs refer to the same repository. An existing binding cannot change after submissions, while its project has an integration hold, or when changing it would split a binding shared by other projects. Equivalent URLs with conflicting historical bindings fail closed and require explicit reconciliation; saved submissions and holds are never rewritten. Omitting the key on an existing project preserves its binding even if its URL has changed. The current project API does not expose a repository-URL edit. The target branch remains the project’s validated Git ref. The roster must contain 1–100 unique, nonempty checks. The workflow-policy revision is independent of the existing project policy revision. A code submission pins both. There is no implicit empty roster and an agent cannot create, weaken, or replace it.

POST /api/v1/projects/{project}/attempts/{attempt}/submissions accepts the following common fields:

{
  "generation": 1,
  "task_revision": 2,
  "project_policy_revision": 3,
  "workflow_policy_revision": 4,
  "kind": "code",
  "summary": "bounded result summary",
  "acceptance_evidence": [{"criterion": "exact criterion text", "evidence": "bounded evidence"}],
  "handoff": "bounded handoff",
  "repository": "canonical configured repository URL",
  "base_revision": "full source revision",
  "candidate_revision": "full candidate revision",
  "candidate_tree": "full candidate tree",
  "candidate_remote": "credential-free canonical configured repository identity",
  "candidate_ref": "refs/agent-coordinator/candidates/attempt-id"
}

The native CLI derives candidate_remote from the saved credential-free canonical_repository_key. It creates the candidate ref, unless the caller selects one under refs/agent-coordinator/candidates/, by pushing the exact clean worktree commit to the configured remote. Before the submission POST, it fetches that ref into an isolated verification namespace and compares both the fetched commit and tree. The ref is create-only: a prior different commit is never overwritten. A failed push or verification leaves the attempt active and unsubmitted; a remote candidate ref may remain if the push succeeded before a later verification failed, but no workflow activity or publication hold is created. That ref can be reused on retry only when it still names the same exact commit.

Native review and integration claims fetch and verify the recorded candidate ref from the configured remote before claiming work. Integration preparation fetches and verifies it again before making the integration result or durable publication intent. These client checks provide source availability across workstations; the service records the credential-free remote identity and ref but never runs Git or claims to have independently verified the remote. MCP coordination can inspect records but native Git capability is required for code submission, review, and integration.

Rows created before candidate refs were required retain candidate_ref = null. They remain visible as historical records, but are not treated as remotely available. Their review and integration activities are blocked with an explicit candidate_checkpoint_missing condition. An operator may reopen an eligible pre-publication submission for a new revision, and under recovery_mode=agent any agent may revise it with reason_code: "candidate_missing" (the service verifies the missing ref itself); the next code submission must create and verify a durable checkpoint. Never infer or synthesize a ref for a legacy commit.

For kind: "general", omit repository/base/candidate fields and send a workflow policy revision of 0; project policy and task revision are still pinned. Every current acceptance criterion must appear exactly once. Submission requires the current unexpired owner, a matching kind/revision/policy, and quiescent subject work: no retained reservations and no nonterminal or uncertain jobs. It atomically ends the implementation attempt, stores the immutable submission, records the contributor session, blocks ordinary subject claims, and creates the applicable review and integration activities. The response is {submission, subject_task_id, activities, next_actions}.

A code submission creates review activities from the pinned review_mode, plus one integration activity. The modes are agent, human, both, either, and none. both requires an agent_review and a human_review approval. either creates one shared either_review activity: an eligible independent agent or a human may claim it, and one approval satisfies the review requirement. Only one session can own that slot at a time. Requesting changes requires a new submission; another reviewer cannot override the decision on the old candidate. Agent independence is enforced both when claiming and when deciding this shared review, including the existing opt-in rules for non-contributing subagents. The existing agent-only, human-only and no-review modes retain their behavior. A general submission creates only its configured reviews; with no review it completes atomically. Activities have stable IDs and linked internal task IDs. They are returned from GET /api/v1/projects/{project}/tasks/{task}/workflow and GET /api/v1/projects/{project}/workflow-activities/{activity}. Internal activity tasks are excluded from ordinary candidate selection and must be claimed through the workflow API.

Activity authority and review

POST /api/v1/projects/{project}/workflow-activities/{activity}/claim accepts {expected_submission_id, expected_project_policy_revision, expected_workflow_policy_revision} and an optional review_independence. It returns {activity, attempt, lease_remaining_ms, renew_after_seconds, current_authority}. Review activity claims enforce actor type. Agent review also rejects every principal/session recorded as a contributor to any revision of the subject task; contributor history survives releases, recoveries, changes requested, and later submissions. Integration claims require all current approvals and, if automatic integration is disabled, a current human authorization. Integration claims atomically acquire the exclusive hold for the project’s canonical repository key plus target branch. The hold is global across projects and survives attempt/session/credential expiry.

Expired or revoked activity ownership respects the project’s recovery mode. In manual mode, agents cannot take over. A human inspects saved work and physical resources. Before publication intent exists, a human may reopen an expired, revoked, or requirements-stale candidate; under recovery_mode=agent an agent may revise it with a reason code (see below). Either fences its linked attempts and requires a new submission and new reviews. Live current owners must release first (the integration owner revising its own candidate is the exception), and unresolved jobs/resources always block reopening. After publication intent, use publication reconciliation. Completed workflows cannot be reopened.

A submission pins a digest of the task’s judged fields (title, description, acceptance criteria, kind), not whole task or policy revisions. Priority edits and policy changes such as the lease length no longer make a candidate stale. A policy update reconciles required reviews for subjects still in review: it adds reviews a tightened review_mode now requires, cancels queued ones no longer required, and advances subjects whose approvals already satisfy the mode. Approvals are matched by approver class (a human approval satisfies any review kind, an agent approval satisfies agent or either reviews, each approval counts once; both needs one of each). Subjects already in integration keep the review set they were approved under. The required-check roster is captured on the publication intent; the integration result and finalization validate against that captured roster. The reconcile records a workflow.reconciled event; at startup it runs only in normal coordination with a trusted clock and only adds or cancels reviews. Agents holding agent_rule_editing may edit the rules text but never review_mode, recovery_mode or the permission switches, so an author cannot loosen review to land its own candidate. An ac_amendment is refused when no review is required. The submission reports its ac_amendment, and a review decision records its amendment_decision and, when the poster states it, its review_independence (distinct_launch, distinct_host or distinct_vendor).

distinct_launch is enforced (decision U5). A session’s launch is the root of its parent_session_id chain as the service recorded it at registration: a top-level session is its own launch, and a registered subagent shares its parent’s. When a review claim or decision asserts distinct_launch, the service also refuses (reviewer_shares_launch) a reviewer whose launch is the launch of any recorded contributor session of the subject task or of the tasks its landing range carries. When the integrator later records a landing range, an approval recorded as distinct_launch whose reviewer shares a launch with a contributor of a carried task is voided like a contributor’s approval. Without the assertion the principal/session checks above apply unchanged, so a same-launch subagent review permitted by allow_subagent_reviews still works but cannot be recorded as distinct_launch. The supervisor’s reviewer asserts it at claim and decision. The launch walk follows at most 64 parent hops; a reviewer or contributor chain nested deeper cannot be resolved, so a distinct_launch assertion involving it is refused (reviewer_shares_launch) rather than trusted. For an agent previewing a review activity, the read-only preconditions result also reports distinct_launch_eligible and distinct_launch_unmet_preconditions (the reviewer_shares_launch blocker the assertion would add), leaving eligible_to_claim and unmet_preconditions unchanged for clients that do not assert it. Launch lineage is only what sessions declared at registration; a shared credential can always open a fresh top-level session, so this enforces the recorded launch separation, not process isolation.

An authenticated agent may reconcile only when the exact current candidate and policy are unchanged, current scoped decisions are resolved, a fresh observation of the pinned repository branch exactly matches the immutable pre-publication base or intended result, the old publisher is confirmed stopped, the integration owner is no longer live, and no registered job is live or uncertain and no reservation remains held. Agent reconciliation cannot publish or create check receipts. A moved target, missing durable intent or local journal, conflicting result, or any ambiguous producer remains human-gated with the target hold preserved. The native CLI serializes its local journal and records its evidence; the service stores this as a client attestation and does not verify remote Git or process state itself. Both human and agent reconciliation retain the original record and create a fresh integration activity under the same target hold.

Activity attempts renew through the existing POST /api/v1/projects/{project}/attempts/{attempt}/renew endpoint and use existing checkout, reservation, and job endpoints. They release through POST /api/v1/projects/{project}/workflow-activities/{activity}/release with {generation, summary, blocked:false}; a release after publication intent is refused. Inspect and reconcile that publication instead; reconciliation creates a replacement activity and retains the historical result. Workflow state changes always require the activity ID as an additional candidate/type guard.

POST /api/v1/projects/{project}/workflow-activities/{activity}/review accepts:

{
  "generation": 1,
  "submission_id": "uuid",
  "decision": "approved",
  "summary": "bounded review summary",
  "findings": [{"severity": "required", "remedy": "bounded remedy", "evidence": "bounded evidence"}]
}

decision is approved or changes_requested; severity is required or advisory. The current, unexpired linked attempt and exact current submission are required. A decision is immutable. Changes requested completes that review activity, cancels the other pending activities, makes the subject eligible for a new revision attempt, and keeps all old submissions, approvals, findings, and contributors. A later submission creates fresh activities; old approvals do not transfer. The subject’s task and project policy revisions must still match the submission for approval to affect readiness.

Checks and integration

Required check receipts are selected by job ID; clients do not submit check outcomes. A selectable job must belong to the current integration activity attempt, have a terminal producer state of succeeded, exit code 0, inputs_unchanged: true, no reconciliation amendment, and exact source revision and tree matching the intended integrated result. Each job registered for a check also carries immutable check_identity, check_version, and check_environment fields that exactly match one pinned required-check entry. One current producer receipt must cover every roster entry, with no duplicate identity tuple. Candidate jobs cannot satisfy integrated-result checks merely because candidate and result happen to share a commit label.

When project policy disables automatic integration, a human records intent with POST /api/v1/projects/{project}/workflow-activities/{activity}/authorization and {submission_id, expected_project_policy_revision, expected_workflow_policy_revision, summary}. Authorization is immutable and applies only to that current candidate and policy pair.

After claiming integration and registering an isolated checkout, the owner calls POST /api/v1/projects/{project}/workflow-activities/{activity}/publication-intent:

{
  "generation": 1,
  "submission_id": "uuid",
  "observed_target_revision": "full target revision before publication",
  "observed_target_tree": "full target tree before publication",
  "result_revision": "full intended integrated revision",
  "result_tree": "full intended integrated tree"
}

The service saves one immutable intent before any Git-side compare-and-swap. The client must independently verify that the remote target still equals observed_target_revision before publishing. Repeating with a new key cannot replace an intent; retry uses the original key.

A fresh activity GET returns publication_allowed and qualifying_check_job_ids. Publishing is allowed only while the exact candidate, policies, approvals, authorization, activity lease, canonical hold, intent, and one successful stable-input exact-result producer per required roster entry are all current. Publication intent alone is not permission to publish. Once any integration result exists, publication is no longer allowed for that activity.

POST /api/v1/projects/{project}/workflow-activities/{activity}/integration-result accepts {generation, submission_id, publication_state, observed_target_revision, result_revision, result_tree, check_job_ids, summary} where publication state is published, not_published, or uncertain. Values must match the saved intent. An uncertain result may omit checks, is durable, makes the activity recovery-required, and retains the global hold. It never completes work or releases the hold. A known not_published result also retains the hold until explicit reconciliation.

A human reconciles with POST /api/v1/projects/{project}/workflow-activities/{activity}/publication-reconciliation and {submission_id, disposition, observed_target_revision, observed_target_tree, evidence}, where disposition is published, not_published, or target_moved. target_moved records that the intended result was published but a later external actor advanced the target before finalization; it closes the old activity and hold and creates a fresh integration activity for the same approved candidate. The new activity requires fresh authorization and checks against its new result. Reconciliation cannot invent check success. Confirmed publication must match the intent’s exact result revision/tree. Confirmed nonpublication permits an explicit new integration attempt after the old hold is closed in the same transaction.

Agents use the separate POST /api/v1/projects/{project}/workflow-activities/{activity}/agent-publication-reconciliation operation with {attempt_id, generation, submission_id, disposition, canonical_repository_key, target_branch, observed_target_revision, observed_target_tree, observed_at, local_journal_verified, publisher_stopped, evidence}. observed_at is Unix milliseconds and must be no more than two minutes old. Both boolean fields must be true. The disposition can only be published when both identities equal the immutable intended result, or not_published when both equal the saved pre-publication target. The endpoint also requires an expired or revoked integration owner, no registered live/uncertain job, no held reservation, current candidate/policy/decisions, and the still-held original target. Moved or missing targets, an unavailable local journal, a running publisher, or any other uncertainty stay human-gated. The service cannot independently inspect Git or prove that a workstation process stopped; agent evidence remains a client attestation. The native CLI obtains its protected journal lock and remote observation before it sends this mutation.

For a known published result, call POST /api/v1/projects/{project}/workflow-activities/{activity}/finalize with {generation, submission_id, observed_target_revision, observed_target_tree}. These last fields are a fresh post-publication remote observation and must equal the intended result revision and tree. Finalization verifies the exact current candidate, task revision, project policy revision, workflow-policy roster, approvals, authorization, intent/result, exact remote observation, source-bound job receipts, activity authority, and quiescence of both subject and integration tasks. It then atomically completes the integration activity and subject task, closes the global hold, and makes dependencies eligible. The only other completion path is the human-only external delivery below; there is no generic or agent completion endpoint.

Tasks delivered outside the coordinator

Some changes cannot go through review and integration: a task that touches privileged paths (for example .github/workflows) is landed by its owner with scripts/ship.py, which fast-forwards main to a commit whose required checks are already green. Submitting that commit afterwards would send an already-published change through review and an integrator publication that is a no-op, and canceling the task would record the delivered work as canceled. Use the external completion instead, so the task ends done and counts as done in digests.

POST /api/v1/projects/{project}/tasks/{task}/complete-external takes {expected_revision, delivered_revision, target_branch, reason, checks}: delivered_revision is the full revision on the target branch that contains the delivered change, target_branch must equal the project’s target branch, reason says how it was delivered, and checks is a list of {identity, conclusion} results read on that exact revision. Every conclusion must be success, and when the project has a workflow policy every roster check identity must be cited (required_check_missing). The task must be open, at expected_revision, with no current attempt and no active review or integration work; release or recover the attempt first (task_attempt_protected, task_workflow_protected). The task becomes done (not archived or deleted), its dependents become eligible, and the revision, branch, check results, reason and acting human are kept in the task.completed_externally event, which task history shows as the completed_externally lifecycle event.

This is an exception to the rule above that the service accepts no caller-authored assertion about checks, because the service cannot reach the repository: it records the human’s attestation that the revision is on the target branch and that its checks passed; it does not verify either. It is a labelled human gate (task_external_completion). Agent sessions are always refused, whatever the task’s origin or the project’s delegation, so an agent cannot use it to skip review of an ordinary candidate. Use it only after confirming, from the remote, that delivered_revision is an ancestor of the target branch tip and that the required checks succeeded on it. An ordinary candidate still needs review and integration.

The caller must be a human actor, and only a browser password sign-in is one, so the manager’s task detail page is the way to use it: Complete as delivered externally is offered to a signed-in human on an open task with no current attempt and no active review or integration, prefills the target branch and the roster’s check identities, and shows the service’s refusal code. A CLI session authenticates with an agent credential and is refused with 403 operation_not_permitted.

Local worktree cleanup after completion

Every agent cleans up after its own completed tasks, in every project, so finished work does not waste or clutter workstation disk. The procedure has one source of truth: the service’s worktree and branch cleanup instruction (WORKTREE_CLEANUP_INSTRUCTIONS in crates/server/src/discovery.rs). It is the same for every project and is served in the orientation instructions that each agent acknowledges before claiming, as step 8 of the orientation’s completion_workflow, and in the MCP server instructions (also published anonymously in /api/v1/info under data.agent_startup.mcp.instructions). Follow that served text; this book deliberately does not restate it.

Coordination hooks

The coordination module must call these workflow hooks while holding its existing writer transaction:

  • guard_normal_claim(connection, project_id, task_id) rejects internal activity tasks and subjects with a current submission; record_contributor(connection, task_id, actor_id, session_id, now) runs when normal or recovery work is claimed and again before owned edit/checkout.
  • guard_subject_mutation(connection, project_id, task_id) rejects definition/dependency edits while a submission is current. Policy changes may proceed, but immediately make pinned submissions stale until an authorized new submission/reconciliation.
  • guard_release_or_recovery(connection, project_id, task_id) preserves workflow integration holds and refuses recovery/requeue paths that would bypass a current submission or publication uncertainty.
  • activity checkout/reservation/job registration calls guard_activity_work(connection, project_id, activity_task_id, now) to reject stale candidates and policies while still allowing checkpoint, renewal, release, and late reporter observations.
  • task detail/status reads call workflow_snapshot(connection, project_id, task_id, now) and expose its submission, activities, blockers, and next actions. Ordinary ready lists exclude task IDs present as workflow_activities.activity_task_id.

The jobs module must accept optional check identity/version/environment at job registration and store them immutably. It must expose a server-side receipt lookup; workflow completion never accepts caller-authored status JSON.

Normal attempts pin attempts.task_revision and attempts.policy_revision when claimed. Submission compares those stored values to the current task and project and to the request. Existing pre-migration attempts have null pins and must be released and reclaimed; callers cannot choose a newer revision at submission.

An authenticated human may cancel a stale current submission with POST /api/v1/projects/{project}/tasks/{task}/workflow/reopen and {submission_id, reason}. An agent session uses the same endpoint to revise (CLI agent-coordinator revise) and must add reason_code and, where noted, evidence; the project must use recovery_mode=agent, otherwise the refusal is a labelled human gate (details.gate: "human_reopen_required"):

reason_codeAllowed agent caller
conflict, check_failedThe session owning the submission’s live integration attempt; evidence required
candidate_missingAny agent, when the code submission has no durable candidate ref
requirements_changedAny agent that is not a task contributor, when the judged-field digest changed
author_withdrawThe submission’s author

Agents may revise a subject at most three times in 24 hours; the fourth attempt returns revise_limit_reached with details.required_actor: "human" and the task preconditions show the same code until a human acts; agent claims of the task are refused with it. On integrator projects the integrator’s own conflict revise may instead serialize the subject after the landing that moved the target; a serialized revise does not count toward the three, but six revises of any kind in 24 hours park the subject (see the integrator revise route in the API contract). Reopening requires subject and activity quiescence, refuses uncertain or known publication, cancels pending activities, preserves all evidence, closes any safely releasable hold, and makes ordinary revision work eligible.