Orchestrator API Reference

spec-kitty orchestrator-api is the canonical JSON-first host interface for external orchestrators.

It is intentionally stricter than the human-facing CLI:

  • use --mission; the --feature flag has been removed from all user-facing commands
  • expect one JSON envelope on stdout for both success and failure
  • treat error_code as the stable machine discriminator
  • do not append --json; JSON is the default output for this command group

Canonical Terms

  • Mission Type = reusable blueprint key
  • Mission = tracked item under kitty-specs/<mission-slug>/
  • Mission Run = runtime/session instance

Contract Version

  • CONTRACT_VERSION: 1.18.0
  • MIN_PROVIDER_VERSION: 0.1.0
  • Startup probe: spec-kitty orchestrator-api contract-version
  • A --provider-version below MIN_PROVIDER_VERSION, or one that does not parse as a version string, always returns exit code 1 with error_code: CONTRACT_VERSION_MISMATCH — never a silent default. Call contract-version with --provider-version at startup to fail fast on an incompatible provider instead of discovering the mismatch mid-workflow.

Version history (the authoritative ledger lives at the CONTRACT_VERSION constant in src/specify_cli/orchestrator_api/envelope.py):

  • 1.1.0 — start-implementation allocates/reuses the lane worktree; its response carries lane_id / lane_branch / lane_base_ref, and workspace_path means that lane worktree. New error code LANE_ALLOCATION_FAILED.
  • 1.2.0 — new read-only resolve-workspace command (#2337). Purely additive.
  • 1.3.0 — transition accepts structured --review-result-json, allowing guarded in_review exits without recovery-only force overrides.
  • 1.4.0 — added 11 new verbs (#3837): design-phase scaffolding (specify, plan, tasks, check-prerequisites, record-analysis); decision-resolution (open-decision, resolve-decision, defer-decision, cancel-decision, answer-decision); plus the read-only design-status query verb. Purely additive — see Design-Phase Commands below. An external host can now drive the entire design pipeline (specify → plan → tasks → check-prerequisites/record-analysis → decision resolution) without crossing into host-CLI territory; see Host Boundary Rules for the updated Boundary Decision Matrix.
  • 1.5.0 — the tasks verb's pass-through data gained the planning_commit object (action / sha / previous_sha / branch_tip) from the delegate finalize-tasks --json payload (#4141, the --refresh-planning-commit re-point affordance). Purely additive.
  • 1.6.0 — the planning_commit.action vocabulary gains "repinned" (#4827, the --refresh-planning-commit --allow-orphaned re-point affordance). Purely additive.
  • 1.7.0 — accept-mission now applies the host readiness verdict (#4934): it calls collect_feature_summary(..., strict_metadata=True) and refuses with MISSION_NOT_READY whenever summary.ok is False, carrying outstanding / activity_issues / skipped_checks / blocked_checks plus the mission identity fields — see Acceptance Payload below. Acceptance is now recorded only through the same FR-010 locked pre-stamp verdict guard the host accept CLI uses, so a verdict committed between the readiness check and the stamp is refused rather than silently accepted over. A behavioural tightening (a call that previously accepted can now refuse), not purely additive.
  • 1.8.0 — the tasks verb's pass-through data gains three keys (parsed_spec_ids, rejected_requirement_refs, success_criteria_coverage) from the delegate finalize-tasks --json payload (requirement-id-grammar-01M3NRCA WP02). The plan verb's pass-through data gains requirement_id_warnings, and its previously unregistered delegate error codes now remap onto the closed PLAN_SETUP_FAILED envelope with the real code preserved as data.reason (WP05) — this closes a latent leak: SPEC_FILE_MISSING, TEMPLATE_CONFIGURATION_ERROR and PLAN_CONTEXT_UNRESOLVED previously passed through verbatim as error_code (none was contract-registered) and now surface as PLAN_SETUP_FAILED too. Additive keys plus a closed-envelope remap on plan's previously-leaking codes; minor bump.
  • 1.9.0 — the consolidate-mission failure envelope's data gains teardown_error_code when the landing succeeded and only the cleanup after it refused (COORD_MOVED_AFTER_LANDING, #5613; see data.teardown_error_code). The envelope error_code stays PREFLIGHT_FAILED and data.errors is unchanged. Purely additive: the key is absent on every other failure.
  • 1.10.0 — consolidate-mission can newly refuse a mission before it merges any lane (#5668): a lane holding content committed after review approved it, or an approved work package whose approval records no usable lane head. The envelope error_code stays PREFLIGHT_FAILED and data gains preflight_error_code (see data.preflight_error_code). A call that used to succeed can now refuse, so a minor bump; the key is absent on every other failure. data also carries preflight_error_codes, every distinct code the refusal names, the first equal to preflight_error_code (#5720).
  • 1.11.0 — accept-mission and consolidate-mission take --origin-check [enforce|warn] and can newly refuse before they read evidence or move a lane, when the Mission's status evidence (and, for consolidate-mission, its approved lane branches) is behind, diverged from or unreachable on its remote (#5780). The envelope error_code stays MISSION_NOT_READY / PREFLIGHT_FAILED; data gains preflight_error_code, preflight_error_codes and origin_freshness (see Origin freshness), and a successful warn run gains origin_warnings. A call that used to succeed can now refuse, so a minor bump; every key is absent otherwise.
  • 1.12.0 — adds governed context/interviews, bounded artifact authoring and native next, plus the truthful Python delivery profile and capability probe. Existing commands and envelope fields remain. For API-authored Missions, successful design completion/finalization through orchestrator-api now checks accepted provenance and canonical prerequisites; stale or unfinished inputs refuse. Native spec-kitty next --result success and agent mission finalize-tasks run the same check (#5879) and refuse with the same DESIGN_* code; agent tasks finalize-tasks does not. New error_code values: DESIGN_ARTIFACT_UNSUPPORTED, DESIGN_BOUNDS_EXCEEDED, DESIGN_COMMIT_FAILED, DESIGN_CONTEXT_FAILED, DESIGN_CONTEXT_STALE, DESIGN_FINALIZED, DESIGN_INTERVIEW_FAILED, DESIGN_INTERVIEW_INCOMPLETE, DESIGN_INTERVIEW_INVALID, DESIGN_IO_FAILED, DESIGN_LOCK_TIMEOUT, DESIGN_PARENT_STALE, DESIGN_PATH_REFUSED, DESIGN_PREREQUISITES_FAILED, DESIGN_RECEIPT_UNREADABLE, DESIGN_REQUEST_INVALID, DESIGN_REVISION_CONFLICT, DESIGN_STATUS_EVENT_LOG_UNREADABLE, RUNTIME_NEXT_FAILED, RUNTIME_BLOCKED and UNSUPPORTED_CAPABILITY. Behaviour change: plan now fails when its delegate payload result is blocked or error instead of emitting success. See Governed planning delivery.
  • 1.13.0 — start-implementation can newly fail with STATUS_LOCK_HELD (#5819) when the single_branch write-checkout claim lock stays held by another claimant past its bound (it used to escape as a traceback). The failure data carries the Mission identity, wp_id, message and lock_timeout_seconds. A new error_code on an existing verb, so a minor bump; no field is removed or renamed.
  • 1.14.0 — the safe-commit verbs (append-history) can newly fail with SAFE_COMMIT_PATH_LOOP (a requested path is a symlink loop), SAFE_COMMIT_INDEX_DELETION_CONFLICT (an index deletion conflicts with the commit) and SAFE_COMMIT_INDEX_RESIDUE (#5443). SAFE_COMMIT_INDEX_RESIDUE means the commit DID land (commit_sha names it) but the real index still stages a committed path; it refines SAFE_COMMIT_RECOVERY_FAILED, which keeps meaning the commit may not have landed. New error_code values on an existing verb, so a minor bump; no field is removed or renamed.
  • 1.15.0 — the safe-commit verbs (append-history) can newly fail with SAFE_COMMIT_UNREQUESTED_PATHS (#5903) when a pre-commit hook that runs git add -u sweeps the operator's other modified tracked files into the commit: the commit DID land (commit_sha names it) but it carries paths the operation never requested, and the operator's real index is re-synced for those paths before the failure is raised. Like SAFE_COMMIT_INDEX_RESIDUE it refines SAFE_COMMIT_RECOVERY_FAILED (the commit landed). A new error_code on an existing verb, so a minor bump; no field is removed or renamed.
  • 1.16.0 — answer-decision now surfaces decision-log commit warnings in its data under a warnings key (list of strings) when the decision record's safe_commit is refused (#6222, follow-up to #3930, which fixed the host next --answer --json surface only). Previously an external orchestrator got success: true while the committed decision record diverged silently. The key is present only when non-empty — a clean commit leaves the payload byte-identical — so this is a purely additive, optional field; no field is removed or renamed.
  • 1.18.0 — consolidate-mission now skips a fully-canceled-with-provenance lane at parity with host consolidate (#5814): a lane whose every work package is an acceptable canceled-with-provenance ending is no longer merged, so its committed content never lands on the target and its work packages are neither gate-evaluated nor marked merged/done. A call that used to answer success: true while shipping that canceled content now still answers success: true but leaves the canceled lane off the target (the approved survivor lanes land unchanged). A behaviour change to the shipped tree of a previously-success call, so a minor bump; no envelope field or error_code is added, removed or renamed.
  • 1.17.0 — resolve-decision, defer-decision and cancel-decision can newly fail with DECISION_EVENT_REPAIR_FAILED (#5681), and DESIGN_STATUS_EVENT_LOG_UNREADABLE (already returned there for an unreadable decision index) gains a new trigger on them, an unreadable event log: an idempotent retry of an interrupted terminal call now re-writes the missing DecisionPointResolved event and reports its event_lamport (previously always null on an idempotent call). A newly reachable error_code on existing verbs and a non-null event_lamport on idempotent calls, so a minor bump; no field is removed or renamed.

Response Envelope

Every command returns exactly one JSON object with these 7 top-level keys:

{
  "contract_version": "1.18.0",
  "command": "orchestrator-api.mission-state",
  "timestamp": "2026-04-08T12:00:00+00:00",
  "correlation_id": "corr-0123456789abcdef",
  "success": true,
  "error_code": null,
  "data": {}
}
Field Meaning
contract_version Host API contract version.
command Fully-qualified command name.
timestamp ISO 8601 UTC response timestamp.
correlation_id Unique per-response correlation token.
success true for success, false for failure.
error_code Machine-readable failure code, otherwise null.
data Command-specific payload.

Parser and usage failures also return the same envelope shape with error_code="USAGE_ERROR".

Canonical Mission Identity

Success payloads that identify a tracked mission emit:

Field Meaning
mission_id Canonical ULID machine identity. Aggregate routing uses this field.
mission_slug Human-readable mission slug. Display context only.
mission_number Display-only numeric prefix. null pre-merge, assigned at merge time. Never used for identity.
mission_type Blueprint key

The --mission selector accepts any of mission_id, mid8 (first 8 chars of the ULID), or mission_slug. Ambiguous handles return MISSION_AMBIGUOUS_SELECTOR and list the candidates — there is no silent fallback. See Mission ID Canonical Identity Migration.

Forbidden in orchestrator-api payloads:

  • feature_slug

Removed at the CLI boundary:

  • --feature (hard-removed in #1060; passing it yields exit code 2 with "No such option: --feature")

Commands

Command Mutates state Purpose
contract-version no Check API compatibility.
mission-state no Query mission state and WP lanes.
list-ready no List WPs ready to start.
resolve-workspace no Resolve a WP's existing lane workspace (contract >= 1.2.0).
start-implementation yes Atomically move a WP into implementation.
start-review yes Claim active review for a WP.
transition yes Emit one explicit lane transition.
append-history yes Append a WP activity-log note.
accept-mission yes Record mission acceptance.
consolidate-mission yes Consolidate the mission into its target branch.
specify yes Create a mission scaffold (contract >= 1.4.0).
plan yes Scaffold plan.md for a mission (contract >= 1.4.0).
tasks yes Finalize WP task metadata (contract >= 1.4.0).
check-prerequisites no Read-only mission-prerequisite context for analyze (contract >= 1.4.0).
record-analysis yes Persist an analyze report, artifact-verified (contract >= 1.4.0).
open-decision yes Open a Decision Moment ledger entry (contract >= 1.4.0).
resolve-decision yes Resolve a decision with a final answer (contract >= 1.4.0).
defer-decision yes Defer a decision for later resolution (contract >= 1.4.0).
cancel-decision yes Cancel a decision (contract >= 1.4.0).
design-status no Read-only design-phase status query (contract >= 1.4.0).
answer-decision yes Resolve a spec-kitty next decision_required moment, with full host-CLI event/lifecycle parity (contract >= 1.4.0).
artifact-read no Read bounded canonical design content and its exact revision (contract >= 1.12.0).
artifact-submit yes Author and commit bounded content with revision and lineage checks (contract >= 1.12.0).
design-validate no Check canonical prerequisites without completing a step (contract >= 1.12.0).
design-context no Discover resolved templates, governance and interview slots (contract >= 1.12.0).
interview-record yes Record canonical specification or planning interview answers (contract >= 1.12.0).
next query: no; advancement: yes Query or advance the native workflow authority (contract >= 1.12.0).

Legacy command names such as feature-state, accept-feature, and merge-feature are forbidden.

See Design-Phase Commands below for the full request/response/error-code contract of the 1.4 design verbs, and Governed Planning Delivery for the six 1.11 verbs.

Required Flags

The tracked-mission selector is always:

spec-kitty orchestrator-api mission-state --mission 077-mission-terminology-cleanup

Run-affecting implementation and review mutations require --policy. Today that means start-implementation, start-review, and transition when the target lane is run-affecting. append-history, accept-mission, and consolidate-mission do not accept --policy.

The policy JSON object must include:

  • orchestrator_id
  • orchestrator_version
  • agent_family
  • approval_mode
  • sandbox_mode
  • network_mode
  • dangerous_flags

Secret-like values in --policy are rejected.

Minimal policy example:

{
  "orchestrator_id": "spec-kitty-orchestrator",
  "orchestrator_version": "0.1.0",
  "agent_family": "claude",
  "approval_mode": "full_auto",
  "sandbox_mode": "workspace_write",
  "network_mode": "none",
  "dangerous_flags": []
}

Lane Model for Orchestrators

External providers should treat these lanes as the public orchestration model:

Lane Meaning
planned WP exists but has not started.
claimed WP is claimed by an actor as part of implementation start.
in_progress Implementation or rework is active.
for_review Implementation is ready for review.
in_review A reviewer has claimed active review.
approved Review accepted but integration may still be pending.
done WP is complete.
blocked WP cannot continue without intervention.
canceled WP was intentionally canceled.

The reference orchestrator normally drives:

planned -> claimed -> in_progress -> for_review -> in_review -> done

Rejected review cycles move back through:

in_review -> in_progress -> for_review

Acceptance Payload

accept-mission requires every WP to be approved or done. On success it returns:

Field Meaning
accepted_wps WPs counted by mission acceptance (approved plus done)
approved_wps Review-passed WPs still awaiting merge/integration
done_wps WPs already merged/integrated
merge_pending_wps Alias of approved_wps; WPs accepted-ready but not done

accept-mission does not move WPs from approved to done; merge owns that transition.

MISSION_NOT_READY (contract >= 1.7.0)

Once every WP is approved/done, accept-mission additionally applies the SAME host readiness verdict the accept CLI computes via collect_feature_summary(..., strict_metadata=True) — one readiness authority, never a second computation. When summary.ok is False (a pending/failing acceptance matrix, a missing/corrupt lanes.json, a target-branch mismatch, an unmet path convention, a dirty working tree, and so on), accept-mission refuses with MISSION_NOT_READY and this error data:

Field Meaning
outstanding dict[str, list[str]] — AcceptanceSummary.outstanding()'s buckets (e.g. lane_blockers, metadata, git_dirty, path_violations)
activity_issues list[str] — human-readable readiness issues
skipped_checks list[{check, detail}] — gates the readiness pass never reached
blocked_checks list[{check, detail}] — gates that stopped readiness outright (e.g. a missing lanes.json reports check: "lanes_manifest")

No acceptance is recorded on this refusal: meta.json gains no accepted_at / acceptance_mode / acceptance_history, and HEAD is unchanged. The readiness gate may still update the matrix rows it judged in the working-tree acceptance-matrix.json, exactly as the host accept CLI does.

Acceptance is recorded only inside the same FR-010 locked pre-stamp verdict re-check the host accept CLI uses: the acceptance matrix is re-read fresh under the per-mission status lock immediately before the write. A verdict that a concurrent acceptance-verdict invocation commits between the readiness check above and this stamp — or a lock-acquisition timeout — is still refused with MISSION_NOT_READY, never silently accepted over.

Design-Phase Commands

Contract 1.4.0 (#3837) adds 11 verbs so an external host can drive the entire design pipeline — specify → plan → tasks → check-prerequisites/record-analysis → decision resolution — the same way it already drives the WP-implementation loop above, without ever calling spec-kitty next or another host-CLI command directly. See Host Boundary Rules for when to use these verbs versus the host CLI.

specify

Creates a mission scaffold — the same enriched payload the host CLI's own specify --json returns (agent_feature.create_mission's raw payload plus scaffold_only / spec_state / next_action / next_step), not the unenriched dict one layer beneath it.

Request:

spec-kitty orchestrator-api specify \
  --mission 090-orchestrator-driven-mission \
  --mission-type software-dev \
  --topology lanes \
  --policy '{"orchestrator_id":"ci","orchestrator_version":"1.0.0","agent_family":"ci-bot","approval_mode":"full_auto","sandbox_mode":"workspace_write","network_mode":"none","dangerous_flags":[]}'
Flag Required Meaning
--mission yes Mission slug to create.
--mission-type yes Blueprint key (e.g. software-dev).
--topology no Create-time mission shape: single_branch | lanes | coord | lanes_with_coord. Defaults to the context-derived default (matches the host CLI's own --topology default) when omitted.
--policy yes Policy metadata JSON (see Required Flags).

Error codes: POLICY_METADATA_REQUIRED, POLICY_VALIDATION_FAILED, MISSION_ALREADY_EXISTS (the mission-creation delegate refused a duplicate — a live same-key prior mission (#4033), or a retry whose scaffold is byte-identical to what is already committed — surfaced as the typed MissionAlreadyExistsError signal), MISSION_CREATE_FAILED (any other creation failure with no more specific error_code; a typed upstream code such as ACTIVE_CHARTER_CONFIG_INVALID (raised as ActiveCharterConfigError) is passed through verbatim instead). Classification consumes only the delegate's typed error_code, never its message prose (#3861).

plan

Scaffolds plan.md. Deliberately unenriched — the response data is agent_feature.setup_plan's raw payload verbatim (the host CLI's own --json path adds no enrichment here either, unlike specify), with mission_slug filled in only if the delegate payload did not already carry it.

Request:

spec-kitty orchestrator-api plan \
  --mission 090-orchestrator-driven-mission \
  --policy '{"orchestrator_id":"ci","orchestrator_version":"1.0.0","agent_family":"ci-bot","approval_mode":"full_auto","sandbox_mode":"workspace_write","network_mode":"none","dangerous_flags":[]}'

Error codes: POLICY_METADATA_REQUIRED, POLICY_VALIDATION_FAILED, MISSION_NOT_FOUND, PLAN_SETUP_FAILED (delegate call failed with no more specific error_code of its own). A typed upstream code that is itself contract-registered passes through verbatim; a typed but unregistered delegate code (e.g. SPEC_REQUIREMENT_IDS_INVALID, SPEC_FILE_MISSING, TEMPLATE_CONFIGURATION_ERROR, PLAN_CONTEXT_UNRESOLVED) is instead reported as PLAN_SETUP_FAILED, with the real code preserved as data.reason (requirement-id-grammar-01M3NRCA WP05).

tasks

Finalizes WP task metadata. Same deliberate non-enrichment as plan — the response data is agent_feature.finalize_tasks's raw payload verbatim, with mission_slug always filled in (unlike plan, the raw payload never carries it).

Request:

spec-kitty orchestrator-api tasks \
  --mission 090-orchestrator-driven-mission \
  --policy '{"orchestrator_id":"ci","orchestrator_version":"1.0.0","agent_family":"ci-bot","approval_mode":"full_auto","sandbox_mode":"workspace_write","network_mode":"none","dangerous_flags":[]}'

Error codes: POLICY_METADATA_REQUIRED, POLICY_VALIDATION_FAILED, MISSION_NOT_FOUND, TASKS_FINALIZE_FAILED (delegate call failed with no more specific error_code of its own; a typed upstream code is passed through verbatim instead).

check-prerequisites

Read-only mission-prerequisite context for /spec-kitty.analyze — it supplies context only and never performs analyze's cross-artifact reasoning itself. No --policy required.

Request:

spec-kitty orchestrator-api check-prerequisites \
  --mission 090-orchestrator-driven-mission \
  --include-tasks
Flag Required Meaning
--mission yes Mission slug.
--include-tasks no Include tasks.md validation (matches the host CLI's own --include-tasks default of false).

Response data: the host CLI's own validate_feature_structure shape, with mission_slug filled in (the raw shape does not already carry it).

Error codes: MISSION_NOT_FOUND (also returned — translated — when the delegate call reports its own MISSION_CONTEXT_UNRESOLVED, which never crosses this surface verbatim per the Terminology Canon), CHECK_PREREQUISITES_FAILED (fallback; any other typed upstream error_code is passed through verbatim).

record-analysis

Persists an /spec-kitty.analyze report. Success is determined by re-reading the artifact off disk after the write, never by the underlying write call's own return/raise/hang behavior (NFR-004): a caller MUST NOT build retry logic that assumes a raised exception or a timeout means the write did not happen — it may have. Concretely, success: true requires BOTH (a) analysis-report.md's re-read generated_at is strictly later than this call's start timestamp, AND (b) the re-read verdict matches the verdict submitted in this call's body. An unknown verdict (no valid analysis-findings/v1 carrier in the submitted body) is never reported as success, even when the write itself genuinely, freshly succeeds.

Request:

spec-kitty orchestrator-api record-analysis \
  --mission 090-orchestrator-driven-mission \
  --input-file analysis-report-draft.md \
  --agent ci-bot \
  --policy '{"orchestrator_id":"ci","orchestrator_version":"1.0.0","agent_family":"ci-bot","approval_mode":"full_auto","sandbox_mode":"workspace_write","network_mode":"none","dangerous_flags":[]}'
Flag Required Meaning
--mission yes Mission slug.
--input-file no (default -) Markdown report path, or - to read the report body from stdin.
--agent no Agent name that produced the analysis report.
--policy yes Policy metadata JSON.

Response data (success): mission_slug, path (the written analysis-report.md), verdict, generated_at.

Error codes: POLICY_METADATA_REQUIRED, POLICY_VALIDATION_FAILED, MISSION_NOT_FOUND, RECORD_ANALYSIS_INPUT_FILE_NOT_FOUND (--input-file could not be read), RECORD_ANALYSIS_EMPTY_BODY (body was empty), RECORD_ANALYSIS_MALFORMED_CARRIER (the body carried a present-but-invalid analysis-findings/v1 carrier), PLACEMENT_RESOLUTION_REQUIRED, DIRTY_WORKTREE (pre-existing uncommitted changes block the write), RECORD_ANALYSIS_WRITE_NOT_CONFIRMED (no fresh write could be confirmed by re-reading the artifact), RECORD_ANALYSIS_VERDICT_UNRELIABLE (a fresh write WAS confirmed but its re-read verdict either disagrees with what this call submitted, or the submission itself computed to unknown).

open-decision

Opens a new Decision Moment ledger entry, or returns idempotently if a matching one already exists.

Request:

spec-kitty orchestrator-api open-decision \
  --mission 090-orchestrator-driven-mission \
  --origin specify \
  --input-key clarification.scope \
  --question "Should this mission include the legacy migration path?" \
  --step-id interview-scope \
  --options '["yes", "no"]' \
  --actor ci-bot \
  --policy '{"orchestrator_id":"ci","orchestrator_version":"1.0.0","agent_family":"ci-bot","approval_mode":"full_auto","sandbox_mode":"workspace_write","network_mode":"none","dangerous_flags":[]}'
Flag Required Meaning
--mission yes Mission slug.
--origin yes Origin flow: charter | specify | plan.
--input-key yes The input key this decision governs.
--question yes Human-readable question text.
--step-id no Interview step identifier.
--slot-key no Slot key (use when --step-id is unavailable).
--options no Candidate answers as a JSON array string.
--actor yes Actor identity.
--policy yes Policy metadata JSON.

Response data: mission identity fields (mission_slug / mission_number / mission_type — no mission_id, unlike the Canonical Mission Identity payload shape documented above), decision_id, status ("open"), idempotent, artifact_path, event_lamport.

Error codes: POLICY_METADATA_REQUIRED, POLICY_VALIDATION_FAILED, MISSION_NOT_FOUND, INVALID_ORIGIN_FLOW (--origin outside {charter, specify, plan}, rejected before the decision service is ever called), DECISION_MISSING_STEP_OR_SLOT (neither --step-id nor --slot-key supplied), DECISION_ALREADY_CLOSED (a matching logical-key entry already exists in a terminal state), DECISION_EVENT_REPAIR_FAILED (the idempotent-open path could not re-emit a missing ledger event).

resolve-decision

Resolves a decision with a concrete final answer.

Request:

spec-kitty orchestrator-api resolve-decision \
  --mission 090-orchestrator-driven-mission \
  --decision-id 01HXYZDECISIONULID \
  --final-answer "yes" \
  --rationale "Legacy migration is in scope per stakeholder sign-off." \
  --actor ci-bot \
  --policy '{"orchestrator_id":"ci","orchestrator_version":"1.0.0","agent_family":"ci-bot","approval_mode":"full_auto","sandbox_mode":"workspace_write","network_mode":"none","dangerous_flags":[]}'
Flag Required Meaning
--mission yes Mission slug.
--decision-id yes Decision ledger entry ID (ULID).
--final-answer yes The chosen answer (non-empty).
--other-answer no Set when the answer is a write-in, not one of the offered options.
--rationale no Explanation of the choice.
--resolved-by no Identity of the resolving party (falls back to --actor).
--actor yes Actor identity.
--policy yes Policy metadata JSON.

Response data: mission identity fields (mission_slug / mission_number / mission_type — no mission_id, unlike the Canonical Mission Identity payload shape documented above), decision_id, status ("resolved"), terminal_outcome, idempotent, event_lamport.

On an idempotent retry, event_lamport is the lamport of the terminal event the retry re-wrote when it was missing (contract >= 1.17.0), and null when the event was already present.

Error codes: POLICY_METADATA_REQUIRED, POLICY_VALIDATION_FAILED, MISSION_NOT_FOUND, DECISION_NOT_FOUND (--decision-id not present in the ledger), DECISION_TERMINAL_CONFLICT (the decision is already terminal with a different outcome/payload than requested — never pre-checked locally, always the service layer's own verdict, matching the host-CLI decision_app resolve subcommand's own error code), DECISION_EVENT_REPAIR_FAILED (an idempotent retry could not re-write the missing terminal event; contract >= 1.17.0), DESIGN_STATUS_EVENT_LOG_UNREADABLE (an idempotent retry found the event log unreadable, or decisions/index.json is unreadable).

defer-decision

Defers a decision for later resolution.

Request:

spec-kitty orchestrator-api defer-decision \
  --mission 090-orchestrator-driven-mission \
  --decision-id 01HXYZDECISIONULID \
  --rationale "Awaiting stakeholder input; revisit after design review." \
  --actor ci-bot \
  --policy '{"orchestrator_id":"ci","orchestrator_version":"1.0.0","agent_family":"ci-bot","approval_mode":"full_auto","sandbox_mode":"workspace_write","network_mode":"none","dangerous_flags":[]}'
Flag Required Meaning
--mission yes Mission slug.
--decision-id yes Decision ledger entry ID (ULID).
--rationale yes Explanation of why (must be non-empty).
--resolved-by no Identity of the deferring party (falls back to --actor).
--actor yes Actor identity.
--policy yes Policy metadata JSON.

Response data: mission identity fields (mission_slug / mission_number / mission_type — no mission_id, unlike the Canonical Mission Identity payload shape documented above), decision_id, status ("deferred"), terminal_outcome, idempotent, event_lamport.

On an idempotent retry, event_lamport is the lamport of the terminal event the retry re-wrote when it was missing (contract >= 1.17.0), and null when the event was already present.

Error codes: POLICY_METADATA_REQUIRED, POLICY_VALIDATION_FAILED, MISSION_NOT_FOUND, DECISION_MISSING_STEP_OR_SLOT (empty/whitespace-only --rationale, rejected before the service layer is ever called — reuses this code rather than minting a new one, matching the host CLI's own cmd_defer guard verbatim), DECISION_NOT_FOUND, DECISION_TERMINAL_CONFLICT, DECISION_EVENT_REPAIR_FAILED (an idempotent retry could not re-write the missing terminal event; contract >= 1.17.0), DESIGN_STATUS_EVENT_LOG_UNREADABLE (an idempotent retry found the event log unreadable, or decisions/index.json is unreadable).

cancel-decision

Cancels a decision deemed no longer relevant. Same request/response/error shape as defer-decision above (status is "canceled" instead of "deferred"; --rationale is likewise required and non-empty).

Request:

spec-kitty orchestrator-api cancel-decision \
  --mission 090-orchestrator-driven-mission \
  --decision-id 01HXYZDECISIONULID \
  --rationale "Superseded by a later decision; no longer applicable." \
  --actor ci-bot \
  --policy '{"orchestrator_id":"ci","orchestrator_version":"1.0.0","agent_family":"ci-bot","approval_mode":"full_auto","sandbox_mode":"workspace_write","network_mode":"none","dangerous_flags":[]}'

Error codes: POLICY_METADATA_REQUIRED, POLICY_VALIDATION_FAILED, MISSION_NOT_FOUND, DECISION_MISSING_STEP_OR_SLOT, DECISION_NOT_FOUND, DECISION_TERMINAL_CONFLICT, DECISION_EVENT_REPAIR_FAILED (an idempotent retry could not re-write the missing terminal event; contract >= 1.17.0), DESIGN_STATUS_EVENT_LOG_UNREADABLE (an idempotent retry found the event log unreadable, or decisions/index.json is unreadable).

design-status

Read-only design-phase status query — no --policy required, no state transition, no event emission (mirrors list-ready's contract for the design pipeline instead of the WP loop).

Fails closed rather than reporting a wrong phase. If status.events.jsonl cannot be read cleanly — a torn/truncated line, or a structural drift between the persisted status.json and a fresh event-log reduction — this verb returns DESIGN_STATUS_EVENT_LOG_UNREADABLE instead of a plausible-but-wrong current_phase/next_action. A caller can trust any current_phase this verb actually returns; it must handle the explicit unreadable case rather than treating a non-success response as "phase unknown, assume the beginning."

Request:

spec-kitty orchestrator-api design-status --mission 090-orchestrator-driven-mission

Response data: mission identity fields (mission_slug / mission_number / mission_type — no mission_id, unlike the Canonical Mission Identity payload shape documented above), current_phase (specify | plan | tasks | analyze), next_action (the verb the host should call next, or null once the design phase is complete; overridden to resolve-decision whenever any decision is open, regardless of phase), open_decisions (list of {decision_id, origin} for every open ledger entry, from any phase).

Error codes: MISSION_NOT_FOUND, DESIGN_STATUS_EVENT_LOG_UNREADABLE.

answer-decision

Resolves a spec-kitty next control-loop decision_required moment (a blocking audit checkpoint, or a missing required input) for a DAG step in any mission phase.

This is a composite verb, not a simple single-purpose call. One invocation performs, in order: (1) persists the answer against the resolved decision_id; (2) pairs the previous issuance's lifecycle record before the DAG advances; (3) advances the DAG (the engine call that produces the next Decision, using this call's own --result); (4) emits the mission's MissionNextInvoked event-log entry; (5) writes a new issuance lifecycle record whenever the resulting decision's kind is "step". Steps 2/4/5 are full host-CLI lifecycle parity — answer-decision reaches them through the same extracted seam spec-kitty next --answer ... itself calls (operator ruling SPEC-FRESH2-001), so a host driving decisions exclusively through answer-decision produces the identical event log and lifecycle-record trail a host shelling spec-kitty next --answer would have produced. Treat this parity as a promise: an external host does not need to also call the host CLI to keep the mission's audit trail complete.

Request:

spec-kitty orchestrator-api answer-decision \
  --mission 090-orchestrator-driven-mission \
  --agent ci-bot \
  --answer "yes" \
  --result success \
  --policy '{"orchestrator_id":"ci","orchestrator_version":"1.0.0","agent_family":"ci-bot","approval_mode":"full_auto","sandbox_mode":"workspace_write","network_mode":"none","dangerous_flags":[]}'
Flag Required Meaning
--mission yes Mission slug.
--agent yes Agent/actor identity performing this call.
--answer yes The answer value to persist for the pending decision.
--result yes Outcome of the current issuance: success | failed | blocked.
--decision-id no Run-snapshot pending decision id to answer; auto-resolved when omitted and exactly one decision is pending.
--policy yes Policy metadata JSON.

Response data: the SAME shape spec-kitty next --answer ... --json returns — every Decision.to_dict() field (kind, agent, mission_slug, mission_number, mission_type, mission, mission_state, timestamp, action, wp_id, workspace_path, prompt_file, reason, guard_failures, progress, origin, run_id, step_id, decision_id, input_key, question, options, is_query, preview_step) — plus one sibling field, answered_decision_id (the decision_id this call persisted the answer against). Note Decision.to_dict() does NOT carry a mission_id field (unlike the Canonical Mission Identity payload shape documented above, its mission_identity_fields() helper here only normalizes mission_slug / mission_number / mission_type). data does not carry an answer echo key; the host CLI's terser answered key is similarly not used.

Since contract 1.16.0, data also carries a warnings key (a list of strings) only when the Step-1 decision-log safe_commit was refused — byte-identical to the host next --answer --json surface (#6222, follow-up to #3930). On a clean commit the key is absent and the payload is unchanged. A warnings entry means the answer event was written to decisions.events.jsonl but not committed to git, so the committed decision record may be stale; the answer itself is not lost locally.

Error codes: POLICY_METADATA_REQUIRED, POLICY_VALIDATION_FAILED, MISSION_NOT_FOUND, RESULT_REQUIRED (--result omitted), INVALID_RESULT (--result is not one of success/failed/blocked, rejected before any decision resolution or persistence), NO_PENDING_DECISION (no pending decision exists to answer), AMBIGUOUS_PENDING_DECISION (more than one pending decision and --decision-id was omitted — lists the candidate ids), DECISION_NOT_PENDING (an explicit --decision-id does not match any currently-pending entry).

Example Commands

spec-kitty orchestrator-api contract-version
spec-kitty orchestrator-api mission-state --mission 077-mission-terminology-cleanup
spec-kitty orchestrator-api list-ready --mission 077-mission-terminology-cleanup
spec-kitty orchestrator-api start-implementation \
  --mission 077-mission-terminology-cleanup \
  --wp WP12 \
  --actor codex \
  --policy '{"orchestrator_id":"local","orchestrator_version":"1.0.0","agent_family":"codex","approval_mode":"never","sandbox_mode":"danger-full-access","network_mode":"enabled","dangerous_flags":[]}'

Start implementation

spec-kitty orchestrator-api start-implementation \
  --mission 077-mission-terminology-cleanup \
  --wp WP12 \
  --actor spec-kitty-orchestrator \
  --policy '{"orchestrator_id":"spec-kitty-orchestrator","orchestrator_version":"0.1.0","agent_family":"claude","approval_mode":"full_auto","sandbox_mode":"workspace_write","network_mode":"none","dangerous_flags":[]}'

Important response fields:

Field Meaning
workspace_path Path where the provider should run the implementation agent.
prompt_path WP markdown prompt file to feed to the implementation agent.
to_lane Expected to be in_progress on a fresh start.
no_op true when the same actor already owns the compatible state.

Resolve an existing workspace (read-only, contract >= 1.2.0)

spec-kitty orchestrator-api resolve-workspace \
  --mission 077-mission-terminology-cleanup \
  --wp WP12

Returns the WP's lane workspace_path / prompt_path (plus lane_id / lane_branch / lane_base_ref when the WP is lane-assigned) for its existing lane — without allocating, creating, validating-clean, or transitioning. Use it to resume a WP already past start-implementation (for example, dispatching a reviewer to a WP parked in for_review after an interrupted run, where calling start-implementation would wrongly re-transition it). No --policy is required: the command mutates nothing. The composed path is not guaranteed to exist for a WP that was never started.

Mark implementation ready for review

spec-kitty orchestrator-api transition \
  --mission 077-mission-terminology-cleanup \
  --wp WP12 \
  --to for_review \
  --actor spec-kitty-orchestrator \
  --policy '{"orchestrator_id":"spec-kitty-orchestrator","orchestrator_version":"0.1.0","agent_family":"claude","approval_mode":"full_auto","sandbox_mode":"workspace_write","network_mode":"none","dangerous_flags":[]}' \
  --subtasks-complete \
  --implementation-evidence-present \
  --note "Implementation complete"

in_progress -> for_review requires evidence that the implementation handoff is ready. Providers may supply the explicit guard hints shown above or omit them and let the host derive the facts. --force is reserved for recovery.

Claim review

spec-kitty orchestrator-api start-review \
  --mission 077-mission-terminology-cleanup \
  --wp WP12 \
  --actor spec-kitty-orchestrator \
  --policy '{"orchestrator_id":"spec-kitty-orchestrator","orchestrator_version":"0.1.0","agent_family":"codex","approval_mode":"full_auto","sandbox_mode":"workspace_write","network_mode":"none","dangerous_flags":[]}' \
  --review-ref review/WP12/attempt-1

On current hosts this moves for_review -> in_review.

Complete approved review

spec-kitty orchestrator-api transition \
  --mission 077-mission-terminology-cleanup \
  --wp WP12 \
  --to done \
  --actor spec-kitty-orchestrator \
  --policy '{"orchestrator_id":"spec-kitty-orchestrator","orchestrator_version":"0.1.0","agent_family":"codex","approval_mode":"full_auto","sandbox_mode":"workspace_write","network_mode":"none","dangerous_flags":[]}' \
  --review-ref review/WP12/attempt-1 \
  --review-result-json '{"reviewer":"codex","verdict":"approved","reference":"review/WP12/attempt-1"}' \
  --evidence-json '{"review":{"reviewer":"codex","verdict":"approved","reference":"review/WP12/attempt-1"}}' \
  --note "Codex review approved"

The structured review result satisfies the in_review exit guard; done evidence keeps the terminal event independently auditable. --force remains recovery-only.

Send rejected review back to rework

spec-kitty orchestrator-api transition \
  --mission 077-mission-terminology-cleanup \
  --wp WP12 \
  --to in_progress \
  --actor spec-kitty-orchestrator \
  --policy '{"orchestrator_id":"spec-kitty-orchestrator","orchestrator_version":"0.1.0","agent_family":"codex","approval_mode":"full_auto","sandbox_mode":"workspace_write","network_mode":"none","dangerous_flags":[]}' \
  --review-ref review/WP12/attempt-1 \
  --review-result-json '{"reviewer":"codex","verdict":"changes_requested","reference":"review/WP12/attempt-1"}' \
  --note "Review rejected; rework required"

Then rerun the implementation agent with the review feedback and transition back to for_review.

Worktree Expectations

The host returns the workspace path. The provider is responsible for ensuring the path exists and is usable for the agent process before spawning an agent. Do not treat the returned string as proof that a worktree already exists.

State mutation commands should not be run from a protected main branch when they need to commit activity-log updates. Use a mission lane/worktree branch for provider-owned mutation calls.

Error Codes

Current machine-readable error codes (the authoritative list is allowed_error_codes in src/specify_cli/core/upstream_contract.json):

  • USAGE_ERROR
  • POLICY_METADATA_REQUIRED
  • POLICY_VALIDATION_FAILED
  • MISSION_NOT_FOUND
  • STATUS_READ_PATH_NOT_FOUND
  • WP_NOT_FOUND
  • TRANSITION_REJECTED
  • WP_ALREADY_CLAIMED (for start-implementation on an in_progress WP after a reviewer's rework verdict, the WP's implementer of record is admitted as a no_op resume rather than refused; an unrelated actor is still refused)
  • MISSION_NOT_READY (on accept-mission, data.preflight_error_code can refine it with an origin freshness code; see below)
  • WORKFLOW_EVIDENCE_REQUIRED
  • PREFLIGHT_FAILED (on consolidate-mission, data.preflight_error_code or data.teardown_error_code can refine it; see below)
  • CONTRACT_VERSION_MISMATCH
  • UNSUPPORTED_STRATEGY
  • HISTORY_COMMIT_FAILED
  • DEPENDENCIES_NOT_SATISFIED
  • LANE_ALLOCATION_FAILED
  • ANCESTRY_NOT_ESTABLISHED
  • STATUS_LOCK_HELD (start-implementation: the single_branch write-checkout claim lock stayed held past its bound; retry)
  • SAFE_COMMIT_BACKSTOP
  • SAFE_COMMIT_DESTINATION_NOT_FOUND
  • SAFE_COMMIT_DESTINATION_REF_SHAPE
  • SAFE_COMMIT_EMPTY_CHANGESET
  • SAFE_COMMIT_GENERIC
  • SAFE_COMMIT_HEAD_MISMATCH
  • SAFE_COMMIT_NOT_A_WORKTREE
  • SAFE_COMMIT_PROTECTED_BRANCH
  • SAFE_COMMIT_PATH_POLICY
  • SAFE_COMMIT_PATH_LOOP
  • SAFE_COMMIT_INDEX_DELETION_CONFLICT
  • SAFE_COMMIT_INDEX_RESIDUE (the commit DID land; commit_sha names it; a SAFE_COMMIT_RECOVERY_FAILED refinement)
  • SAFE_COMMIT_UNREQUESTED_PATHS (a pre-commit hook swept unrequested tracked paths into the commit, which DID land; commit_sha names it; the real index is re-synced for those paths; a SAFE_COMMIT_RECOVERY_FAILED refinement, contract 1.15.0)
  • SAFE_COMMIT_RECOVERY_FAILED

Added in contract 1.4.0 (#3837), for the 11 design-phase verbs above:

  • MISSION_ALREADY_EXISTS — specify
  • MISSION_CREATE_FAILED — specify
  • PLAN_SETUP_FAILED — plan
  • TASKS_FINALIZE_FAILED — tasks
  • PLACEMENT_RESOLUTION_REQUIRED — record-analysis
  • CHECK_PREREQUISITES_FAILED — check-prerequisites
  • RECORD_ANALYSIS_EMPTY_BODY — record-analysis
  • RECORD_ANALYSIS_INPUT_FILE_NOT_FOUND — record-analysis
  • RECORD_ANALYSIS_MALFORMED_CARRIER — record-analysis
  • RECORD_ANALYSIS_WRITE_NOT_CONFIRMED — record-analysis
  • RECORD_ANALYSIS_VERDICT_UNRELIABLE — record-analysis
  • DIRTY_WORKTREE — record-analysis
  • INVALID_ORIGIN_FLOW — open-decision
  • DECISION_MISSING_STEP_OR_SLOT — open-decision, defer-decision, cancel-decision
  • DECISION_ALREADY_CLOSED — open-decision
  • DECISION_NOT_FOUND — resolve-decision, defer-decision, cancel-decision
  • DECISION_TERMINAL_CONFLICT — resolve-decision, defer-decision, cancel-decision
  • DECISION_EVENT_REPAIR_FAILED — open-decision, resolve-decision, defer-decision, cancel-decision
  • DESIGN_STATUS_EVENT_LOG_UNREADABLE — design-status, open-decision, resolve-decision, defer-decision, cancel-decision
  • RESULT_REQUIRED — answer-decision
  • INVALID_RESULT — answer-decision
  • NO_PENDING_DECISION — answer-decision
  • AMBIGUOUS_PENDING_DECISION — answer-decision
  • DECISION_NOT_PENDING — answer-decision
  • DECISION_OPERATION_FAILED — open-decision, resolve-decision, defer-decision, cancel-decision (fallback when a decision-ledger operation fails for a reason without a more specific registered code)

consolidate-mission: data.preflight_error_code

consolidate-mission merges the code lanes directly, with no reconciliation claim behind it, so it checks every approved lane against the commit review approved before it merges the first one. A lane that fails keeps the PREFLIGHT_FAILED envelope code, puts the message in data.errors, and carries a stable code in data.preflight_error_code. No branch was moved.

data.preflight_error_code Meaning What to do
LANE_MOVED_AFTER_APPROVAL The lane holds a content commit made after review approved it. Move the work package back for review, approve it again, re-run.
APPROVAL_STAMP_MISSING An approved work package records no lane head, so what review approved cannot be determined. Move it back for review and approve it again, or attest the approval with spec-kitty consolidate --attest-approved-reviewed (the host command only).
APPROVAL_STAMP_NOT_ON_LANE The approval names a commit that is not on the lane: the lane was rewritten after review. Move the work package back for review, approve it again, re-run.

data.preflight_error_code is the first code the message names. data.preflight_error_codes is a list of every distinct code the message names, in the order they appear, so a mission whose lanes refuse for different reasons reports all of them. Both keys are absent on every other failure. The host command spec-kitty consolidate reports the same three codes in its message and exits 1. When a mixed-lane refusal also applies, it prints that refusal first and these codes after This Mission also has:; consolidate-mission never raises the mixed-lane refusal, so its list holds only the codes above.

accept-mission and consolidate-mission: origin freshness

Before either command reads evidence or moves a branch, it asks the remote of each branch it is about to trust (one contact per remote, never prompts). The listing is bounded at 5 seconds and the fetch that follows, skipped when the listing shows nothing new, at 15 seconds more, so a check takes at most 20 seconds. With no remote, or a branch that was never pushed, nothing is contacted and nothing changes. A refusal keeps the envelope code the command already used, MISSION_NOT_READY for accept-mission and PREFLIGHT_FAILED for consolidate-mission, puts the message in data.errors, and adds these keys. No branch was moved.

data.preflight_error_code Meaning What to do
ORIGIN_STATUS_STALE The remote's status evidence branch has commits that change this Mission's status.events.jsonl and that your clone lacks (or the branch is diverged). Run the git pull command the message names in the evidence checkout, then re-run. With a merge record present, abort first, as the message says.
ORIGIN_LANE_STALE (state behind) consolidate-mission only: an approved lane's remote tip is ahead of the local lane, which has nothing origin lacks. git fetch <remote> <lane>, then git -C <lane worktree> merge --ff-only <remote>/<lane> (or git branch -f <lane> <remote>/<lane> when no checkout holds the lane), then re-run.
ORIGIN_LANE_STALE (state local_missing) The approved lane exists on the remote but not locally. git branch <lane> <remote>/<lane>, then re-run.
ORIGIN_LANE_STALE (state diverged) The lane has commits origin lacks and origin has commits the lane lacks. Merge origin's lane in (git -C <lane worktree> merge <remote>/<lane>), or, if the local lane is the truth, merge or rebase it onto <remote>/<lane> first, then push. Never git branch -f: it drops the local commits. Then re-run.
ORIGIN_UNREACHABLE A remote resolved but could not be reached in this invocation: it timed out (5 s listing, 15 s fetch) or the transport failed. A timeout may only be slowness: retry. Otherwise restore connectivity, or re-run with --origin-check warn (or set SPEC_KITTY_ORIGIN_CHECK=warn).
ORIGIN_REMOTE_AMBIGUOUS The repository has remotes but none owns the branch: no branch.<name>.remote, not exactly one remote, and no origin. data.origin_freshness[].detail lists the remotes. Run git config branch.<name>.remote <remote> for the branch, then re-run, or re-run with --origin-check warn.
ORIGIN_COMPARE_FAILED The remote answered, but git could not compare the local branch with its fetched tip (a shallow-clone boundary or a damaged ref). data.origin_freshness[].detail carries the git error text. Run git fetch --unshallow <remote> (or repair the ref), then re-run, or re-run with --origin-check warn.

data.preflight_error_code is the first code the message names and data.preflight_error_codes lists every distinct code, as for the codes above.

data.origin_freshness is a list with one row per branch that failed the check:

{
  "branch": "kitty/mission-my-mission-01ABCDEF",
  "remote": "origin",
  "state": "behind",
  "behind": 2,
  "ahead": 0,
  "scope": "kitty-specs/my-mission/status.events.jsonl",
  "detail": null
}

state is one of up_to_date, behind, ahead, diverged, local_missing, remote_missing, unreachable, no_remote, remote_ambiguous or compare_failed. A remote_missing row is never a refusal; its detail is set when this clone once saw the branch on the remote and the remote no longer lists it (the branch may have been deleted there), and the same text is a warning. scope names the status log the status evidence row was judged on and is null for a lane. detail says why a remote was unreachable or could not be compared (the git error text), lists the remotes of a remote_ambiguous branch, or notes that a no_remote clone still holds remote-tracking refs (its remote was removed; a warning, never a refusal), and is null otherwise. A consolidate-mission origin refusal also carries data.target_branch, like every other PREFLIGHT_FAILED of that command. With --origin-check warn, or SPEC_KITTY_ORIGIN_CHECK=warn, the command does not refuse: it continues, writes each warning to stderr, and a successful envelope carries them in data.origin_warnings (a list of strings). A coordination Mission whose coordination branch exists only on the remote is not judged here; the existing COORDINATION_WORKTREE_UNMATERIALIZED refusal applies.

--origin-check also accepts off (and so does SPEC_KITTY_ORIGIN_CHECK=off). The command then contacts no remote, never refuses on freshness, and carries one warning in data.origin_warnings and on stderr saying that origin was not checked and stale evidence is accepted. Use it only where you cannot reach the remote and accept that a teammate's newer status events or lane commits go unseen: a rejection pushed to origin can then land as done.

consolidate-mission: data.teardown_error_code

A consolidate-mission failure keeps the PREFLIGHT_FAILED envelope code and puts the message in data.errors. When the landing succeeded and only the cleanup after it refused, data also carries teardown_error_code, a stable code to key on instead of the prose. A PREFLIGHT_FAILED without a teardown_error_code is raised before any lane/target mutation; a refusal leaves refs untouched.

data.teardown_error_code Meaning What to do
COORD_MOVED_AFTER_LANDING The mission branch moved after the landing was verified. The branch was kept, the late commit is intact and the landed target stands. Review the commit(s) with the git log command in the message, land any that belong on the target, then delete the branch.

The key is absent on every other failure. The message in data.errors ends with Error code: COORD_MOVED_AFTER_LANDING. The host command spec-kitty consolidate reports the same condition with exit code 75.

consolidate-mission does not run the host command's reconciliation gate, so the gate's verdict codes (APPROVED_CONTENT_MISSING, CANCELED_REACHABLE_VIA_DEPENDENCY and the others in the status model) are never reported here, and --resume and --attest-canceled-superseded exist only on spec-kitty consolidate. The full list of that command's exit codes and codes is in the CLI reference.

Provider Rules

  • Call contract-version once before mutating state.
  • Use only orchestrator-api for lane changes.
  • Keep retry decisions based on error_code, not prose.
  • Preserve review_ref values in logs and issue/PR links.
  • Treat mission-state as authoritative after every recovery.
  • Keep agent stdout/stderr in provider logs; do not stuff full logs into WP history entries.

Migration Notes

  • The human-facing CLI still supports hidden deprecated aliases during the migration window.
  • The orchestrator API does not. It is canonical-only on --mission and mission_* payload fields.

See also:

Governed planning delivery (Python profile)

Contract 1.12.0 adds six verbs to Stijn's existing concern-module registry. It implements the Mission discovery, interview, authored specification/plan, task authoring and native lifecycle slice of spec-kitty.orchestrator/2. The transport remains the existing Python JSON envelope. It is a translation profile, with full_go_conformance: false; clients must not infer Go transport or transaction guarantees from the semantic family name. The corresponding Go contract amendment defines the lifecycle coverage.

contract-version now returns data.delivery_profile, including supported commands, translated operations, limits and unavailable capabilities. contract-version --require-capability artifact-submit probes a required capability without changing a Mission. Unknown or unavailable names return UNSUPPORTED_CAPABILITY. The provider-version handshake still applies.

Semantic operation Python command or authority
Discover Mission types and resolved planning context design-context
Create a Mission specify and native creation service
Record specification/planning interviews interview-record and native decision service
Read or submit governed planning content artifact-read, artifact-submit
Check design prerequisites design-validate and canonical validators
Issue/complete runtime actions next and native lifecycle authority
Finalize task graph and seed ready work tasks and native finalizer
Read work progress Existing mission-state, list-ready, design-status

Unavailable guarantees include GapDB atomic journals, runtime leases/fences, immutable Go WorkRevision generations, native asynchronous operations, watches, durable operation replay and remote serving. These verbs execute in the local host. Tracker publication remains a separate integration.

design-context

Read-only, with --stage specify|plan|tasks. Before creation, omit --mission and explicitly select an activated --mission-type. For an existing Mission, its recorded Mission type governs context; an incompatible override is refused.

spec-kitty orchestrator-api design-context --mission-type software-dev --stage specify
spec-kitty orchestrator-api design-context --mission "$MISSION" --stage plan

Data contains mission_slug (null before creation), mission_type, activated mission_types, resolved templates, canonical questions, interview status, action-scoped governance, context_sha256 and published limits. Template keys retain the resolved template-set vocabulary (spec and plan for software-dev); artifact submission uses the separate closed kind vocabulary below. Each template includes bounded UTF-8 content and a SHA-256 revision. The context digest covers semantic template/governance inputs rather than session metadata or local absolute paths. Reading it does not mark doctrine as loaded or complete a workflow phase.

interview-record

Mutation requires --mission, --stage specify|plan, --answers (a JSON mapping of canonical question IDs to nonempty answers), --actor and --policy. Discover the IDs with design-context; submit answers incrementally. The native decision ledger owns their identities and terminal states.

spec-kitty orchestrator-api interview-record --mission "$MISSION" --stage specify \
  --answers '{"problem_statement":"Describe the actual problem"}' --actor client --policy "$POLICY"

Only resolved nonempty answers satisfy a required slot. Pending, deferred and canceled decisions remain incomplete. Retrying an identical resolved answer is idempotent; conflicting terminal answers refuse. data.interview contains complete, incomplete, answers, questions and decision_ids. If a native batch fails after earlier answers were applied, the failure reports those decision IDs and reconciliation_required; callers must reconcile that outcome before retrying. It does not claim transaction rollback.

artifact-read

Read-only, with --mission, --kind and optional --artifact-id. Closed kinds:

Kind Canonical content
specification spec.md
plan plan.md
research research.md
data_model data-model.md
quickstart quickstart.md
contract Registered contract basename under contracts/
outline wps.yaml
work_package Prompt for a package declared in the outline

Contracts and work packages require a safe --artifact-id. Raw caller paths and undeclared packages are refused. Data includes kind, artifact_id, content and exact-byte sha256. Missing content is explicit: null content, revision absent. Reads use host-resolved artifact placement.

artifact-submit

Mutation requires --mission, --request-json, --actor and --policy. The request is a closed object: unknown fields refuse. Use --request-json - to send bounded UTF-8 JSON on stdin; external CLI clients should use this form so content size does not depend on OS argument limits. Literal JSON remains supported for small requests.

{
  "artifacts": [{
    "kind": "plan",
    "content": "Authored substantive plan text",
    "expected_sha256": "absent"
  }],
  "parents": {"specification": "<current 64-character SHA-256>"},
  "context_sha256": "<current design-context digest for plan>"
}

Use artifact-read for each expected target revision and required parent, and design-context for the stage digest. A plan pins its specification; an outline pins specification and plan; a package also pins its outline. Supporting plan artifacts use plan context. Early research uses specification context and can precede the first specification without a specification parent. Resolved required interview slots and substantive committed parent content are checked before writes. The complete bounded batch is validated before effects.

Host-owned accepted lineage is checked again at validation/finalization: passing a new current parent digest cannot make an already stale plan or prompt current. Once the native task board is finalized, API design edits and plan scaffolding refuse with DESIGN_FINALIZED. This surface does not reopen work.

Data contains accepted artifact identities/revisions and the actual host commit outcome, without echoing content. A cooperative host lock serializes API writers. It does not lock every existing CLI/manual writer. Files and Git commit effects precede receipt persistence; partial failures report materialized/committed effects or reconciliation_required. Inspect that outcome before retrying. Receipts live in the Git common directory, are shared between linked worktrees, and are not portable between clones. Missing/corrupt receipts while the separate persistent API-enabled marker remains present fail closed. The marker and receipts are trusted host metadata; deleting both or using another clone does not preserve the host's API provenance history.

Bounds: 256 KiB per artifact, 1 MiB per batch, at most 64 artifacts. Context responses are at most 1 MiB; interview answers at most 16 KiB each and interview actor identity at most 1 KiB. Artifact submission actor identity is at most 256 bytes. Artifact request JSON is at most 2 MiB (including JSON escaping and structure). Limits count UTF-8 bytes.

design-validate

Read-only, with --mission and --stage specify|plan|tasks. Checks substantive, committed, current content, required resolved decisions and canonical stage requirements. Task checks include the declared graph, package prompts and requirement references. It does not mark a runtime action complete or finalize packages. tasks remains the finalization command and native next remains the completion authority. Legacy manual planning without API receipts retains its existing workflow.

next

next --mission "$MISSION" queries the native runtime without starting a run or advancing an action, even when --agent is provided. Advancement requires --agent, --result success|failed|blocked and --policy. Optional --answer and --decision-id follow the native pending-decision rules.

spec-kitty orchestrator-api next --mission "$MISSION"
spec-kitty orchestrator-api next --mission "$MISSION" --agent client \
  --result success --policy "$POLICY"

The first advancement issues an action; later paired results complete the actually issued native action. For API-authored content, successful completion of specify/plan/tasks revalidates that stage under the authoring lock. An initial preview is not an issued action. Native lifecycle pairing, charter preflight, run index and decisions remain authoritative.

Typed native query/step/decision-required/terminal data is preserved. Issued steps include bounded prompt: {content, sha256, bytes}; host absolute prompt_file is not exposed. Native errors return RUNTIME_NEXT_FAILED; blocked outcomes return RUNTIME_BLOCKED, both with nonzero exit. A decision-required state is a successful typed response requiring client action.

API-only planning sequence

Discover context → specify → record specification interview → submit and validate specification → plan scaffold → record plan interview → submit and validate substantive plan → submit outline → submit declared prompts → validate tasks → tasks finalize → list-ready. Supporting artifacts may be submitted through the same governed seam. Clients that drive native actions also query and submit paired results through next; scaffolding alone never completes a design phase.

Status API and Go progress mapping

Go already has a ProgressService with bounded Mission/list/get/watch projections and explicit authority, observation and freshness distinctions. Jeroen's Mission UI consumes a pinned display contract; its separate Java REST/SSE service is unfinished. Future orchestrator/UI readers should share the planned native status port (#5532, topology integration #5631). This PR preserves current native readers and adds no Java dependency or competing status reducer. Display artifact presence and provisional English nextAction must not become orchestrator completion authority. Go projection-revision cursors and UI JSONL offset/hash cursors require explicit versioned translation. See the application seam ADR.