From faa1b7230728add141eab074d0d57782c55887bf Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Tue, 6 Oct 2026 10:37:37 -0400 Subject: [PATCH] fix(family): undoing the decision that created an idea leaves no applicability or scope behind (#4989) When the undone decision had no recorded before-state, the synthesized prior copied the canon idea's current applies_when and platforms into a retired row. Before that decision the record was not an idea, so the restored state now has neither. Caught by test_undoing_a_promotion_restores_the_prior_state_and_keeps_judged_rows (run 8281). Co-Authored-By: Claude Opus 5.5 --- src/scribe/services/family.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/scribe/services/family.py b/src/scribe/services/family.py index 88f3d36a..2be70237 100644 --- a/src/scribe/services/family.py +++ b/src/scribe/services/family.py @@ -620,9 +620,12 @@ async def undo(user_id: int, decision_id: int, *, reason: str, decided_via: str raise ValueError(f"decision {decision_id} cannot be undone: {why}") idea = await session.get(FamilyIdea, target.idea_id) before = await _snapshot(session, idea) + # No recorded prior state means the decision CREATED the idea: before + # it, the record had no applicability test and no scope. It comes back + # as retired, with neither, rather than being deleted with its log. prior = target.before or { - "status": "retired", "applies_when": idea.applies_when or "", - "canon_version": idea.canon_version, "platforms": before["platforms"], + "status": "retired", "applies_when": "", + "canon_version": idea.canon_version, "platforms": [], } if prior["status"] == "canon" and not (prior.get("applies_when") or "").strip(): raise ValueError("the recorded prior state is canon with no 'applies when'")