From 979492449efbd7cbf5f343bfc8573dde04b4cfe0 Mon Sep 17 00:00:00 2001 From: dave Date: Fri, 15 May 2026 22:28:53 +0000 Subject: [PATCH] =?UTF-8?q?huskies:=20merge=201105=20bug=20Freeze=20from?= =?UTF-8?q?=20Backlog=20stores=20wrong=20resume=5Fto=20=E2=80=94=20Unfreez?= =?UTF-8?q?e=20restores=20to=20Coding=20instead=20of=20Backlog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/pipeline_state/apply.rs | 10 +++++++ server/src/pipeline_state/tests.rs | 46 ++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/server/src/pipeline_state/apply.rs b/server/src/pipeline_state/apply.rs index e278bc16..2e5861c3 100644 --- a/server/src/pipeline_state/apply.rs +++ b/server/src/pipeline_state/apply.rs @@ -78,6 +78,16 @@ pub fn apply_transition( super::Stage::Rejected { reason, .. } | super::Stage::Blocked { reason } => { crate::crdt_state::set_resume_to_raw(story_id, reason); } + // Story 1105: write the resume target so read-back can reconstruct the + // correct variant. Without this, the register is stale (or empty) and + // the deserialiser falls back to Coding regardless of where the story + // was when it was frozen. + super::Stage::Frozen { resume_to } => { + crate::crdt_state::set_resume_to_raw(story_id, resume_to.dir_name()); + } + super::Stage::ReviewHold { resume_to, .. } => { + crate::crdt_state::set_resume_to_raw(story_id, resume_to.dir_name()); + } _ => {} } diff --git a/server/src/pipeline_state/tests.rs b/server/src/pipeline_state/tests.rs index b452372e..449c93ab 100644 --- a/server/src/pipeline_state/tests.rs +++ b/server/src/pipeline_state/tests.rs @@ -656,6 +656,52 @@ fn freeze_transitions_to_frozen_variant_with_resume_to() { ); } +// ── Story 1105: Freeze from Backlog round-trip ─────────────────────────────── + +/// Regression test (story 1105): freezing a Backlog story must store +/// `resume_to = Backlog` in the CRDT register so that unfreeze returns the +/// story to Backlog, not to Coding. +#[test] +fn freeze_from_backlog_round_trips_resume_to_backlog() { + crate::crdt_state::init_for_test(); + crate::db::ensure_content_store(); + + let story_id = "991105_freeze_backlog_roundtrip"; + crate::db::write_item_with_content( + story_id, + "1_backlog", + "---\nname: Freeze Backlog Round-trip\n---\n# Story\n", + crate::db::ItemMeta::named("Freeze Backlog Round-trip"), + ); + + let item = read_typed(story_id).unwrap().unwrap(); + assert!( + matches!(item.stage, Stage::Backlog), + "expected Backlog, got {:?}", + item.stage + ); + + super::apply::transition_to_frozen(story_id).expect("freeze should succeed"); + + let item = read_typed(story_id).unwrap().unwrap(); + match &item.stage { + Stage::Frozen { resume_to } => assert!( + matches!(**resume_to, Stage::Backlog), + "resume_to should be Backlog after freezing from Backlog; got {resume_to:?}" + ), + other => panic!("expected Stage::Frozen after freeze; got {other:?}"), + } + + super::apply::transition_to_unfrozen(story_id).expect("unfreeze should succeed"); + + let item = read_typed(story_id).unwrap().unwrap(); + assert!( + matches!(item.stage, Stage::Backlog), + "unfreeze should restore to Backlog, not Coding; got {:?}", + item.stage + ); +} + // ── Story 868: MergeFailure regression ───────────────────────────── /// Regression test (story 868): applying `PipelineEvent::MergeFailed` to a story