diff --git a/docs/superpowers/plans/2026-07-01-scene-layer-seed-simplification.md b/docs/superpowers/plans/2026-07-01-scene-layer-seed-simplification.md new file mode 100644 index 0000000..30e5759 --- /dev/null +++ b/docs/superpowers/plans/2026-07-01-scene-layer-seed-simplification.md @@ -0,0 +1,128 @@ +# Scene Layer Seed Simplification Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Make `SxCPSceneLayerSeedOptions` use the visible node seed for `random` mode so prompt clothing/content choices are reproducible from the workflow. + +**Architecture:** Keep the existing scene seed pipeline. Change only layer seed option resolution so `random` no longer replaces the visible seed with a hidden `SystemRandom` value. Preserve existing `seed_trace` and scene pair metadata behavior. + +**Tech Stack:** Python ComfyUI custom nodes, existing smoke tests in `tools/prompt_smoke.py`. + +--- + +### Task 1: Add Failing Smoke Coverage + +**Files:** +- Modify: `tools/prompt_smoke.py` + +- [ ] **Step 1: Add assertions to `smoke_node_scene_chain_registration`** + +Add a small check after the node registry assertions or near the existing scene layer seed checks: + +```python + random_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build( + "softcore_branch", + "random", + 123456789, + "content_pose", + "same_for_all_rows", + "replace_layer", + )[0] + fixed_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build( + "softcore_branch", + "fixed", + 123456789, + "content_pose", + "same_for_all_rows", + "replace_layer", + )[0] + random_seed_item = json.loads(random_seed_options)["items"][0] + fixed_seed_item = json.loads(fixed_seed_options)["items"][0] + _expect(random_seed_item.get("seed") == 123456789, "Scene random layer seed should use the visible node seed") + _expect( + random_seed_item.get("seed") == fixed_seed_item.get("seed"), + "Scene random and fixed layer seeds should match when the visible seed matches", + ) +``` + +- [ ] **Step 2: Run focused smoke test and verify it fails** + +Run: + +```bash +python tools/prompt_smoke.py --case node_scene_chain_registration --quiet +``` + +Expected: FAIL with `Scene random layer seed should use the visible node seed`. + +### Task 2: Make Visible Seed Authoritative + +**Files:** +- Modify: `node_scene.py` + +- [ ] **Step 1: Remove hidden random replacement** + +In `_layer_seed_options_json`, remove the `SystemRandom` override: + +```python + resolved_seed = max(0, min(0xFFFFFFFF, int(seed))) +``` + +The `if seed_mode == "random": ...` branch should be deleted. Leave `seed_mode` in metadata unchanged for compatibility. + +- [ ] **Step 2: Run focused smoke test and verify it passes** + +Run: + +```bash +python tools/prompt_smoke.py --case node_scene_chain_registration --quiet +``` + +Expected: `OK: smoke passed (1 cases).` + +### Task 3: Verify and Commit + +**Files:** +- Verify: `node_scene.py` +- Verify: `tools/prompt_smoke.py` +- Commit: implementation and plan file + +- [ ] **Step 1: Compile changed Python files** + +Run: + +```bash +python -m py_compile node_scene.py tools/prompt_smoke.py +``` + +Expected: exit code 0. + +- [ ] **Step 2: Run full smoke suite** + +Run: + +```bash +python tools/prompt_smoke.py --quiet +``` + +Expected: either all smoke cases pass, or only the existing unrelated `krea2_prompt_guide_policy` methodology-memory failure remains. + +- [ ] **Step 3: Review diff** + +Run: + +```bash +git --git-dir=.git-real --work-tree=. diff --stat +git --git-dir=.git-real --work-tree=. diff -- node_scene.py tools/prompt_smoke.py +``` + +Expected: only the random seed behavior and its smoke coverage changed. + +- [ ] **Step 4: Commit implementation** + +Run: + +```bash +git --git-dir=.git-real --work-tree=. add node_scene.py tools/prompt_smoke.py docs/superpowers/plans/2026-07-01-scene-layer-seed-simplification.md +git --git-dir=.git-real --work-tree=. commit -m "Make scene layer random seeds reproducible" +``` diff --git a/node_scene.py b/node_scene.py index f0ed5a7..ad7234d 100644 --- a/node_scene.py +++ b/node_scene.py @@ -446,8 +446,6 @@ def _layer_seed_options_json( if combine_mode == "replace_layer": items = [item for item in _seed_option_items(seed_options) if item.get("layer") != layer] resolved_seed = max(0, min(0xFFFFFFFF, int(seed))) - if seed_mode == "random": - resolved_seed = random.SystemRandom().randint(0, 0xFFFFFFFF) items.append( { "layer": layer, diff --git a/tools/prompt_smoke.py b/tools/prompt_smoke.py index c6c0490..1630b2a 100644 --- a/tools/prompt_smoke.py +++ b/tools/prompt_smoke.py @@ -15980,6 +15980,32 @@ def smoke_node_scene_chain_registration() -> None: _expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry") nodes = sxcp_nodes.NODE_CLASS_MAPPINGS + random_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build( + "softcore_branch", + "random", + 123456789, + "content_pose", + "same_for_all_rows", + "replace_layer", + )[0] + fixed_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build( + "softcore_branch", + "fixed", + 123456789, + "content_pose", + "same_for_all_rows", + "replace_layer", + )[0] + random_seed_item = json.loads(random_seed_options)["items"][0] + fixed_seed_item = json.loads(fixed_seed_options)["items"][0] + _expect( + random_seed_item.get("seed") == 123456789, + "Scene random layer seed should use the visible node seed", + ) + _expect( + random_seed_item.get("seed") == fixed_seed_item.get("seed"), + "Scene random and fixed layer seeds should match when the visible seed matches", + ) scene, start_summary, _start_metadata = nodes["SxCPSceneStart"]().build( 1, 41,