huskies: merge 997

This commit is contained in:
dave
2026-05-14 11:01:06 +00:00
parent 0572af2193
commit c7a7cb4281
40 changed files with 256 additions and 253 deletions
+82 -48
View File
@@ -245,12 +245,13 @@ pub fn set_plan_state(story_id: &str, state: crate::pipeline_state::PlanState) -
///
/// `stage` is the typed pipeline state; it is serialised to the canonical
/// clean wire form (story 934) via [`stage_dir_name`] at the CRDT boundary.
/// The `retries` count embedded in `Stage::Coding` / `Stage::Merge` is
/// automatically written to the `retry_count` CRDT register (story 997).
pub fn write_item(
story_id: &str,
stage: &Stage,
name: Option<&str>,
agent: Option<&str>,
retry_count: Option<i64>,
depends_on: Option<&str>,
merged_at: Option<f64>,
) {
@@ -260,6 +261,12 @@ pub fn write_item(
Stage::Merge { claim, .. } => claim.as_ref(),
_ => None,
};
// Extract retries from the Stage payload; non-Coding/Merge stages store 0.
let stage_retries: f64 = match stage {
Stage::Coding { retries, .. } => *retries as f64,
Stage::Merge { retries, .. } => *retries as f64,
_ => 0.0,
};
let Some(state_mutex) = get_crdt() else {
return;
};
@@ -307,11 +314,9 @@ pub fn write_item(
s.crdt.doc.items[idx].agent.set(a.to_string())
});
}
if let Some(rc) = retry_count {
apply_and_persist(&mut state, |s| {
s.crdt.doc.items[idx].retry_count.set(rc as f64)
});
}
apply_and_persist(&mut state, |s| {
s.crdt.doc.items[idx].retry_count.set(stage_retries)
});
if let Some(d) = depends_on {
apply_and_persist(&mut state, |s| {
s.crdt.doc.items[idx].depends_on.set(d.to_string())
@@ -365,7 +370,7 @@ pub fn write_item(
"stage": stage_str,
"name": name.unwrap_or(""),
"agent": agent.unwrap_or(""),
"retry_count": retry_count.unwrap_or(0) as f64,
"retry_count": stage_retries,
"depends_on": depends_on.unwrap_or(""),
"claim_agent": insert_claim_agent,
"claim_ts": insert_claim_ts,
@@ -429,7 +434,6 @@ pub fn write_item_str(
stage: &str,
name: Option<&str>,
agent: Option<&str>,
retry_count: Option<i64>,
depends_on: Option<&str>,
merged_at: Option<f64>,
) {
@@ -453,58 +457,88 @@ pub fn write_item_str(
crate::slog!("[crdt_state] write_item_str: unknown stage '{stage}' for {story_id}");
return;
};
write_item(
story_id,
&typed,
name,
agent,
retry_count,
depends_on,
merged_at,
);
write_item(story_id, &typed, name, agent, depends_on, merged_at);
}
/// Set `retry_count` to an explicit value for a pipeline item.
/// Set `retries` to an explicit value for a pipeline item via a Stage transition.
///
/// Pure metadata operation — the item's stage is not changed.
/// Call `set_retry_count(story_id, 0)` to reset the counter after a
/// stage transition or an explicit unblock.
/// Reads the current Stage from the CRDT, updates the `retries` field (only
/// meaningful for `Stage::Coding` and `Stage::Merge`), and writes back via
/// `write_item`. No-op for items not in a Coding or Merge stage.
pub fn set_retry_count(story_id: &str, count: i64) {
let Some(state_mutex) = get_crdt() else {
let Some(item) = super::super::read::read_item(story_id) else {
return;
};
let Ok(mut state) = state_mutex.lock() else {
return;
let new_stage = match item.stage().clone() {
Stage::Coding {
claim,
plan,
retries: _,
} => Stage::Coding {
claim,
plan,
retries: count.max(0) as u32,
},
Stage::Merge {
feature_branch,
commits_ahead,
claim,
retries: _,
} => Stage::Merge {
feature_branch,
commits_ahead,
claim,
retries: count.max(0) as u32,
},
_ => return,
};
if let Some(&idx) = state.index.get(story_id) {
apply_and_persist(&mut state, |s| {
s.crdt.doc.items[idx].retry_count.set(count as f64)
});
}
write_item(story_id, &new_stage, None, None, None, None);
}
/// Increment `retry_count` by 1 and return the new value.
/// Increment `retries` by 1 and return the new value.
///
/// Pure metadata operation — the item's stage is not changed.
/// Returns 0 if the item is not found in the CRDT (no-op in that case).
/// Use the returned value to decide whether the story should be blocked.
/// Reads the current Stage, increments the embedded `retries` field, and
/// writes back via `write_item`. Returns `0` if the item is not found or is
/// not in a Coding or Merge stage (no-op in that case).
pub fn bump_retry_count(story_id: &str) -> i64 {
let Some(state_mutex) = get_crdt() else {
let Some(item) = super::super::read::read_item(story_id) else {
return 0;
};
let Ok(mut state) = state_mutex.lock() else {
return 0;
let (new_stage, new_retries) = match item.stage().clone() {
Stage::Coding {
claim,
plan,
retries,
} => {
let n = retries + 1;
(
Stage::Coding {
claim,
plan,
retries: n,
},
n,
)
}
Stage::Merge {
feature_branch,
commits_ahead,
claim,
retries,
} => {
let n = retries + 1;
(
Stage::Merge {
feature_branch,
commits_ahead,
claim,
retries: n,
},
n,
)
}
_ => return 0,
};
let Some(&idx) = state.index.get(story_id) else {
return 0;
};
let current = match state.crdt.doc.items[idx].retry_count.view() {
JsonValue::Number(n) => n as i64,
_ => 0,
};
let new_count = current + 1;
apply_and_persist(&mut state, |s| {
s.crdt.doc.items[idx].retry_count.set(new_count as f64)
});
new_count
write_item(story_id, &new_stage, None, None, None, None);
new_retries as i64
}