Editor: reset confirmation must list every key the reset wipes

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 <noreply@anthropic.com>
This commit is contained in:
digital-garbage
2026-06-15 09:28:43 +03:00
co-authored by Claude Opus 4.8
parent ec8b5005b9
commit 0b72037c5b
4 changed files with 36 additions and 6 deletions
+8
View File
@@ -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
+13
View File
@@ -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 (
+13 -6
View File
@@ -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];
}
+2
View File
@@ -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}. "