huskies: merge 945

This commit is contained in:
dave
2026-05-13 06:05:01 +00:00
parent 3a8894ea8f
commit 9ce5a8df0c
53 changed files with 497 additions and 654 deletions
+10 -87
View File
@@ -82,15 +82,15 @@ pub fn set_epic(story_id: &str, epic_id: Option<&str>) -> bool {
true
}
/// Set the `review_hold` CRDT flag for a pipeline item (sub-story 932).
/// Set the `resume_to` CRDT register for a pipeline item (story 945).
///
/// `true` marks the item as held for human review at a pipeline-stage boundary;
/// the auto-assigner skips items with this flag. `false` clears the hold and
/// is written explicitly (the register is not removed) so the cleared state
/// survives CRDT replay correctly.
/// This sibling register stores the wire-form stage name (`"coding"`, `"qa"`,
/// etc.) that a `Stage::Frozen` or `Stage::ReviewHold` variant should resume
/// to. Empty string means "no resume target stored" (defaults to `Coding`
/// on read).
///
/// Returns `true` if the item was found and the op was applied, `false` otherwise.
pub fn set_review_hold(story_id: &str, value: bool) -> bool {
pub fn set_resume_to(story_id: &str, stage: &Stage) -> bool {
let Some(state_mutex) = get_crdt() else {
return false;
};
@@ -100,54 +100,8 @@ pub fn set_review_hold(story_id: &str, value: bool) -> bool {
let Some(&idx) = state.index.get(story_id) else {
return false;
};
apply_and_persist(&mut state, |s| s.crdt.doc.items[idx].review_hold.set(value));
true
}
/// Set the `frozen` CRDT flag for a pipeline item (story 934, stage 4).
///
/// `true` freezes the story at its current `Stage` — the auto-assigner skips
/// it but the stage register is untouched. `false` unfreezes; the story
/// remains at its current stage and resumes auto-assignment. Both writes
/// are explicit (not removals) so the cleared state survives CRDT replay.
///
/// Returns `true` if the item was found and the op was applied, `false` otherwise.
pub fn set_frozen(story_id: &str, value: bool) -> bool {
let Some(state_mutex) = get_crdt() else {
return false;
};
let Ok(mut state) = state_mutex.lock() else {
return false;
};
let Some(&idx) = state.index.get(story_id) else {
return false;
};
apply_and_persist(&mut state, |s| s.crdt.doc.items[idx].frozen.set(value));
true
}
/// Set the `mergemaster_attempted` CRDT flag for a pipeline item.
///
/// Passing `true` records that a mergemaster session has been spawned for this
/// item, preventing repeated auto-spawns across restarts.
/// Passing `false` explicitly writes `false` (does not remove the register) so
/// the cleared state is distinguishable from an unset register and survives
/// CRDT replay correctly.
///
/// Returns `true` if the item was found and the op was applied, `false` otherwise.
pub fn set_mergemaster_attempted(story_id: &str, value: bool) -> bool {
let Some(state_mutex) = get_crdt() else {
return false;
};
let Ok(mut state) = state_mutex.lock() else {
return false;
};
let Some(&idx) = state.index.get(story_id) else {
return false;
};
apply_and_persist(&mut state, |s| {
s.crdt.doc.items[idx].mergemaster_attempted.set(value)
});
let value = stage_dir_name(stage).to_string();
apply_and_persist(&mut state, |s| s.crdt.doc.items[idx].resume_to.set(value));
true
}
@@ -244,7 +198,6 @@ pub fn write_item(
name: Option<&str>,
agent: Option<&str>,
retry_count: Option<i64>,
blocked: Option<bool>,
depends_on: Option<&str>,
claimed_by: Option<&str>,
claimed_at: Option<f64>,
@@ -292,9 +245,6 @@ pub fn write_item(
s.crdt.doc.items[idx].retry_count.set(rc as f64)
});
}
if let Some(b) = blocked {
apply_and_persist(&mut state, |s| s.crdt.doc.items[idx].blocked.set(b));
}
if let Some(d) = depends_on {
apply_and_persist(&mut state, |s| {
s.crdt.doc.items[idx].depends_on.set(d.to_string())
@@ -335,17 +285,14 @@ pub fn write_item(
"name": name.unwrap_or(""),
"agent": agent.unwrap_or(""),
"retry_count": retry_count.unwrap_or(0) as f64,
"blocked": blocked.unwrap_or(false),
"depends_on": depends_on.unwrap_or(""),
"claimed_by": claimed_by.unwrap_or(""),
"claimed_at": claimed_at.unwrap_or(0.0),
"merged_at": merged_at.unwrap_or(0.0),
"qa_mode": "",
"mergemaster_attempted": false,
"review_hold": false,
"item_type": "",
"epic": "",
"frozen": false,
"resume_to": "",
})
.into();
@@ -366,17 +313,14 @@ pub fn write_item(
item.name.advance_seq(floor);
item.agent.advance_seq(floor);
item.retry_count.advance_seq(floor);
item.blocked.advance_seq(floor);
item.depends_on.advance_seq(floor);
item.claimed_by.advance_seq(floor);
item.claimed_at.advance_seq(floor);
item.merged_at.advance_seq(floor);
item.qa_mode.advance_seq(floor);
item.mergemaster_attempted.advance_seq(floor);
item.review_hold.advance_seq(floor);
item.item_type.advance_seq(floor);
item.epic.advance_seq(floor);
item.frozen.advance_seq(floor);
item.resume_to.advance_seq(floor);
}
// Broadcast a CrdtEvent for the new item.
@@ -404,7 +348,6 @@ pub fn write_item_str(
name: Option<&str>,
agent: Option<&str>,
retry_count: Option<i64>,
blocked: Option<bool>,
depends_on: Option<&str>,
claimed_by: Option<&str>,
claimed_at: Option<f64>,
@@ -436,7 +379,6 @@ pub fn write_item_str(
name,
agent,
retry_count,
blocked,
depends_on,
claimed_by,
claimed_at,
@@ -463,25 +405,6 @@ pub fn set_retry_count(story_id: &str, count: i64) {
}
}
/// Set the `blocked` register on a story to the given value.
///
/// Pure metadata operation — the item's stage is not changed.
/// Use this alongside a state-machine transition out of `Blocked` /
/// `MergeFailure` to keep the legacy `blocked` register in sync with the
/// typed stage post-865 (where YAML side-effects no longer clear the
/// register on their own).
pub fn set_blocked(story_id: &str, blocked: bool) {
let Some(state_mutex) = get_crdt() else {
return;
};
let Ok(mut state) = state_mutex.lock() else {
return;
};
if let Some(&idx) = state.index.get(story_id) {
apply_and_persist(&mut state, |s| s.crdt.doc.items[idx].blocked.set(blocked));
}
}
/// Increment `retry_count` by 1 and return the new value.
///
/// Pure metadata operation — the item's stage is not changed.