Status: Complete
What was done:
- Created
crates/cockpit-core/src/model.rswith all domain types from SPEC.md §6. - Newtypes:
ReviewId,IssueRef,PrRef,CommentId,ProjectRef— all distinct types wrappingString, withnew(),as_str(), andDisplay. - Enums:
GateState,AgentMode,CommentOrigin,Anchor,Artifact. - Structs:
PlanStep,PlanDoc,DiffData,Comment,AgentRun,ProjectPlan,Review. - Wired
pub mod model;inlib.rs.
Test results:
- 7 tests pass (6 new in model + 1 existing smoke).
cargo fmtclean,cargo clippy -- -D warningsclean,cargo test --allgreen.
Decisions:
- Used
SystemTimeinstead ofInstantforAgentRun.started_atbecauseInstantis not serializable and has no cross-process meaning. Documented inline. stale: boolonReviewkept as boolean per SPEC.md §6 — it is genuinely binary (an ancestor is or is not in rework) and the spec is explicit.DiffDatais a placeholder withraw: String— will be fleshed out in T0.5/T4.3.- Deferred
uuiddependency for ID generation to the task that first needs runtime IDs. - Used a
newtype_id!macro for the five ID types since they are structurally identical. - Added
Eqto all types whose fields support it, per CLAUDE.md §2 "derive eagerly."
Status: Complete
What was done:
- Created
crates/cockpit-core/src/gate.rswith theGatedtrait and all state transitions. - Pure state transitions as default methods:
open,request_changes,approve,mark_reworked,mark_agent_failed. - Effectful methods (
dispatch,reconcile) are stubs returningError::NotImplemented. Gatedimplemented for bothReviewandProjectPlan— one loop, written once.- Stale flag logic as inherent methods on
Review(mark_stale,clear_stale). gate::Errorwiththiserror:IllegalTransition,NoComments,NotImplemented.
Test results:
- 34 new tests (41 total). All pass.
- Every legal transition tested (6 transitions from SPEC.md §7).
- Every illegal transition tested (19 invalid from/event pairs).
- Edge cases: no-comments rejected, comments cleared on reworked, comments preserved on agent failure, full cycle, agent-failed-then-redispatch, stale flag orthogonal to loop.
ProjectPlanfull cycle verifies same trait, no forking.
Decisions:
- Separated pure state transitions (default trait methods) from effectful dispatch/reconcile. This makes the state machine testable without real adapters.
mark_agent_failedpreserves comments — they are still pending feedback for re-dispatch.mark_reworkedclears comments — enforces Invariant 4 (ephemeral).gate_state_mut/comments_mutare public trait methods (Rust traits can't have private methods) but documented as implementation details.- Stale logic is on
Reviewdirectly, not onGated, because it's Review-specific per SPEC.md §7 ("stale gates the frontier, not the loop").