feat(932): add review_hold CRDT register + migrate callers off yaml_legacy
review_hold is now a typed bool register on PipelineItemCrdt alongside blocked / mergemaster_attempted. Exposed via the typed setter `crdt_state::set_review_hold(story_id, value)` and the `WorkItem::review_hold()` accessor. Replaces the legacy `review_hold: true` YAML front-matter field. Migrated callers: - http/mcp/qa_tools.rs::tool_approve_qa — clear via set_review_hold(false) - agents/lifecycle.rs::reject_story_from_qa — clear via set_review_hold(false) - agents/pool/pipeline/advance/helpers.rs::write_review_hold_to_store — set via set_review_hold(true), no more content rewrite - agents/pool/auto_assign/reconcile.rs (two callsites) — set via set_review_hold(true) instead of FS YAML write - agents/pool/auto_assign/story_checks.rs::has_review_hold — reads the typed register instead of conflating with Stage::Frozen (real bug fix: the legacy implementation returned `stage.is_frozen()`, which made the auto-assigner treat *every* held-for-review item as frozen even when it wasn't actually parked at the freeze stage). Dead yaml_legacy helpers removed: - write_review_hold(path), write_review_hold_in_content(content) - clear_front_matter_field(path) — last caller was the qa_tools wrap The yaml_residue marker doc now only mentions 933; the 932 line is gone. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,17 +21,15 @@ pub(super) fn read_story_front_matter_agent(
|
||||
.filter(|s| !s.is_empty())
|
||||
}
|
||||
|
||||
/// Return `true` if the story is in the `Frozen` pipeline stage.
|
||||
/// Return `true` if the story has its `review_hold` CRDT register set.
|
||||
///
|
||||
/// In the typed CRDT model, `Frozen` is the authoritative representation of
|
||||
/// stories that are held for human review (replacing the legacy
|
||||
/// `review_hold: true` YAML front-matter field). The typed stage register is
|
||||
/// the only source consulted — stale YAML is ignored.
|
||||
/// Sub-story 932: `review_hold` is now a dedicated CRDT register on
|
||||
/// `PipelineItemCrdt`, distinct from `Stage::Frozen`. 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 {
|
||||
crate::pipeline_state::read_typed(story_id)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|item| item.stage.is_frozen())
|
||||
crate::crdt_state::read_item(story_id)
|
||||
.map(|w| w.review_hold())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
@@ -138,29 +136,42 @@ mod tests {
|
||||
// ── has_review_hold ───────────────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn has_review_hold_returns_true_when_frozen() {
|
||||
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();
|
||||
crate::db::write_item_with_content(
|
||||
"890_spike_frozen",
|
||||
"7_frozen",
|
||||
"---\nname: Frozen Spike\n---\n# Spike\n",
|
||||
crate::db::ItemMeta::named("Frozen Spike"),
|
||||
crate::crdt_state::write_item(
|
||||
"890_spike_held",
|
||||
"3_qa",
|
||||
Some("Held Spike"),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
assert!(has_review_hold(tmp.path(), "3_qa", "890_spike_frozen"));
|
||||
crate::crdt_state::set_review_hold("890_spike_held", true);
|
||||
assert!(has_review_hold(tmp.path(), "3_qa", "890_spike_held"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_review_hold_returns_false_for_qa_stage() {
|
||||
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::db::write_item_with_content(
|
||||
crate::crdt_state::write_item(
|
||||
"890_spike_active_qa",
|
||||
"3_qa",
|
||||
"---\nname: Active QA Spike\n---\n# Spike\n",
|
||||
crate::db::ItemMeta::named("Active QA Spike"),
|
||||
Some("Active QA Spike"),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
assert!(!has_review_hold(tmp.path(), "3_qa", "890_spike_active_qa"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user