feat(orchestration): implement event sourcing core #23
Labels
No labels
area:config
area:contracts
area:engine
area:eventsourcing
area:frontend
area:git
area:ipc
area:persistence
area:provider
area:scaffold
area:terminal
type:user-story
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/peon!23
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "grunt/4"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
OrchestrationCommandenum (15 command variants covering projects, threads, turns, provider events, checkpoints, and git operations)OrchestrationEventenum (17 event variants) andStoredEventenvelopedecide()function that validates commands against the read model and produces domain events, withDeciderErrorfor invariant violationsproject()fold function that applies events to theOrchestrationReadModel, including cascade deletions for projects/threadsProviderEventenum minimally in the provider module (required byIngestProviderEventcommand)Test plan
cargo fmt --checkpassescargo clippy -p forge-agent-backend -- -D warningspassescargo test -p forge-agent-backend— 34 tests passCloses #4
Overall the structure is solid — pure functions, clean separation of decider/projector, good test coverage for the happy paths. Three issues need fixing before merge: one functional bug (data loss) and two missing negative tests that the AC explicitly requires.
@ -0,0 +395,4 @@])}ProviderEvent::Error { code, message } => {Bug: checkpoint description is permanently lost.
The command carries a
descriptionfield but the decider silently drops it withdescription: _, andCheckpointCreatedhas nodescriptionfield. The projector then hardcodesdescription: String::new(). Any description the user provides is thrown away at the boundary and can never be recovered from the event log.Fix: add
description: StringtoCheckpointCreatedinevents.rs, passdescription.clone()from the command here, and usedescription.clone()instead ofString::new()in the projector'sCheckpoint { .. }construction.@ -0,0 +300,4 @@#[test]fn turn_sequence_increments() {let (model, _, thread_id) = setup_thread();Missing negative test — AC violation.
The decider has a guard at
decider.rsthat rejectsDeleteProjectwhen the project has running threads (InvariantViolation("cannot delete project with running threads")), but there is no test exercising this path. The issue AC says "at least one positive + one negative test per command variant" —DeleteProjecthas a not-found negative but not a running-threads negative.Add a test: create a project, create a thread, start a turn (puts thread in
Running), then assert thatDeleteProjectreturnsDeciderError::InvariantViolation.@ -0,0 +598,4 @@let cmd = OrchestrationCommand::CommitChanges {thread_id,message: "feat: add feature".into(),};Missing negative tests for
ApprovePendingActionandRejectPendingAction— AC violation.Both commands validate that the thread is in
AwaitingApprovalstatus and returnInvariantViolationotherwise, but only positive tests exist. The issue AC requires at least one negative per command variant.Add two tests:
approve_pending_action_on_idle_thread_fails— callApprovePendingActionon a thread inIdlestatus, assertDeciderError::InvariantViolation.reject_pending_action_on_idle_thread_fails— same forRejectPendingAction.Overall the structure is solid — pure functions, clean separation of decider/projector, good test coverage for the happy paths. Three issues need fixing before merge: one functional bug (data loss) and two missing negative tests that the AC explicitly requires.
@ -0,0 +395,4 @@])}ProviderEvent::Error { code, message } => {Bug: checkpoint description is permanently lost.
The command carries a
descriptionfield but the decider silently drops it withdescription: _, andCheckpointCreatedhas nodescriptionfield. The projector then hardcodesdescription: String::new(). Any description the user provides is thrown away at the boundary and can never be recovered from the event log.Fix: add
description: StringtoCheckpointCreatedinevents.rs, passdescription.clone()from the command here, and usedescription.clone()instead ofString::new()in the projector'sCheckpoint { .. }construction.@ -0,0 +300,4 @@#[test]fn turn_sequence_increments() {let (model, _, thread_id) = setup_thread();Missing negative test — AC violation.
The decider has a guard at
decider.rsthat rejectsDeleteProjectwhen the project has running threads (InvariantViolation("cannot delete project with running threads")), but there is no test exercising this path. The issue AC says "at least one positive + one negative test per command variant" —DeleteProjecthas a not-found negative but not a running-threads negative.Add a test: create a project, create a thread, start a turn (puts thread in
Running), then assert thatDeleteProjectreturnsDeciderError::InvariantViolation.@ -0,0 +598,4 @@let cmd = OrchestrationCommand::CommitChanges {thread_id,message: "feat: add feature".into(),};Missing negative tests for
ApprovePendingActionandRejectPendingAction— AC violation.Both commands validate that the thread is in
AwaitingApprovalstatus and returnInvariantViolationotherwise, but only positive tests exist. The issue AC requires at least one negative per command variant.Add two tests:
approve_pending_action_on_idle_thread_fails— callApprovePendingActionon a thread inIdlestatus, assertDeciderError::InvariantViolation.reject_pending_action_on_idle_thread_fails— same forRejectPendingAction.Pull request closed