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, canonical_repository_key, required_checks}. The canonical repository key is an operator-managed global identity shared by projects that use different URL aliases for the same repository. The service cannot infer aliases; operators must use one key consistently. The target branch remains the project’s validated Git ref. The roster must contain 1–100 unique, nonempty checks. A canonical binding cannot change after submissions or while a hold exists. 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"
}

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 one agent_review and/or human_review activity from the pinned review_mode, plus one integration activity. 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}. 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, the human may reopen an expired, revoked, or policy-stale candidate; this fences its linked attempts and requires a new submission and new reviews. Live current owners must release first, and unresolved jobs/resources always block reopening. After publication intent, use publication reconciliation. Completed workflows cannot be reopened.

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.

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. There is no generic completion endpoint.

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}. This 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.