huskies: merge 919

This commit is contained in:
dave
2026-05-13 06:22:22 +00:00
parent 9ce5a8df0c
commit 09a8edc0a1
7 changed files with 146 additions and 48 deletions
+58 -5
View File
@@ -687,6 +687,8 @@ fn merge_failure_transition_emits_event_with_full_reason() {
fn merge_failure_plus_merge_failed_is_self_loop() {
let s = Stage::MergeFailure {
reason: "initial failure".into(),
feature_branch: fb("feature/story-1"),
commits_ahead: nz(1),
};
let result = transition(
s,
@@ -758,18 +760,20 @@ fn repeated_merge_failure_apply_transition_no_error_no_duplicate_notification()
);
}
// ── Story 893: MergeFailure + Unblock → Coding (retry) ─────────────
// ── Story 919: MergeFailure + Unblock → Merge (re-attempt) ─────────
/// AC1: `MergeFailure + Unblock` transitions to `Coding` (retry), not `Backlog`.
/// AC1: `MergeFailure + Unblock` transitions to `Merge` (re-attempt), not `Coding` or `Backlog`.
#[test]
fn merge_failure_unblock_returns_to_coding() {
fn merge_failure_unblock_returns_to_merge() {
let s = Stage::MergeFailure {
reason: "conflicts in server/src/main.rs".into(),
feature_branch: fb("feature/story-42"),
commits_ahead: nz(3),
};
let result = transition(s, PipelineEvent::Unblock).unwrap();
assert!(
matches!(result, Stage::Coding),
"MergeFailure + Unblock should return to Coding for immediate retry, got: {result:?}"
matches!(result, Stage::Merge { .. }),
"MergeFailure + Unblock should return to Merge for immediate re-attempt, got: {result:?}"
);
}
@@ -778,6 +782,8 @@ fn merge_failure_unblock_returns_to_coding() {
fn merge_failure_demote_returns_to_backlog() {
let s = Stage::MergeFailure {
reason: "conflicts".into(),
feature_branch: fb("feature/story-1"),
commits_ahead: nz(1),
};
let result = transition(s, PipelineEvent::Demote).unwrap();
assert!(
@@ -794,6 +800,8 @@ fn merge_failure_demote_returns_to_backlog() {
fn merge_failure_accept_pure_transition() {
let s = Stage::MergeFailure {
reason: "conflicts unresolvable".into(),
feature_branch: fb("feature/story-1"),
commits_ahead: nz(1),
};
let result = transition(s, PipelineEvent::Accepted).unwrap();
assert!(
@@ -852,4 +860,49 @@ fn merge_failure_accept_moves_to_done_via_crdt() {
);
}
// ── Story 919: MergeFailure + Unblock → Merge (regression) ─────────
/// AC3: CRDT-based regression — set stage to `MergeFailure`, call `unblock_story`
/// via `apply_transition`, assert the Stage register becomes `Stage::Merge`.
#[test]
fn merge_failure_unblock_moves_to_merge_via_crdt() {
crate::crdt_state::init_for_test();
crate::db::ensure_content_store();
let story_id = "99919_story_merge_failure_unblock";
crate::db::write_item_with_content(
story_id,
"merge_failure",
"---\nname: MergeFailure Unblock Regression\n---\n# Story\n",
crate::db::ItemMeta::named("MergeFailure Unblock Regression"),
);
let fired = super::apply::apply_transition(story_id, PipelineEvent::Unblock, None)
.expect("MergeFailure + Unblock should succeed");
assert!(
matches!(fired.before, Stage::MergeFailure { .. }),
"fired.before should be MergeFailure: {:?}",
fired.before
);
assert!(
matches!(fired.after, Stage::Merge { .. }),
"fired.after should be Merge, not Coding or Backlog: {:?}",
fired.after
);
let item = read_typed(story_id)
.expect("CRDT read should succeed")
.expect("item should exist");
assert_eq!(
item.stage.dir_name(),
"merge",
"CRDT stage should be merge after MergeFailure + Unblock"
);
assert!(
!matches!(item.stage, Stage::MergeFailure { .. }),
"MergeFailure variant must not remain after Unblock"
);
}
// ── ProjectionError Display ─────────────────────────────────────────