From 0b72037c5bc2f238ee1cab8b28735a573ed196ab Mon Sep 17 00:00:00 2001 From: digital-garbage <133010426+digital-garbage@users.noreply.github.com> Date: Mon, 15 Jun 2026 09:28:43 +0300 Subject: [PATCH] Editor: reset confirmation must list every key the reset wipes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reset confirmation read per-scene scene_refinement_keys (which drops keys via its divergence fallback), but the backend _v2_reset_prompt_keys wipes the scene-count-independent pool (prompt_scene_shortcut_keys all_keys). So a second key being trained could be reset without ever being listed in the confirmation — a silent wipe. Expose refinement_key_pool on the preview (= refinement_keys_in_text over the whole prompt, which equals the backend's all_keys) and list the reset confirmation from it. Verified pool == backend reset set, including the divergence case where the per-scene list showed nothing. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 8 ++++++++ movie_editor/backend/bridge.py | 13 +++++++++++++ movie_editor/frontend/store.js | 19 +++++++++++++------ movie_editor/server.py | 2 ++ 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe57cd75..b802bcf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ## [Unreleased] +### Fixed +- "Reset Studio session" silently wiped keys it never listed. The confirmation read the per-scene + `scene_refinement_keys` (which drops keys via its divergence fallback), while the backend reset + wipes the scene-count-independent **pool** (`prompt_scene_shortcut_keys.all_keys` — every key + fired in the prompt). So a second key you were training could be cleared without ever appearing + in the confirmation. The preview now exposes `refinement_key_pool` (mirrors the backend reset + exactly) and the confirmation lists from it, so what's shown is precisely what gets wiped. + ## [3.0.2] - 2026-06-15 ### Fixed diff --git a/movie_editor/backend/bridge.py b/movie_editor/backend/bridge.py index 2f21f8d2..fb090aca 100644 --- a/movie_editor/backend/bridge.py +++ b/movie_editor/backend/bridge.py @@ -102,6 +102,19 @@ def scene_refinement_keys(prompt: str, scene_count: int, default_key: str = "def return out +def refinement_key_pool(prompt: str) -> list: + """Every non-default refinement key whose shortcut fires anywhere in the prompt — the exact + set a Studio session reset will wipe. Scene-count independent, so it mirrors the backend + `_v2_reset_prompt_keys` (which clears `prompt_scene_shortcut_keys(...).all_keys`), NOT the + per-scene `scene_refinement_keys` (whose divergence fallback can drop a key the reset still + wipes). Used so the reset confirmation lists exactly what will be cleared.""" + keys_in_text = _funpack_attr("conditioning", "refinement_keys_in_text") + try: + return sorted(k for k in keys_in_text(str(prompt or "")) if k) + except Exception: + return [] + + def validate_generation_prompt(full, target) -> dict: """Build the generation prompt and run fingerprint; track changes since last queue.""" from .timeline import ( diff --git a/movie_editor/frontend/store.js b/movie_editor/frontend/store.js index ea585b68..62de325f 100644 --- a/movie_editor/frontend/store.js +++ b/movie_editor/frontend/store.js @@ -2811,16 +2811,23 @@ ); } - // Every refinement key a reset will wipe: the project/default key plus every non-default - // key whose shortcut fires in the current prompt (those keys also get TRAINED per scene, so - // a reset must clear them too). Mirrors the backend _v2_reset_prompt_keys union. + // Every refinement key a reset will wipe: the project/default key plus every non-default key + // whose shortcut fires anywhere in the prompt. Uses the scene-count-independent + // refinement_key_pool, which mirrors the backend _v2_reset_prompt_keys EXACTLY — the per-scene + // scene_refinement_keys can drop a key via its divergence fallback, which would under-report and + // let the reset silently wipe a key the user wasn't warned about. Falls back to the per-scene + // list only for an older preview payload without the pool. function sessionResetKeys() { const pv = state.preview || {}; const defKey = (state.project?.refinement_key || "default").trim() || "default"; const out = new Set([defKey]); - (pv.scene_refinement_keys || []).forEach((rk) => { - if (rk && !rk.uses_default) (rk.keys || []).forEach((k) => { if (k) out.add(k); }); - }); + if (Array.isArray(pv.refinement_key_pool)) { + pv.refinement_key_pool.forEach((k) => { if (k) out.add(k); }); + } else { + (pv.scene_refinement_keys || []).forEach((rk) => { + if (rk && !rk.uses_default) (rk.keys || []).forEach((k) => { if (k) out.add(k); }); + }); + } return [...out]; } diff --git a/movie_editor/server.py b/movie_editor/server.py index 356e02b7..461c3f89 100644 --- a/movie_editor/server.py +++ b/movie_editor/server.py @@ -1144,6 +1144,7 @@ if web is not None and PromptServer is not None: result["parsed_scenes"] = len(parsed.get("scenes", [])) result["scene_refinement_keys"] = bridge.scene_refinement_keys( prompt, len(parsed.get("scenes", [])), p.refinement_key) + result["refinement_key_pool"] = bridge.refinement_key_pool(prompt) except Exception as e: # noqa: BLE001 result["parse_error"] = str(e) return web.json_response(result) @@ -1161,6 +1162,7 @@ if web is not None and PromptServer is not None: result["parsed_scenes"] = got result["scene_refinement_keys"] = bridge.scene_refinement_keys( prompt, got, p.refinement_key) + result["refinement_key_pool"] = bridge.refinement_key_pool(prompt) if expected and got != expected: result["warning"] = ( f"Studio split produced {got} scene(s) but the timeline has {expected}. "