Compare commits
2 Commits
5e218e2d33
...
38979a79a1
| Author | SHA1 | Date | |
|---|---|---|---|
| 38979a79a1 | |||
| 39782ce843 |
@@ -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"
|
||||
```
|
||||
@@ -0,0 +1,68 @@
|
||||
# Scene Layer Seed Design
|
||||
|
||||
## Problem
|
||||
|
||||
`SxCPSceneLayerSeedOptions` currently has a confusing double-seed behavior. When
|
||||
`seed_mode=random`, the node displays one seed in the widget, but the build
|
||||
method replaces it with a hidden `SystemRandom` value. The generated prompt may
|
||||
therefore use a clothing/content seed that is not visible in the workflow after
|
||||
the run.
|
||||
|
||||
This makes softcore branch clothing hard to reproduce. In a scene pair, the
|
||||
woman's softcore outfit is selected from the softcore branch content seed, then
|
||||
the hardcore branch may inherit that outfit through clothing continuity. If the
|
||||
resolved content seed is hidden, the user cannot reliably answer which seed
|
||||
picked the clothes.
|
||||
|
||||
## Approved Behavior
|
||||
|
||||
The visible `seed` field in `SxCPSceneLayerSeedOptions` is the authoritative
|
||||
seed. `seed_mode=random` must no longer replace it with a hidden random value.
|
||||
For layer-seed behavior:
|
||||
|
||||
- `follow_global`: use the scene seed.
|
||||
- `fixed`: use the visible node `seed`.
|
||||
- `random`: use the visible node `seed`.
|
||||
- `disabled`: apply no layer seed.
|
||||
|
||||
The `random` option can remain in the UI for compatibility, but it behaves like
|
||||
an explicit seed mode. If a user wants a new random value, they should randomize
|
||||
the visible seed field in the node or use ComfyUI's widget randomization.
|
||||
|
||||
## Seed Reporting
|
||||
|
||||
The existing scene `seed_trace` remains the source of truth for resolved prompt
|
||||
axis seeds. For softcore branch clothing, the relevant trace is usually:
|
||||
|
||||
- layer: `softcore_branch`
|
||||
- reroll axis: `content` or `content_pose`
|
||||
- affected axes: `content_seed`, and for `content_pose` also `pose_seed` and
|
||||
`role_seed`
|
||||
|
||||
Scene pair metadata should continue to include the full softcore and hardcore
|
||||
scene chain so `seed_trace` is preserved in `metadata_json` and
|
||||
`scene_metadata_json`.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
This change does not introduce a separate `clothing_seed` axis. Clothing is still
|
||||
part of the existing content axis. Splitting clothing from content would be a
|
||||
larger behavior change and should be handled separately if needed.
|
||||
|
||||
This change does not alter image sampler seeds. KSampler/image seeds remain
|
||||
separate from prompt layer seeds.
|
||||
|
||||
## Testing
|
||||
|
||||
Add or update smoke coverage for `SxCPSceneLayerSeedOptions`:
|
||||
|
||||
- A `random` mode layer seed emits the visible widget seed in its metadata.
|
||||
- A `fixed` mode layer seed emits the same seed as `random` when given the same
|
||||
visible seed.
|
||||
- A softcore branch `content` or `content_pose` seed appears in the generated
|
||||
softcore row `seed_config`.
|
||||
- The generated softcore outfit can be traced to that visible content seed.
|
||||
|
||||
Existing scene pair tests should continue to verify that softcore branch seeds do
|
||||
not leak into hardcore pose/content seeds unless explicitly applied to the hard
|
||||
branch.
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user