huskies: merge 1052

This commit is contained in:
dave
2026-05-14 18:04:35 +00:00
parent 977b954e98
commit b9709a6466
6 changed files with 73 additions and 5 deletions
+2 -2
View File
@@ -55,8 +55,8 @@ pub use types::{
pub use write::{
bump_retry_count, migrate_legacy_stage_strings, migrate_merge_job, migrate_names_from_slugs,
migrate_node_claims_to_agent_claims, migrate_story_ids_to_numeric, name_from_story_id,
set_agent, set_depends_on, set_epic, set_item_type, set_name, set_plan_state, set_qa_mode,
set_resume_to, set_resume_to_raw, set_retry_count, write_item,
purge_done_stage_merge_jobs, set_agent, set_depends_on, set_epic, set_item_type, set_name,
set_plan_state, set_qa_mode, set_resume_to, set_resume_to_raw, set_retry_count, write_item,
};
#[cfg(test)]
+55
View File
@@ -650,6 +650,61 @@ pub fn migrate_merge_job(db_path: &std::path::Path) {
slog!("[crdt] Migrated {count} MergeJob entries to typed MergeResult format");
}
/// Delete MergeJob CRDT entries for stories that have reached a terminal stage
/// (Done, Archived, Abandoned, Superseded, Rejected).
///
/// Pre-1036 code left stale MergeJob entries after a story recovered from a
/// merge failure and transitioned to Done. Those entries caused the gateway
/// UI to mislabel recovered stories as "FAILED" because `load_pipeline_state`
/// unconditionally read the MergeJob error field for every story. This
/// migration removes the orphaned entries so the state is clean on the next
/// server start.
///
/// Running this migration repeatedly is safe — tombstoned entries are filtered
/// out by the read path, so subsequent calls are no-ops.
pub fn purge_done_stage_merge_jobs() {
use crate::pipeline_state::Stage;
let Some(jobs) = crate::crdt_state::read_all_merge_jobs() else {
return;
};
if jobs.is_empty() {
return;
}
// Collect IDs of every story currently in a terminal stage.
let terminal_ids: std::collections::HashSet<String> = crate::pipeline_state::read_all_typed()
.into_iter()
.filter(|item| {
matches!(
item.stage,
Stage::Done { .. }
| Stage::Archived { .. }
| Stage::Abandoned { .. }
| Stage::Superseded { .. }
| Stage::Rejected { .. }
)
})
.map(|item| item.story_id.0.clone())
.collect();
let to_delete: Vec<String> = jobs
.into_iter()
.filter(|j| terminal_ids.contains(&j.story_id))
.map(|j| j.story_id)
.collect();
if to_delete.is_empty() {
return;
}
let count = to_delete.len();
for story_id in &to_delete {
crate::crdt_state::delete_merge_job(story_id);
}
slog!("[crdt] Purged {count} stale MergeJob entries for terminal-stage stories");
}
#[cfg(test)]
mod merge_job_migration_tests {
use super::super::super::state::init_for_test;
+1
View File
@@ -19,4 +19,5 @@ pub use item::write_item_str;
pub use migrations::{
migrate_legacy_stage_strings, migrate_merge_job, migrate_names_from_slugs,
migrate_node_claims_to_agent_claims, migrate_story_ids_to_numeric, name_from_story_id,
purge_done_stage_merge_jobs,
};