Data model: implement phases, seam decisions and typed refusals

This mission adds no persisted data. The "entities" are the in-process values that flow between the implement phases and the typed errors seams raise instead of printing. Persisted state (meta.json, status.events.jsonl, lanes.json, git refs) is read and written exactly as today (C-001).

Phase results (values passed along the sequence)

ValueProduced by phaseFields (existing names kept)Consumed by
ImplementContextcontextrepo_root, auto_commit, mission_slug, feature_dir, wp_file, declared_deps (today the 5-tuple from _detect_wp_context + find_repo_root)every later phase
ClaimPreflightclaim preflight + dependency gateplanning_branch, status_feature_dir, lanes_feature_dirplanning commit, lane selection
(none)planning-artifact commitside effect only, or a refusal—
WorkspaceSelectionworkspace/lane selectionresolved_workspace, lanes_manifest, laneallocate, record claim, present
AllocationResultallocatetoday's LaneWorkspaceResult (workspace_path, branch_name, lane_id, is_reuse, mission_branch, execution_mode, lane_test_env, resolution_kind) + effective_baserecord claim, present
status_resultrecord claimtoday's start_implementation_status returnclaim commit

Rules:

or NamedTuple in implement_phases.py.

preflight, the dependency gate, the planning commit, the bulk-edit gate, the operational context and workspace/lane selection. create wraps allocate and status start. The claim commit runs after create.

  • Each value is built by exactly one phase and is immutable afterwards. Prefer a frozen dataclass
  • The tracker step boundaries stay where they are today. detect wraps context. validate wraps
  • Exception handling per tracker step is unchanged; data-model.md does not alter it.

Seam decisions

See contracts/seam-decisions.md for signatures. Each decision is pure or takes an injected port, never prints, and raises one of the typed errors below or an exception type it raises today.

Typed refusals

ErrorRaised byRendered by (unchanged text)Status
WorkPackageStartRejected ("WP … is not finalized; run …")dependency-graph seam claim-precondition decisionvalidate tracker errorexisting type, moved raise site
ValueError("dependencies_not_satisfied: …")samevalidate tracker errorexisting type and text, moved raise site
BaseRefUnresolved (new, carries base_ref)lanes seam base-ref resolutioncommand prints _BASE_REF_UNRESOLVED_MSG + exit 1new type replacing a typer.Exit inside a decision
ValueError("… is not assigned to any lane in lanes.json")lanes seam lane lookupvalidate tracker errorexisting
MissingLanesError, CorruptLanesErrorlanes/persistence.py (unchanged)validate tracker errorunchanged (C-006)
WriteCheckout{WrongBranch,Occupied,Dirty}Errorlanes/implement_support.py (unchanged)create tracker errorunchanged (C-006)
meta.json demotion / corrupt refusal (str verdict)coordination planning-commit predicateadapter prints Error: <text> + exit 1existing predicate returns `str
structural planning change listimplement_cores.detect_structural_planning_changes (unchanged)adapter prints the #1598 refusalunchanged
PrimaryKindReachedCoordStagingErrorcoordination planning-commit partition guardpropagates (validate tracker error)existing type, moved raise site
PlacementResolutionRequiredplacement resolution (FR-008, one remedy definition, FR-018)validate tracker errorexisting type; single message definition
VCS-lock: meta missing / invalid JSONlanes seam VCS-lock decision (typed)command prints today's two messages + exit 1new types replacing typer.Exit inside a decision

Invariants

1. The side-effect order in plan.md "Engineering Alignment" holds (FR-009 pins it). 2. No lower package imports specify_cli.cli (C-004). The adapters in cli/commands/ are the only printers. 3. The C-006 five-tuple _BookkeepingTransactionIdentifiers keeps its arity and order. 4. The claim commit propagates exactly SafeCommitPathPolicyError, SafeCommitHeadMismatch and PlacementResolutionRequired; everything else is a warning with exit 0.