huskies: merge 973

This commit is contained in:
dave
2026-05-13 14:02:16 +00:00
parent e9a7468d8a
commit 4b18c01835
4 changed files with 117 additions and 0 deletions
+88
View File
@@ -905,4 +905,92 @@ fn merge_failure_unblock_moves_to_merge_via_crdt() {
);
}
// ── Story 973: Merge → Coding (abort in-flight merge) ───────────────
/// AC1 (pure): `Merge + MergeAborted` transitions to `Coding`.
#[test]
fn merge_aborted_returns_to_coding() {
let s = Stage::Merge {
feature_branch: fb("feature/story-73"),
commits_ahead: nz(2),
};
let result = transition(s, PipelineEvent::MergeAborted).unwrap();
assert!(
matches!(result, Stage::Coding),
"Merge + MergeAborted should return to Coding, got: {result:?}"
);
}
/// AC1 (CRDT): set stage to `Merge`, apply `MergeAborted`, assert CRDT stage is `coding`.
#[test]
fn merge_aborted_moves_to_coding_via_crdt() {
crate::crdt_state::init_for_test();
crate::db::ensure_content_store();
let story_id = "99973_story_merge_aborted";
crate::db::write_item_with_content(
story_id,
"merge",
"---\nname: Merge Aborted Test\n---\n# Story\n",
crate::db::ItemMeta::named("Merge Aborted Test"),
);
let fired = super::apply::apply_transition(story_id, PipelineEvent::MergeAborted, None)
.expect("Merge + MergeAborted should succeed");
assert!(
matches!(fired.before, Stage::Merge { .. }),
"fired.before should be Merge: {:?}",
fired.before
);
assert!(
matches!(fired.after, Stage::Coding),
"fired.after should be Coding: {:?}",
fired.after
);
let item = read_typed(story_id)
.expect("CRDT read should succeed")
.expect("item should exist");
assert_eq!(
item.stage.dir_name(),
"coding",
"CRDT stage should be coding after Merge + MergeAborted"
);
}
/// AC1 (move_story): `move_story_to_stage` with target "current" on a Merge story succeeds.
#[test]
fn move_story_merge_to_current_succeeds() {
crate::crdt_state::init_for_test();
crate::db::ensure_content_store();
let story_id = "99973_story_move_merge_to_current";
crate::db::write_item_with_content(
story_id,
"merge",
"---\nname: Move Merge To Current\n---\n",
crate::db::ItemMeta::named("Move Merge To Current"),
);
let result = crate::agents::lifecycle::move_story_to_stage(story_id, "current");
assert!(
result.is_ok(),
"move_story_to_stage(merge → current) should succeed: {result:?}"
);
let (from, to) = result.unwrap();
assert_eq!(from, "merge", "from_stage should be 'merge'");
assert_eq!(to, "current", "to_stage should be 'current'");
let item = read_typed(story_id)
.expect("CRDT read should succeed")
.expect("item should exist");
assert!(
matches!(item.stage, Stage::Coding),
"story should be in Coding after move_story_to_stage(merge → current): {:?}",
item.stage
);
}
// ── ProjectionError Display ─────────────────────────────────────────
+6
View File
@@ -72,6 +72,8 @@ pub enum PipelineEvent {
FixupRequested,
/// Story 972: user sends a MergeFailure story back to Qa for re-review.
ReQueuedForQa,
/// Story 973: user aborts an in-flight merge, sending the story back to Coding.
MergeAborted,
}
// ── Per-node execution events ───────────────────────────────────────────────
@@ -117,6 +119,7 @@ pub fn event_label(e: &PipelineEvent) -> &'static str {
PipelineEvent::MergemasterAttempted => "MergemasterAttempted",
PipelineEvent::FixupRequested => "FixupRequested",
PipelineEvent::ReQueuedForQa => "ReQueuedForQa",
PipelineEvent::MergeAborted => "MergeAborted",
}
}
@@ -310,6 +313,9 @@ pub fn transition(state: Stage, event: PipelineEvent) -> Result<Stage, Transitio
// ── ReQueuedForQa: MergeFailure → Qa (re-review) ────────────────
(MergeFailure { .. }, ReQueuedForQa) => Ok(Qa),
// ── MergeAborted: Merge → Coding (abort in-flight merge) ─────────
(Merge { .. }, MergeAborted) => Ok(Coding),
// ── MergemasterAttempted: MergeFailure → MergeFailureFinal ─────
(MergeFailure { reason, .. }, MergemasterAttempted) => Ok(MergeFailureFinal { reason }),
(MergeFailureFinal { reason }, MergemasterAttempted) => Ok(MergeFailureFinal { reason }),