huskies: merge 998
This commit is contained in:
@@ -50,46 +50,6 @@ pub(super) fn is_story_blocked(story_id: &str) -> bool {
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Return `true` if the story's merge failure is a git content-conflict
|
||||
/// (`Stage::MergeFailure { kind: ConflictDetected(_), .. }`).
|
||||
///
|
||||
/// Used by the auto-assigner to decide whether to spawn mergemaster automatically.
|
||||
/// The typed kind is carried by the CRDT projection layer (which reads
|
||||
/// `ContentKey::GateOutput` on projection to reconstruct the kind on restart),
|
||||
/// so no direct content-store access is needed here (story 982).
|
||||
pub(super) fn has_content_conflict_failure(story_id: &str) -> bool {
|
||||
crate::pipeline_state::read_typed(story_id)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|item| {
|
||||
matches!(
|
||||
item.stage,
|
||||
crate::pipeline_state::Stage::MergeFailure {
|
||||
kind: crate::pipeline_state::MergeFailureKind::ConflictDetected(_),
|
||||
..
|
||||
}
|
||||
)
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Return `true` if the story is in `Stage::MergeFailureFinal`.
|
||||
///
|
||||
/// Story 945: `Stage::MergeFailureFinal` is the single source of truth —
|
||||
/// the legacy `mergemaster_attempted: bool` CRDT register has been deleted.
|
||||
/// Used to prevent the auto-assigner from repeatedly spawning mergemaster for
|
||||
/// the same story after a failed mergemaster session.
|
||||
pub(super) fn has_mergemaster_attempted(story_id: &str) -> bool {
|
||||
crate::crdt_state::read_item(story_id)
|
||||
.map(|view| {
|
||||
matches!(
|
||||
view.stage(),
|
||||
crate::pipeline_state::Stage::MergeFailureFinal { .. }
|
||||
)
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Return `true` if the story has any `depends_on` entries that are not yet in
|
||||
/// `5_done` or `6_archived`. Reads dependency state from the CRDT (story 929).
|
||||
pub(super) fn has_unmet_dependencies(story_id: &str) -> bool {
|
||||
@@ -345,81 +305,4 @@ mod tests {
|
||||
let archived_deps = check_archived_dependencies("503_story_waiting");
|
||||
assert!(archived_deps.is_empty());
|
||||
}
|
||||
|
||||
// ── Story 982: typed MergeFailureKind — has_content_conflict_failure ──────
|
||||
|
||||
/// AC2 (story 982): `has_content_conflict_failure` returns `true` when the
|
||||
/// story is in `Stage::MergeFailure { kind: ConflictDetected(_), .. }`.
|
||||
/// The test seeds the stage via `transition_to_merge_failure` (no direct
|
||||
/// content-store or MergeJob writes in the test body).
|
||||
#[test]
|
||||
fn has_content_conflict_failure_true_for_conflict_detected_kind() {
|
||||
crate::crdt_state::init_for_test();
|
||||
crate::db::ensure_content_store();
|
||||
let story_id = "982_ac2_conflict_detected";
|
||||
// Seed at Merge stage so the transition is valid.
|
||||
crate::db::write_item_with_content(
|
||||
story_id,
|
||||
"4_merge",
|
||||
"---\nname: AC2 conflict test\n---\n",
|
||||
crate::db::ItemMeta::named("AC2 conflict test"),
|
||||
);
|
||||
// Transition via the lifecycle helper — internally writes ContentKey::GateOutput
|
||||
// so the CRDT projection can reconstruct the kind; no content-store writes here.
|
||||
crate::agents::lifecycle::transition_to_merge_failure(
|
||||
story_id,
|
||||
crate::pipeline_state::MergeFailureKind::ConflictDetected(Some(
|
||||
"CONFLICT (content): server/src/lib.rs".to_string(),
|
||||
)),
|
||||
)
|
||||
.expect("transition should succeed");
|
||||
|
||||
// The typed match now drives the predicate — no substring scan.
|
||||
assert!(
|
||||
has_content_conflict_failure(story_id),
|
||||
"has_content_conflict_failure must be true for ConflictDetected kind"
|
||||
);
|
||||
// Verify the projected stage carries the typed kind.
|
||||
let item = crate::pipeline_state::read_typed(story_id)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert!(
|
||||
matches!(
|
||||
item.stage,
|
||||
crate::pipeline_state::Stage::MergeFailure {
|
||||
kind: crate::pipeline_state::MergeFailureKind::ConflictDetected(_),
|
||||
..
|
||||
}
|
||||
),
|
||||
"stage must be MergeFailure(ConflictDetected): {:?}",
|
||||
item.stage
|
||||
);
|
||||
}
|
||||
|
||||
/// AC2 (story 982): `has_content_conflict_failure` returns `false` when the
|
||||
/// kind is `GatesFailed` — no mergemaster spawn for gate-only failures.
|
||||
#[test]
|
||||
fn has_content_conflict_failure_false_for_gates_failed_kind() {
|
||||
crate::crdt_state::init_for_test();
|
||||
crate::db::ensure_content_store();
|
||||
let story_id = "982_ac2_gates_failed";
|
||||
crate::db::write_item_with_content(
|
||||
story_id,
|
||||
"4_merge",
|
||||
"---\nname: AC2 gates test\n---\n",
|
||||
crate::db::ItemMeta::named("AC2 gates test"),
|
||||
);
|
||||
crate::agents::lifecycle::transition_to_merge_failure(
|
||||
story_id,
|
||||
crate::pipeline_state::MergeFailureKind::GatesFailed(
|
||||
"error[clippy::unused_variable]".to_string(),
|
||||
),
|
||||
)
|
||||
.expect("transition should succeed");
|
||||
|
||||
assert!(
|
||||
!has_content_conflict_failure(story_id),
|
||||
"has_content_conflict_failure must be false for GatesFailed kind"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user