Contracts

finalize-repin-contract.md

Contract: finalize re-pin CLI + JSON + error surfaces (#4827)

CLI flag surface (spec-kitty agent mission finalize-tasks)

Adds one boolean option; the existing surface is otherwise unchanged.

FlagTypeDefaultMeaning
--allow-orphanedboolFalseOnly meaningful with --refresh-planning-commit. Permits the re-pin to re-point a recorded SHA that is orphaned (present in the object store but unreachable from the target-branch tip — the mid-mission-rebase shape). Without it, --refresh-planning-commit stays advance-only (refuses a non-ancestor). Refused regardless for a foreign (absent) object.
  • The golden-contract frozenset _EXPECTED_FLAGS["finalize-tasks"] in test_mission_cli_golden_contract.py gains --allow-orphaned in the same PR (exact set-equality assertion).
  • The --refresh-planning-commit help text is updated to mention the --allow-orphaned escape hatch for the rebase case.

JSON success payload (--json)

planning_commit object action enum extends to include repinned:

{"result": "success",
 "planning_commit": {"action": "repinned", "sha": "<new tip>", "previous_sha": "<orphaned sha>", "branch_tip": "<new tip>"}}

Existing values captured / preserved / refreshed are unchanged. orchestrator_api/envelope.py CONTRACT_VERSION 1.5.0 → 1.6.0 (additive; no consumer contract test pins the action vocabulary).

Error / diagnostic contracts

SituationSurfaceContract
Plain finalize, proven orphan, capturable tipfinalize (human + --json error)fail closed BEFORE any lanes.json write; message names the recorded SHA, the tip, and the --refresh-planning-commit --allow-orphaned recovery.
--refresh-planning-commit (no --allow-orphaned), non-ancestorfinalizerefused (Exit 1); message contains "not an ancestor" (preserves #4141 test).
--refresh-planning-commit --allow-orphaned, foreign (absent) objectfinalizerefused (Exit 1); message says the object is absent / investigate; no re-pin.
Non-git / uncapturable tip, plain finalizefinalizeDEGRADE to historical preserve (no abort).
Orphaned pin at lane allocation / reconcile_merge_recorded_planning_commitraises an orphan-specific error whose next_step names the finalize re-pin recovery — NOT the generic PlanningCommitMergeConflictError.
Healthy (reachable) pin, real content conflict_merge_recorded_planning_commitunchanged generic PlanningCommitMergeConflictError.
Orphaned pin at claim gatecheck_claim_ancestrynames orphan + recovery, not a bare missing_refs entry.
Orphaned pin at owned-review base_mt_resolve_owned_review_basedetects orphan, fails closed / names recovery; never diffs against the dead base.

Backward-compatibility invariants (NFR-001)

  • --allow-orphaned defaults False; every existing invocation behaves identically.
  • The bare-flag non-ancestor refusal keeps the "not an ancestor" substring.
  • Non-git / synthetic-absent-SHA preserve (#3311) is unchanged (degrade path).