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
+34 -22
View File
@@ -212,8 +212,8 @@ fn legacy_stage_to_clean(s: &str) -> Option<&'static str> {
/// vocabulary (`"coding"`, `"merge"`, etc.).
///
/// Items that were at `"7_frozen"` additionally get the new `frozen` flag set
/// — the stage variant `Frozen` was dropped in story 934 stage 4 in favour of
/// an orthogonal CRDT register.
/// Story 945: the `Frozen` variant returns as `Stage::Frozen { resume_to }`,
/// replacing the orthogonal CRDT register that briefly existed in 934 stage 4.
///
/// One-time startup migration: items that have transitioned at least once
/// since story 934 stage 1 (which made writes emit clean form) are no-ops.
@@ -222,8 +222,10 @@ pub fn migrate_legacy_stage_strings() {
return;
};
// First pass: collect (index, clean_stage, set_frozen) for items that
// still carry legacy stage strings.
// First pass: collect (index, clean_stage, was_frozen) for items that
// still carry legacy stage strings. `was_frozen` triggers writing
// `resume_to = "backlog"` so the post-945 typed projection reads back as
// `Stage::Frozen { resume_to: Stage::Backlog }`.
let migrations: Vec<(usize, &'static str, bool)> = {
let Ok(state) = state_mutex.lock() else {
return;
@@ -239,7 +241,11 @@ pub fn migrate_legacy_stage_strings() {
};
let clean = legacy_stage_to_clean(&current)?;
let was_frozen = current == "7_frozen";
Some((idx, clean, was_frozen))
// For legacy frozen items, store the post-945 stage as
// "frozen" rather than "backlog" so the typed projection
// produces `Stage::Frozen` again.
let stage_out = if was_frozen { "frozen" } else { clean };
Some((idx, stage_out, was_frozen))
})
.collect()
};
@@ -258,12 +264,14 @@ pub fn migrate_legacy_stage_strings() {
s.crdt.doc.items[idx].stage.set(clean.to_string())
});
if was_frozen {
apply_and_persist(&mut state, |s| s.crdt.doc.items[idx].frozen.set(true));
apply_and_persist(&mut state, |s| {
s.crdt.doc.items[idx].resume_to.set("backlog".to_string())
});
}
}
slog!(
"[crdt] Migrated {count} legacy stage strings to clean wire form \
({frozen_count} of which were '7_frozen' → backlog + frozen=true)"
({frozen_count} of which were '7_frozen' → frozen + resume_to=backlog)"
);
}
@@ -292,7 +300,6 @@ mod stage_migration_tests {
None,
None,
None,
None,
);
// Then overwrite the stage register with the raw legacy string,
// bypassing `db::normalise_stage_str` / `write_item_str`'s mapping.
@@ -353,27 +360,33 @@ mod stage_migration_tests {
}
#[test]
fn migrate_collapses_7_frozen_to_backlog_and_sets_frozen_flag() {
fn migrate_promotes_7_frozen_to_typed_frozen_variant() {
init_for_test();
let story_id = "9510_legacy_frozen";
seed_with_raw_stage(story_id, "7_frozen");
// Sanity: before migration, the frozen flag is false.
let before = read_item(story_id).expect("seeded item exists");
assert!(!before.frozen(), "frozen flag should start false");
// Sanity: before migration, the projection's legacy fallback maps
// raw `"7_frozen"` → `Stage::Backlog` (frozen state is lost without the
// migration's resume_to write).
let before = read_item(story_id).expect("legacy 7_frozen should still project");
assert!(
matches!(before.stage(), crate::pipeline_state::Stage::Backlog),
"raw 7_frozen should fall back to Backlog before migration; got {:?}",
before.stage()
);
migrate_legacy_stage_strings();
let after = read_item(story_id).expect("item must still exist after migration");
assert!(
matches!(after.stage(), crate::pipeline_state::Stage::Backlog),
"7_frozen should collapse to Backlog: got {:?}",
after.stage()
);
assert!(
after.frozen(),
"frozen flag should be set after 7_frozen migration"
);
match after.stage() {
crate::pipeline_state::Stage::Frozen { resume_to } => {
assert!(
matches!(**resume_to, crate::pipeline_state::Stage::Backlog),
"resume_to should default to Backlog for migrated 7_frozen items"
);
}
other => panic!("7_frozen should migrate to Stage::Frozen; got {other:?}"),
}
}
#[test]
@@ -390,7 +403,6 @@ mod stage_migration_tests {
None,
None,
None,
None,
);
seed_with_raw_stage("9521_needs_migration", "2_current");