Feature Specification: Orchestrator-API JSON Contract Fidelity

Feature Branch: 053-orchestrator-api-json-contract-fidelity Created: 2026-03-20 Status: Draft References: GitHub Issue #3, Issue #304

Problem Statement

The orchestrator-api claims to be a "JSON-first machine contract" but breaks that promise in two ways:

1. Stale --json flag in docs: The documentation (docs/reference/orchestrator-api.md) tells external callers to pass --json to contract-version, but the 2.x code defines no such flag. External orchestrators follow the documented contract and fail on first handshake.

2. Root CLI path emits prose, not JSON: The _JSONErrorGroup handler only intercepts errors when the sub-app is invoked directly. When invoked through the real entry path (spec-kitty orchestrator-api ...), the root CLI's BannerGroup handles errors first, emitting Typer's human-readable stderr with no stdout. This is why spec-kitty-orchestrator reports "returned no output."

3. Tests don't exercise the real path: Tests invoke the sub-app directly (runner.invoke(app, ...)), which hits _JSONErrorGroup and passes. They never test the actual spec-kitty orchestrator-api ... command path that real callers use.

User Scenarios & Testing

User Story 1 - External Orchestrator Handshake (Priority: P1)

An external orchestrator (e.g., spec-kitty-orchestrator) calls spec-kitty orchestrator-api contract-version to verify compatibility before proceeding. The orchestrator parses stdout as JSON. If the command produces anything other than a valid JSON envelope on stdout, the handshake fails.

Why this priority: This is the entry point for all orchestrator interactions. If the handshake fails, no orchestrator workflow can proceed.

Independent Test: Run spec-kitty orchestrator-api contract-version from a shell and verify stdout is a valid JSON envelope with success: true.

Acceptance Scenarios:

1. Given a clean install of spec-kitty 2.x, When an orchestrator runs spec-kitty orchestrator-api contract-version, Then stdout contains a valid JSON envelope with success: true and the contract version. 2. Given a clean install of spec-kitty 2.x, When an orchestrator runs spec-kitty orchestrator-api contract-version --unknown-flag, Then stdout contains a JSON envelope with success: false and error_code: "USAGE_ERROR" (not prose stderr).


User Story 2 - Parse/Usage Errors Return JSON (Priority: P1)

When any orchestrator-api subcommand is invoked with bad arguments through the root CLI path, the error must be a JSON envelope on stdout, not Typer's prose on stderr.

Why this priority: Without this, every error case breaks the machine contract. Callers cannot programmatically distinguish error types.

Independent Test: Run spec-kitty orchestrator-api contract-version --bogus and verify stdout is a JSON envelope with error_code: "USAGE_ERROR".

Acceptance Scenarios:

1. Given the root CLI entry path, When a user runs spec-kitty orchestrator-api nonexistent-command, Then stdout contains a JSON envelope with error_code: "USAGE_ERROR". 2. Given the root CLI entry path, When a user runs spec-kitty orchestrator-api move-task (missing required args), Then stdout contains a JSON envelope with error_code: "USAGE_ERROR" and a descriptive message.


User Story 3 - Documentation Matches Reality (Priority: P2)

A developer reading docs/reference/orchestrator-api.md can trust that the documented command signatures, flags, and error contracts match the actual CLI behavior.

Why this priority: Stale docs are the root cause of issue #304. Without accurate docs, external integrators build against a phantom API.

Independent Test: Compare every command signature in orchestrator-api.md against the actual CLI --help output and verify they match.

Acceptance Scenarios:

1. Given the orchestrator-api docs, When a developer reads the contract-version section, Then no --json flag is mentioned (the API is always-JSON by design). 2. Given the orchestrator-api docs, When a developer reads the error handling section, Then it states that all errors produce JSON envelopes on stdout with documented error_code values.


Edge Cases

  • What happens when spec-kitty orchestrator-api is invoked with no subcommand? Must produce JSON help or JSON error, not prose.
  • What happens when the root CLI itself fails before reaching the orchestrator-api sub-app (e.g., broken plugin)? Must not break JSON contract for orchestrator-api paths.
  • What happens when stderr is redirected but stdout is not? Callers parse stdout only; stderr content is irrelevant to the contract.

Requirements

Functional Requirements

IDTitleUser StoryPriorityStatus
FR-001JSON errors through root CLIAs an orchestrator, I want parse/usage errors to return JSON envelopes when I invoke commands through spec-kitty orchestrator-api ... so that I can programmatically handle all error cases.HighOpen
FR-002Remove --json flag referencesAs a developer, I want the docs and tests to not reference a --json flag so that I don't build against a nonexistent API surface.HighOpen
FR-003Tests exercise real entry pathAs a maintainer, I want integration tests that invoke spec-kitty orchestrator-api ... through the root CLI so that the tested path matches what callers actually use.HighOpen
FR-004No-subcommand JSON responseAs an orchestrator, I want spec-kitty orchestrator-api (no subcommand) to return a JSON envelope so that even discovery/help requests are machine-parseable.MediumOpen
FR-005Clean up backward-compat shimsAs a maintainer, I want any --json no-op shims or legacy compatibility code removed so that the contract surface is minimal and honest.MediumOpen

Non-Functional Requirements

IDTitleRequirementCategoryPriorityStatus
NFR-001Zero prose on stdout100% of orchestrator-api command invocations (success or failure) must produce valid JSON on stdout, with zero bytes of non-JSON prose.CorrectnessHighOpen
NFR-002Code documentationAll error-handling paths must have inline comments explaining the JSON contract guarantee.MaintainabilityMediumOpen
NFR-003Test coverageEvery orchestrator-api subcommand must have at least one test exercising the root CLI entry path, not just the sub-app.QualityHighOpen

Constraints

IDTitleConstraintCategoryPriorityStatus
C-001No backward compatibilityNo --json flag or shim. The API is always-JSON. Callers that pass --json will get a clean USAGE_ERROR envelope.TechnicalHighOpen
C-0022.x branch targetAll changes target the 2.x branch.TechnicalHighOpen
C-003Minimal codePrefer deleting code over adding it. The fix should reduce the codebase, not grow it.TechnicalMediumOpen

Success Criteria

Measurable Outcomes

  • SC-001: Running spec-kitty orchestrator-api contract-version from a shell produces valid JSON on stdout with exit code 0.
  • SC-002: Running spec-kitty orchestrator-api contract-version --bogus from a shell produces a JSON envelope on stdout with error_code: "USAGE_ERROR" and a nonzero exit code.
  • SC-003: Every command signature in docs/reference/orchestrator-api.md matches the actual --help output with no stale flags.
  • SC-004: Integration tests invoke through the root CLI app, not just the sub-app, and all pass.