huskies: merge 960
This commit is contained in:
@@ -1,18 +1,12 @@
|
||||
//! Front-matter checks for story files: review holds, blocked state, and merge failures.
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
/// Read the optional `agent:` pin for a story.
|
||||
///
|
||||
/// After story 871 the agent assignment lives in the CRDT typed register
|
||||
/// (`PipelineItemView.agent`), not the YAML front matter. We check the CRDT
|
||||
/// first; falling back to legacy YAML parsing keeps behaviour intact for any
|
||||
/// stories whose CRDT entry doesn't yet have the field set.
|
||||
pub(super) fn read_story_front_matter_agent(
|
||||
_project_root: &Path,
|
||||
_stage_dir: &str,
|
||||
story_id: &str,
|
||||
) -> Option<String> {
|
||||
pub(super) fn read_story_front_matter_agent(story_id: &str) -> Option<String> {
|
||||
// Story 929: agent name comes from the CRDT register. The previous
|
||||
// YAML fallback is gone — post-891 every story has its CRDT entry,
|
||||
// and any story without one is treated as having no pinned agent.
|
||||
@@ -26,7 +20,7 @@ pub(super) fn read_story_front_matter_agent(
|
||||
/// The auto-assigner uses this to keep human-QA items / spikes parked after
|
||||
/// gates pass until a reviewer explicitly clears the hold (e.g. via
|
||||
/// `tool_approve_qa`).
|
||||
pub(super) fn has_review_hold(_project_root: &Path, _stage_dir: &str, story_id: &str) -> bool {
|
||||
pub(super) fn has_review_hold(story_id: &str) -> bool {
|
||||
crate::crdt_state::read_item(story_id)
|
||||
.map(|w| w.stage().is_review_hold())
|
||||
.unwrap_or(false)
|
||||
@@ -37,7 +31,7 @@ pub(super) fn has_review_hold(_project_root: &Path, _stage_dir: &str, story_id:
|
||||
///
|
||||
/// The typed pipeline stage register is the only source consulted — the legacy
|
||||
/// `blocked: true` YAML front-matter field is no longer checked.
|
||||
pub(super) fn is_story_blocked(_project_root: &Path, _stage_dir: &str, story_id: &str) -> bool {
|
||||
pub(super) fn is_story_blocked(story_id: &str) -> bool {
|
||||
crate::pipeline_state::read_typed(story_id)
|
||||
.ok()
|
||||
.flatten()
|
||||
@@ -52,11 +46,7 @@ pub(super) fn is_story_blocked(_project_root: &Path, _stage_dir: &str, story_id:
|
||||
/// The typed stage register is consulted first; the CRDT content store is then
|
||||
/// scanned for conflict markers (the projection layer does not carry the reason
|
||||
/// string). No YAML front-matter parsing is performed.
|
||||
pub(super) fn has_content_conflict_failure(
|
||||
_project_root: &Path,
|
||||
_stage_dir: &str,
|
||||
story_id: &str,
|
||||
) -> bool {
|
||||
pub(super) fn has_content_conflict_failure(story_id: &str) -> bool {
|
||||
let is_merge_failure = crate::pipeline_state::read_typed(story_id)
|
||||
.ok()
|
||||
.flatten()
|
||||
@@ -86,11 +76,7 @@ pub(super) fn has_content_conflict_failure(
|
||||
/// 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(
|
||||
_project_root: &Path,
|
||||
_stage_dir: &str,
|
||||
story_id: &str,
|
||||
) -> bool {
|
||||
pub(super) fn has_mergemaster_attempted(story_id: &str) -> bool {
|
||||
crate::crdt_state::read_item(story_id)
|
||||
.map(|view| view.stage().is_mergemaster_attempted())
|
||||
.unwrap_or(false)
|
||||
@@ -98,22 +84,14 @@ pub(super) fn has_mergemaster_attempted(
|
||||
|
||||
/// 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(
|
||||
_project_root: &Path,
|
||||
_stage_dir: &str,
|
||||
story_id: &str,
|
||||
) -> bool {
|
||||
pub(super) fn has_unmet_dependencies(story_id: &str) -> bool {
|
||||
!crate::crdt_state::check_unmet_deps_crdt(story_id).is_empty()
|
||||
}
|
||||
|
||||
/// Return the list of dependency story numbers that are in `6_archived` (satisfied
|
||||
/// via archive rather than via a clean `5_done` completion). Reads from the CRDT
|
||||
/// (story 929).
|
||||
pub(super) fn check_archived_dependencies(
|
||||
_project_root: &Path,
|
||||
_stage_dir: &str,
|
||||
story_id: &str,
|
||||
) -> Vec<u32> {
|
||||
pub(super) fn check_archived_dependencies(story_id: &str) -> Vec<u32> {
|
||||
crate::crdt_state::check_archived_deps_crdt(story_id)
|
||||
}
|
||||
|
||||
@@ -123,7 +101,7 @@ pub(super) fn check_archived_dependencies(
|
||||
/// the legacy `frozen: bool` CRDT register has been deleted. Frozen stories
|
||||
/// are skipped by the auto-assigner until `Unfreeze` returns them to
|
||||
/// `resume_to`.
|
||||
pub(super) fn is_story_frozen(_project_root: &Path, _stage_dir: &str, story_id: &str) -> bool {
|
||||
pub(super) fn is_story_frozen(story_id: &str) -> bool {
|
||||
crate::crdt_state::read_item(story_id)
|
||||
.map(|view| view.stage().is_frozen())
|
||||
.unwrap_or(false)
|
||||
@@ -141,7 +119,6 @@ mod tests {
|
||||
fn has_review_hold_returns_true_when_flag_set() {
|
||||
crate::crdt_state::init_for_test();
|
||||
crate::db::ensure_content_store();
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
// Story 945: review_hold is now a typed Stage variant, seeded via
|
||||
// the wire-form stage register directly.
|
||||
crate::crdt_state::write_item_str(
|
||||
@@ -155,14 +132,13 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
);
|
||||
assert!(has_review_hold(tmp.path(), "3_qa", "890_spike_held"));
|
||||
assert!(has_review_hold("890_spike_held"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_review_hold_returns_false_when_flag_unset() {
|
||||
crate::crdt_state::init_for_test();
|
||||
crate::db::ensure_content_store();
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
crate::crdt_state::write_item_str(
|
||||
"890_spike_active_qa",
|
||||
"3_qa",
|
||||
@@ -174,13 +150,12 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
);
|
||||
assert!(!has_review_hold(tmp.path(), "3_qa", "890_spike_active_qa"));
|
||||
assert!(!has_review_hold("890_spike_active_qa"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_review_hold_returns_false_when_story_unknown() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
assert!(!has_review_hold(tmp.path(), "3_qa", "99_spike_missing"));
|
||||
assert!(!has_review_hold("99_spike_missing"));
|
||||
}
|
||||
|
||||
// ── is_story_blocked — regression: typed stage is sole authority ──────────
|
||||
@@ -189,25 +164,19 @@ mod tests {
|
||||
fn is_story_blocked_set_via_typed_stage_returns_true() {
|
||||
crate::crdt_state::init_for_test();
|
||||
crate::db::ensure_content_store();
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
crate::db::write_item_with_content(
|
||||
"890_story_blocked_set",
|
||||
"2_blocked",
|
||||
"---\nname: Blocked Story\n---\n",
|
||||
crate::db::ItemMeta::named("Blocked Story"),
|
||||
);
|
||||
assert!(is_story_blocked(
|
||||
tmp.path(),
|
||||
"2_blocked",
|
||||
"890_story_blocked_set"
|
||||
));
|
||||
assert!(is_story_blocked("890_story_blocked_set"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_story_blocked_cleared_via_typed_stage_returns_false() {
|
||||
crate::crdt_state::init_for_test();
|
||||
crate::db::ensure_content_store();
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
// First set to blocked.
|
||||
crate::db::write_item_with_content(
|
||||
"890_story_blocked_clear",
|
||||
@@ -222,18 +191,13 @@ mod tests {
|
||||
"---\nname: Clearable Story\n---\n",
|
||||
crate::db::ItemMeta::named("Clearable Story"),
|
||||
);
|
||||
assert!(!is_story_blocked(
|
||||
tmp.path(),
|
||||
"2_current",
|
||||
"890_story_blocked_clear"
|
||||
));
|
||||
assert!(!is_story_blocked("890_story_blocked_clear"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_story_blocked_stale_yaml_is_ignored() {
|
||||
crate::crdt_state::init_for_test();
|
||||
crate::db::ensure_content_store();
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
// YAML front matter says `blocked: true`, but the typed CRDT stage is backlog.
|
||||
// After removing the YAML fallback, the function must return false.
|
||||
crate::db::write_item_with_content(
|
||||
@@ -243,7 +207,7 @@ mod tests {
|
||||
crate::db::ItemMeta::named("Stale"),
|
||||
);
|
||||
assert!(
|
||||
!is_story_blocked(tmp.path(), "1_backlog", "890_story_stale_yaml"),
|
||||
!is_story_blocked("890_story_stale_yaml"),
|
||||
"stale YAML `blocked: true` must not be reported as blocked when typed stage is Backlog"
|
||||
);
|
||||
}
|
||||
@@ -253,7 +217,6 @@ mod tests {
|
||||
#[test]
|
||||
fn has_unmet_dependencies_returns_true_when_dep_not_done() {
|
||||
crate::crdt_state::init_for_test();
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
crate::crdt_state::write_item_str(
|
||||
"10_story_blocked",
|
||||
"2_current",
|
||||
@@ -265,17 +228,12 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
);
|
||||
assert!(has_unmet_dependencies(
|
||||
tmp.path(),
|
||||
"2_current",
|
||||
"10_story_blocked"
|
||||
));
|
||||
assert!(has_unmet_dependencies("10_story_blocked"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_unmet_dependencies_returns_false_when_dep_done() {
|
||||
crate::crdt_state::init_for_test();
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
crate::crdt_state::write_item_str(
|
||||
"999_story_dep",
|
||||
"5_done",
|
||||
@@ -298,17 +256,12 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
);
|
||||
assert!(!has_unmet_dependencies(
|
||||
tmp.path(),
|
||||
"2_current",
|
||||
"10_story_ok"
|
||||
));
|
||||
assert!(!has_unmet_dependencies("10_story_ok"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_unmet_dependencies_returns_false_when_no_deps() {
|
||||
crate::crdt_state::init_for_test();
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
crate::crdt_state::write_item_str(
|
||||
"5_story_free",
|
||||
"2_current",
|
||||
@@ -320,11 +273,7 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
);
|
||||
assert!(!has_unmet_dependencies(
|
||||
tmp.path(),
|
||||
"2_current",
|
||||
"5_story_free"
|
||||
));
|
||||
assert!(!has_unmet_dependencies("5_story_free"));
|
||||
}
|
||||
|
||||
// ── Bug 503: archived-dep visibility ─────────────────────────────────────
|
||||
@@ -333,7 +282,6 @@ mod tests {
|
||||
#[test]
|
||||
fn check_archived_dependencies_returns_archived_ids() {
|
||||
crate::crdt_state::init_for_test();
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
crate::crdt_state::write_item_str(
|
||||
"500_spike_crdt",
|
||||
"6_archived",
|
||||
@@ -356,8 +304,7 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let archived_deps =
|
||||
check_archived_dependencies(tmp.path(), "1_backlog", "503_story_dependent");
|
||||
let archived_deps = check_archived_dependencies("503_story_dependent");
|
||||
assert_eq!(archived_deps, vec![500]);
|
||||
}
|
||||
|
||||
@@ -365,7 +312,6 @@ mod tests {
|
||||
#[test]
|
||||
fn check_archived_dependencies_empty_when_dep_in_done() {
|
||||
crate::crdt_state::init_for_test();
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
crate::crdt_state::write_item_str(
|
||||
"490_story_done",
|
||||
"5_done",
|
||||
@@ -388,8 +334,7 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let archived_deps =
|
||||
check_archived_dependencies(tmp.path(), "1_backlog", "503_story_waiting");
|
||||
let archived_deps = check_archived_dependencies("503_story_waiting");
|
||||
assert!(archived_deps.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user