huskies: merge 870

This commit is contained in:
dave
2026-04-29 15:17:47 +00:00
parent db65271587
commit 2655288412
11 changed files with 251 additions and 80 deletions
+2 -2
View File
@@ -52,8 +52,8 @@ pub use types::{
TestJobCrdt, TestJobView, TokenUsageCrdt, TokenUsageView, subscribe,
};
pub use write::{
migrate_names_from_slugs, migrate_story_ids_to_numeric, name_from_story_id, set_qa_mode,
write_item,
bump_retry_count, migrate_names_from_slugs, migrate_story_ids_to_numeric, name_from_story_id,
set_qa_mode, set_retry_count, write_item,
};
#[cfg(test)]
+2 -2
View File
@@ -133,7 +133,7 @@ pub fn dump_crdt_state(story_id_filter: Option<&str>) -> CrdtStateDump {
_ => None,
};
let retry_count = match item_crdt.retry_count.view() {
JsonValue::Number(n) if n > 0.0 => Some(n as i64),
JsonValue::Number(n) => Some(n as i64),
_ => None,
};
let blocked = match item_crdt.blocked.view() {
@@ -290,7 +290,7 @@ pub(super) fn extract_item_view(item: &PipelineItemCrdt) -> Option<PipelineItemV
_ => None,
};
let retry_count = match item.retry_count.view() {
JsonValue::Number(n) if n > 0.0 => Some(n as i64),
JsonValue::Number(n) => Some(n as i64),
_ => None,
};
let blocked = match item.blocked.view() {
+110
View File
@@ -350,6 +350,51 @@ pub fn write_item(
}
}
/// Set `retry_count` to an explicit value for a pipeline item.
///
/// 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.
pub fn set_retry_count(story_id: &str, count: i64) {
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].retry_count.set(count as f64)
});
}
}
/// Increment `retry_count` 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.
pub fn bump_retry_count(story_id: &str) -> i64 {
let Some(state_mutex) = get_crdt() else {
return 0;
};
let Ok(mut state) = state_mutex.lock() else {
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
}
#[cfg(test)]
mod tests {
use super::super::hex;
@@ -704,6 +749,71 @@ mod tests {
use sqlx::SqlitePool;
use sqlx::sqlite::SqliteConnectOptions;
// ── set_retry_count / bump_retry_count tests ─────────────────────────────
#[test]
fn bump_retry_count_increments_by_one() {
init_for_test();
write_item(
"9001_story_bump_test",
"2_current",
None,
None,
None,
None,
None,
None,
None,
None,
);
let v1 = bump_retry_count("9001_story_bump_test");
assert_eq!(v1, 1, "first bump should return 1");
let v2 = bump_retry_count("9001_story_bump_test");
assert_eq!(v2, 2, "second bump should return 2");
let item = read_item("9001_story_bump_test").expect("item must exist");
assert_eq!(
item.retry_count,
Some(2),
"CRDT must reflect final bump value"
);
}
#[test]
fn set_retry_count_resets_to_zero() {
init_for_test();
write_item(
"9002_story_set_test",
"2_current",
None,
None,
Some(5),
None,
None,
None,
None,
None,
);
set_retry_count("9002_story_set_test", 0);
let item = read_item("9002_story_set_test").expect("item must exist");
assert_eq!(
item.retry_count,
Some(0),
"set_retry_count(0) must reset to 0"
);
}
#[test]
fn bump_returns_zero_for_missing_item() {
init_for_test();
let result = bump_retry_count("nonexistent_story");
assert_eq!(result, 0, "bump on missing item should return 0 (no-op)");
}
#[tokio::test]
async fn bug_511_rowid_replay_preserves_field_update_after_list_insert() {
let tmp = tempfile::tempdir().unwrap();