Contracts

cli-behavior.md

Contract: CLI Behavior for Handle Resolution

round-trip: skip: prose behavior contract, not a serialized fixture

Behavioral contracts for the in-scope commands. Message text below is the target canonical form; <handle> is the verbatim user-supplied handle. Exit codes are non-zero for every error path (never a typer.BadParameter / "Invalid value" usage error for a resolution outcome).

C1 — Nonexistent handle (all in-scope commands)

Given a workspace with ≥1 real mission and a handle H matching none of them.

CommandBefore (defect)After (contract)
research --mission Hscaffolds kitty-specs/H/, exits 0Mission not found: H, non-zero, no filesystem change (human-only; no --json)
plan --mission HN missions found, pass --mission to disambiguateMission not found: H, non-zero (JSON parity)
tasks --mission Hsame disambiguate messageMission not found: H, non-zero (JSON parity)
merge --mission H (fresh)lanes.json is required … run task-finalizationMission not found: H, non-zero (human; --json is dry-run-only)
merge --resume --mission HNo interrupted merge to resumeMission not found: H, non-zero — the unknown-handle refusal pre-empts the no-state message
next --mission Halready clean not-foundkeeps its richer _emit_mission_not_found_error envelope (names H)
materialize --mission Halready clean Mission not found: Hunchanged (this is the exemplar)
resolver error (accept/review/audit)No mission found for handle "H" + backfill remediationunchanged — keeps FR-005 backfill-identity remediation
reconcile --mission Hmission dossier not found: Hunchanged — semantically distinct

the plan/tasks agent commands, materialize — emit a structured error object carrying the error kind and the handle. research (no --json) and merge (dry-run-only --json) are out of the parity claim.

kitty-specs/ is identical before and after (assert via in-process CliRunner).

  • JSON parity (FR-011, scoped): only where a JSON surface exists — next,
  • Snapshot invariant (NFR-001): for every row, a byte-for-byte listing of

C2 — Path-unsafe handle (unchanged)

Given --mission ../x (or any unsafe segment): the existing path-safety refusal fires (Mission slug is not a single safe path segment or equivalent). This is a distinct error from C1 and is not converted to "mission not found".

C3 — Ambiguous handle (unchanged)

Given a handle prefix matching >1 mission: the existing structured ambiguous-selector error (MISSION_AMBIGUOUS_SELECTOR) fires — distinct from C1.

C4 — merge --abort tolerance (preserved)

Given merge --abort --mission H where H is unresolvable during a broken coordination cleanup: abort remains tolerant (non-raising), completing coordination teardown. The C1 not-found gate applies only to the fresh/resume entry, never to --abort.

C5 — next with a missing --mission

Given next invoked with no --mission:

Mission countContract
exactly 1auto-select that mission and proceed as if its handle were passed
>1print each mission as mission_slug (mid8) — friendly_name, ask to re-run with --mission <handle>, exit non-zero (non-usage)
0print "no missions found" + point to the specify command, exit non-zero

available_missions: [{mission_slug, mid8, friendly_name}, …].

yields C1's clean not-found (the missing-handle change must not regress the bad-handle path).

  • No usage error: none of these is a typer.BadParameter / "Invalid value".
  • JSON parity: --json yields the same outcome; the >1 case includes
  • Regression: next --mission H with a nonexistent-but-present handle still

C6 — Canonical message shape (scoped)

Mission not found: <handle> (capital M, matching the materialize exemplar) — one source constant, handle verbatim, adopted by the FIXED commands (plan, tasks, research, merge fresh/resume) and reused by next. Excluded (keep richer envelopes): the identity-aware resolver error (No mission found for handle "<h>" + spec-kitty migrate backfill-identity remediation, FR-005), reconcile's "mission dossier not found", and audit/doctor. NFR-002 measures that the fixed commands route through the one constant — not byte-identity across the whole CLI.