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--featureflag has been removed from all user-facing commands - expect one JSON envelope on stdout for both success and failure
- treat
error_codeas the stable machine discriminator - do not append
--json; JSON is the default output for this command group
Canonical Terms
Mission Type= reusable blueprint keyMission= tracked item underkitty-specs/<mission-slug>/Mission Run= runtime/session instance
Contract Version
CONTRACT_VERSION:1.18.0MIN_PROVIDER_VERSION:0.1.0- Startup probe:
spec-kitty orchestrator-api contract-version - A
--provider-versionbelowMIN_PROVIDER_VERSION, or one that does not parse as a version string, always returns exit code 1 witherror_code: CONTRACT_VERSION_MISMATCH— never a silent default. Callcontract-versionwith--provider-versionat 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-implementationallocates/reuses the lane worktree; its response carrieslane_id/lane_branch/lane_base_ref, andworkspace_pathmeans that lane worktree. New error codeLANE_ALLOCATION_FAILED.1.2.0— new read-onlyresolve-workspacecommand (#2337). Purely additive.1.3.0—transitionaccepts structured--review-result-json, allowing guardedin_reviewexits 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-onlydesign-statusquery 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— thetasksverb's pass-throughdatagained theplanning_commitobject (action/sha/previous_sha/branch_tip) from the delegate finalize-tasks--jsonpayload (#4141, the--refresh-planning-commitre-point affordance). Purely additive.1.6.0— theplanning_commit.actionvocabulary gains"repinned"(#4827, the--refresh-planning-commit --allow-orphanedre-point affordance). Purely additive.1.7.0—accept-missionnow applies the host readiness verdict (#4934): it callscollect_feature_summary(..., strict_metadata=True)and refuses withMISSION_NOT_READYwheneversummary.okisFalse, carryingoutstanding/activity_issues/skipped_checks/blocked_checksplus the mission identity fields — see Acceptance Payload below. Acceptance is now recorded only through the same FR-010 locked pre-stamp verdict guard the hostacceptCLI 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— thetasksverb's pass-throughdatagains three keys (parsed_spec_ids,rejected_requirement_refs,success_criteria_coverage) from the delegate finalize-tasks--jsonpayload (requirement-id-grammar-01M3NRCA WP02). Theplanverb's pass-throughdatagainsrequirement_id_warnings, and its previously unregistered delegate error codes now remap onto the closedPLAN_SETUP_FAILEDenvelope with the real code preserved asdata.reason(WP05) — this closes a latent leak:SPEC_FILE_MISSING,TEMPLATE_CONFIGURATION_ERRORandPLAN_CONTEXT_UNRESOLVEDpreviously passed through verbatim aserror_code(none was contract-registered) and now surface asPLAN_SETUP_FAILEDtoo. Additive keys plus a closed-envelope remap onplan's previously-leaking codes; minor bump.1.9.0— theconsolidate-missionfailure envelope'sdatagainsteardown_error_codewhen the landing succeeded and only the cleanup after it refused (COORD_MOVED_AFTER_LANDING, #5613; seedata.teardown_error_code). The envelopeerror_codestaysPREFLIGHT_FAILEDanddata.errorsis unchanged. Purely additive: the key is absent on every other failure.1.10.0—consolidate-missioncan 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 envelopeerror_codestaysPREFLIGHT_FAILEDanddatagainspreflight_error_code(seedata.preflight_error_code). A call that used to succeed can now refuse, so a minor bump; the key is absent on every other failure.dataalso carriespreflight_error_codes, every distinct code the refusal names, the first equal topreflight_error_code(#5720).1.11.0—accept-missionandconsolidate-missiontake--origin-check [enforce|warn]and can newly refuse before they read evidence or move a lane, when the Mission's status evidence (and, forconsolidate-mission, its approved lane branches) is behind, diverged from or unreachable on its remote (#5780). The envelopeerror_codestaysMISSION_NOT_READY/PREFLIGHT_FAILED;datagainspreflight_error_code,preflight_error_codesandorigin_freshness(see Origin freshness), and a successfulwarnrun gainsorigin_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 nativenext, 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. Nativespec-kitty next --result successandagent mission finalize-tasksrun the same check (#5879) and refuse with the sameDESIGN_*code;agent tasks finalize-tasksdoes not. Newerror_codevalues: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_BLOCKEDandUNSUPPORTED_CAPABILITY. Behaviour change:plannow fails when its delegate payload result isblockedorerrorinstead of emitting success. See Governed planning delivery.1.13.0—start-implementationcan newly fail withSTATUS_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 failuredatacarries the Mission identity,wp_id,messageandlock_timeout_seconds. A newerror_codeon 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 withSAFE_COMMIT_PATH_LOOP(a requested path is a symlink loop),SAFE_COMMIT_INDEX_DELETION_CONFLICT(an index deletion conflicts with the commit) andSAFE_COMMIT_INDEX_RESIDUE(#5443).SAFE_COMMIT_INDEX_RESIDUEmeans the commit DID land (commit_shanames it) but the real index still stages a committed path; it refinesSAFE_COMMIT_RECOVERY_FAILED, which keeps meaning the commit may not have landed. Newerror_codevalues 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 withSAFE_COMMIT_UNREQUESTED_PATHS(#5903) when a pre-commit hook that runsgit add -usweeps the operator's other modified tracked files into the commit: the commit DID land (commit_shanames 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. LikeSAFE_COMMIT_INDEX_RESIDUEit refinesSAFE_COMMIT_RECOVERY_FAILED(the commit landed). A newerror_codeon an existing verb, so a minor bump; no field is removed or renamed.1.16.0—answer-decisionnow surfaces decision-log commit warnings in itsdataunder awarningskey (list of strings) when the decision record'ssafe_commitis refused (#6222, follow-up to #3930, which fixed the hostnext --answer --jsonsurface only). Previously an external orchestrator gotsuccess: truewhile 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-missionnow skips a fully-canceled-with-provenance lane at parity with hostconsolidate(#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 answersuccess: truewhile shipping that canceled content now still answerssuccess: truebut leaves the canceled lane off the target (the approved survivor lanes land unchanged). A behaviour change to the shipped tree of a previously-successcall, so a minor bump; no envelope field orerror_codeis added, removed or renamed.1.17.0—resolve-decision,defer-decisionandcancel-decisioncan newly fail withDECISION_EVENT_REPAIR_FAILED(#5681), andDESIGN_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 missingDecisionPointResolvedevent and reports itsevent_lamport(previously always null on an idempotent call). A newly reachableerror_codeon existing verbs and a non-nullevent_lamporton 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_idorchestrator_versionagent_familyapproval_modesandbox_modenetwork_modedangerous_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_ERRORPOLICY_METADATA_REQUIREDPOLICY_VALIDATION_FAILEDMISSION_NOT_FOUNDSTATUS_READ_PATH_NOT_FOUNDWP_NOT_FOUNDTRANSITION_REJECTEDWP_ALREADY_CLAIMED(forstart-implementationon anin_progressWP after a reviewer's rework verdict, the WP's implementer of record is admitted as ano_opresume rather than refused; an unrelated actor is still refused)MISSION_NOT_READY(onaccept-mission,data.preflight_error_codecan refine it with an origin freshness code; see below)WORKFLOW_EVIDENCE_REQUIREDPREFLIGHT_FAILED(onconsolidate-mission,data.preflight_error_codeordata.teardown_error_codecan refine it; see below)CONTRACT_VERSION_MISMATCHUNSUPPORTED_STRATEGYHISTORY_COMMIT_FAILEDDEPENDENCIES_NOT_SATISFIEDLANE_ALLOCATION_FAILEDANCESTRY_NOT_ESTABLISHEDSTATUS_LOCK_HELD(start-implementation: the single_branch write-checkout claim lock stayed held past its bound; retry)SAFE_COMMIT_BACKSTOPSAFE_COMMIT_DESTINATION_NOT_FOUNDSAFE_COMMIT_DESTINATION_REF_SHAPESAFE_COMMIT_EMPTY_CHANGESETSAFE_COMMIT_GENERICSAFE_COMMIT_HEAD_MISMATCHSAFE_COMMIT_NOT_A_WORKTREESAFE_COMMIT_PROTECTED_BRANCHSAFE_COMMIT_PATH_POLICYSAFE_COMMIT_PATH_LOOPSAFE_COMMIT_INDEX_DELETION_CONFLICTSAFE_COMMIT_INDEX_RESIDUE(the commit DID land;commit_shanames it; aSAFE_COMMIT_RECOVERY_FAILEDrefinement)SAFE_COMMIT_UNREQUESTED_PATHS(a pre-commit hook swept unrequested tracked paths into the commit, which DID land;commit_shanames it; the real index is re-synced for those paths; aSAFE_COMMIT_RECOVERY_FAILEDrefinement, contract1.15.0)SAFE_COMMIT_RECOVERY_FAILED
Added in contract 1.4.0 (#3837), for the 11 design-phase verbs above:
MISSION_ALREADY_EXISTS—specifyMISSION_CREATE_FAILED—specifyPLAN_SETUP_FAILED—planTASKS_FINALIZE_FAILED—tasksPLACEMENT_RESOLUTION_REQUIRED—record-analysisCHECK_PREREQUISITES_FAILED—check-prerequisitesRECORD_ANALYSIS_EMPTY_BODY—record-analysisRECORD_ANALYSIS_INPUT_FILE_NOT_FOUND—record-analysisRECORD_ANALYSIS_MALFORMED_CARRIER—record-analysisRECORD_ANALYSIS_WRITE_NOT_CONFIRMED—record-analysisRECORD_ANALYSIS_VERDICT_UNRELIABLE—record-analysisDIRTY_WORKTREE—record-analysisINVALID_ORIGIN_FLOW—open-decisionDECISION_MISSING_STEP_OR_SLOT—open-decision,defer-decision,cancel-decisionDECISION_ALREADY_CLOSED—open-decisionDECISION_NOT_FOUND—resolve-decision,defer-decision,cancel-decisionDECISION_TERMINAL_CONFLICT—resolve-decision,defer-decision,cancel-decisionDECISION_EVENT_REPAIR_FAILED—open-decision,resolve-decision,defer-decision,cancel-decisionDESIGN_STATUS_EVENT_LOG_UNREADABLE—design-status,open-decision,resolve-decision,defer-decision,cancel-decisionRESULT_REQUIRED—answer-decisionINVALID_RESULT—answer-decisionNO_PENDING_DECISION—answer-decisionAMBIGUOUS_PENDING_DECISION—answer-decisionDECISION_NOT_PENDING—answer-decisionDECISION_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-versiononce before mutating state. - Use only
orchestrator-apifor lane changes. - Keep retry decisions based on
error_code, not prose. - Preserve
review_refvalues in logs and issue/PR links. - Treat
mission-stateas 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
--missionandmission_*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.