Contracts
lightweight-review-baseline.md
Contract: lightweight review missing-baseline diagnostic (issue #989)
Inputs
spec-kitty review --mission <handle> --mode lightweight
Mission state: meta.json has mission_id set (modern mission), baseline_merge_commit is null.
Output (failure path, modern mission)
- Exit code: non-zero.
- Verdict: non-passing.
- Structured diagnostic emitted with code
LIGHTWEIGHT_REVIEW_MISSING_BASELINE. - Payload fields:
code:"LIGHTWEIGHT_REVIEW_MISSING_BASELINE"mission_id: canonical ULIDmission_slug: human slugremediation: string containing the substringbaseline_merge_commit
Output (legacy mission, no mission_id in meta.json)
- Exit code: zero (preserves historical behavior).
- Verdict: passing, but tagged with
LEGACY_MISSION_DEAD_CODE_SKIPso the path is greppable and discoverable.
Output (modern mission, baseline_merge_commit populated)
- Unchanged from today: dead-code scan runs normally.
Invariants
- Modern missions with
baseline_merge_commit: nullMUST NOT exit zero with a clean pass. - The diagnostic code is referenced at least once in production source and once in tests so
rg "LIGHTWEIGHT_REVIEW_MISSING_BASELINE"returns hits in both surfaces (NFR-004).
merge-dry-run-review-artifact.md
Contract: merge --dry-run review-artifact consistency gate (issue #991)
Inputs
spec-kitty merge --mission <handle> --dry-run [--json]
Mission state: at least one WP has lane approved AND its latest tasks/<WP>/review-cycle-N.md artifact has verdict: rejected (or otherwise fails find_rejected_review_artifact_conflicts()).
Output (failure path, --json)
- Exit code: non-zero.
- JSON payload includes
REJECTED_REVIEW_ARTIFACT_CONFLICTunder the same key the real merge uses (typically ablockers/errorsarray). - Each entry identifies the offending
wp_idand the offending review-cycle file path. - Diagnostic payload shape MUST match the real merge's emission of the same conflict (consumers should not need to branch on dry-run vs. real-merge JSON).
Output (failure path, human / non-JSON)
- Exit code: non-zero.
- Human output contains a clearly-labeled
REJECTED_REVIEW_ARTIFACT_CONFLICTline that names the offending WP.
Output (success path)
- Unchanged from today: existing dry-run preview is emitted with no behavior change.
Invariants
- Dry-run and real merge MUST share a single helper that calls
find_rejected_review_artifact_conflicts(). - Existing tests in
tests/post_merge/test_review_artifact_consistency.pyandtests/merge/test_merge_post_merge_invariant.pyremain green. - The success path of dry-run produces no new false-positive blockers (spec C-002).
next-json-claimability.md
Contract: next --json claimability payload (issue #988)
Inputs
spec-kitty next --mission <handle> --json
Mission state: mission_state == "implement", preview_step == "implement", at least one WP in lane planned whose dependencies are satisfied.
Output (JSON, success path)
{
"mission_state": "implement",
"planned_wps": <int>,
"preview_step": "implement",
"wp_id": "WP##", // REQUIRED: concrete WP that `agent action implement` would claim
"selection_reason": null // null when wp_id is set
// ... existing fields unchanged
}
Output (JSON, no-candidate path)
{
"mission_state": "implement",
"planned_wps": <int>,
"preview_step": "implement",
"wp_id": null,
"selection_reason": "no_planned_wps" | "all_wps_in_progress" | "dependencies_unsatisfied" | "baseline_violation"
// ... existing fields unchanged
}
Invariants
wp_id != nulliffselection_reason == null.wp_idMUST equal the WP that an immediately-followingspec-kitty agent action implement --mission <handle> --agent <name>would claim, given the same mission state.- Non-implement
mission_statevalues keep their existing payload shape (wire shape is unchanged for them — see spec C-001).