From eddeccaf68f1dcd012907527bea2cb11163a92aa Mon Sep 17 00:00:00 2001 From: digital-garbage <133010426+digital-garbage@users.noreply.github.com> Date: Sat, 1 Aug 2026 18:31:39 +0300 Subject: [PATCH] fix: restore the GuideEntry dataclass, repairing the i2v guide stack 7106db0 added scene_anchor_media_refs by writing it over the "@dataclass / class GuideEntry:" header lines. The class body survived as unreachable code after that function's return, so GuideEntry was referenced in three places but never defined. Effect: every code path that switches the per-scene i2v guide stack on raised NameError -- build_scene_guides_payload and continuity_media_refs both died as soon as guide_settings.stack_enabled was true. It went unnoticed for seven weeks because the stack defaults off and no test covered it. Fix moves the stranded body back out of scene_anchor_media_refs and restores the two lost header lines. The body is byte-identical to b9f6e74; only its position and header changed. scene_anchor_media_refs is untouched. Adds two regression tests, both verified to fail against the unfixed file. Tests: 22 failed / 462 passed (baseline 22/460 -- +2 passing, 0 new failures). Co-Authored-By: Claude Opus 5 --- movie_editor/backend/timeline.py | 32 ++++++++++++++++------------- movie_editor/tests/test_timeline.py | 28 +++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/movie_editor/backend/timeline.py b/movie_editor/backend/timeline.py index ef2a3fc6..23cd1785 100644 --- a/movie_editor/backend/timeline.py +++ b/movie_editor/backend/timeline.py @@ -234,20 +234,8 @@ def continuity_media_refs(full: Project, target: Project) -> list[str]: return list(dict.fromkeys(refs)) -def scene_anchor_media_refs(target: Project) -> list[str]: - """Media-bin ids required as i2v anchors on generative scenes.""" - refs: list[str] = [] - for sc in target.scenes: - if sc.excluded or is_video_clip(sc): - continue - src = sc.source - if not src: - continue - stype = source_type(sc) - ref = src.media_ref - if ref and stype in ("image", "generated_frame", "mixed", "anchor_guide"): - refs.append(ref) - return list(dict.fromkeys(refs)) +@dataclass +class GuideEntry: """Optional per-scene guide (only used when project guide_settings.stack_enabled).""" enabled: bool = True source: str = "template" # "template" | "scene" | "image" @@ -274,6 +262,22 @@ def scene_anchor_media_refs(target: Project) -> list[str]: return asdict(self) +def scene_anchor_media_refs(target: Project) -> list[str]: + """Media-bin ids required as i2v anchors on generative scenes.""" + refs: list[str] = [] + for sc in target.scenes: + if sc.excluded or is_video_clip(sc): + continue + src = sc.source + if not src: + continue + stype = source_type(sc) + ref = src.media_ref + if ref and stype in ("image", "generated_frame", "mixed", "anchor_guide"): + refs.append(ref) + return list(dict.fromkeys(refs)) + + def is_mixed_source(scene: Scene) -> bool: return (scene.source.type or "carry") == "mixed" diff --git a/movie_editor/tests/test_timeline.py b/movie_editor/tests/test_timeline.py index 594e3029..1a6a953e 100644 --- a/movie_editor/tests/test_timeline.py +++ b/movie_editor/tests/test_timeline.py @@ -239,6 +239,34 @@ def test_guide_settings_default_off(): assert gs["accumulate_prior"] is False +def test_guide_entry_is_importable_and_round_trips(): + """GuideEntry's class header was once overwritten by a neighbouring function, + leaving its body stranded as dead code after a `return`. Nothing caught it because + the guide stack defaults off, so no other test reaches the code that uses it.""" + from movie_editor.backend.timeline import GuideEntry + + g = GuideEntry.from_dict({"enabled": True, "source": "image", "media_ref": "img1"}) + assert g.enabled is True and g.source == "image" and g.media_ref == "img1" + assert g.to_dict()["strength"] == 0.35 + + +def test_guide_stack_enabled_paths_do_not_raise(): + """Both consumers of GuideEntry only run when the stack is switched on — the exact + configuration that used to raise NameError.""" + p = _project( + guide_settings={"stack_enabled": True}, + scenes=[ + {"id": "s1", "text": "a", "source": {"type": "image", "media_ref": "img1"}}, + {"id": "s2", "text": "b", "source": {"type": "mixed", "media_ref": "img2"}, + "guides": [{"enabled": True, "source": "image", "media_ref": "img3"}]}, + ], + ) + payload = build_scene_guides_payload(p) + assert payload["stack_enabled"] is True + assert payload["scenes"][1][0]["media_ref"] == "img3" + assert "img3" in continuity_media_refs(p, p) + + def test_continuity_defaults_auto_on(): cs = normalize_continuity_settings({}) assert cs["auto_enabled"] is True