Add multi-seed route simulation sweep

This commit is contained in:
2026-06-27 19:58:11 +02:00
parent 4a3610fbc9
commit 1ca9c95bfe
6 changed files with 111 additions and 20 deletions
+13 -13
View File
@@ -106,7 +106,7 @@
"item_templates": [ "item_templates": [
"{tease_act} in {position}, with {touch_detail}, {clothing_detail}, and {mood_detail}", "{tease_act} in {position}, with {touch_detail}, {clothing_detail}, and {mood_detail}",
"{position} featuring {tease_act}, {body_contact}, {touch_detail}, and {visibility}", "{position} featuring {tease_act}, {body_contact}, {touch_detail}, and {visibility}",
"hardcore foreplay setup: {tease_act}, {clothing_detail}, {face_detail}, and {body_contact}", "hardcore foreplay setup with {tease_act}, {clothing_detail}, {face_detail}, and {body_contact}",
"{tease_act} on {surface}, with {touch_detail}, {mood_detail}, and {visibility}", "{tease_act} on {surface}, with {touch_detail}, {mood_detail}, and {visibility}",
"{position} while {tease_act}, with {face_detail}, {clothing_detail}, and {touch_detail}" "{position} while {tease_act}, with {face_detail}, {clothing_detail}, and {touch_detail}"
], ],
@@ -230,7 +230,7 @@
"{manual_act} in {position}, with {manual_detail}, {body_contact}, and {visibility}", "{manual_act} in {position}, with {manual_detail}, {body_contact}, and {visibility}",
"{position} featuring {manual_act}, {hand_detail}, {reaction_detail}, and {visibility}", "{position} featuring {manual_act}, {hand_detail}, {reaction_detail}, and {visibility}",
"{manual_act} on {surface}, with {manual_detail}, {hand_detail}, and {reaction_detail}", "{manual_act} on {surface}, with {manual_detail}, {hand_detail}, and {reaction_detail}",
"manual stimulation setup: {position}, {manual_act}, {body_contact}, and {visibility}", "manual stimulation setup with {position}, {manual_act}, {body_contact}, and {visibility}",
"{position} while {manual_act}, with {hand_detail}, {manual_detail}, and {reaction_detail}" "{position} while {manual_act}, with {hand_detail}, {manual_detail}, and {reaction_detail}"
], ],
"item_axes": { "item_axes": {
@@ -339,7 +339,7 @@
"{worship_act} in {position}, with {touch_detail}, {face_detail}, and {visibility}", "{worship_act} in {position}, with {touch_detail}, {face_detail}, and {visibility}",
"{position} featuring {worship_act}, {body_contact}, {touch_detail}, and {reaction_detail}", "{position} featuring {worship_act}, {body_contact}, {touch_detail}, and {reaction_detail}",
"{worship_act} on {surface}, with {body_contact}, {face_detail}, and {visibility}", "{worship_act} on {surface}, with {body_contact}, {face_detail}, and {visibility}",
"body worship setup: {position}, {worship_act}, {touch_detail}, and {reaction_detail}", "body worship setup with {position}, {worship_act}, {touch_detail}, and {reaction_detail}",
"{position} while {worship_act}, with {body_contact}, {face_detail}, and {visibility}" "{position} while {worship_act}, with {body_contact}, {face_detail}, and {visibility}"
], ],
"item_axes": { "item_axes": {
@@ -452,7 +452,7 @@
"{transition_act} in {position}, with {clothing_detail}, {hand_detail}, and {visibility}", "{transition_act} in {position}, with {clothing_detail}, {hand_detail}, and {visibility}",
"{position} featuring {transition_act}, {body_contact}, {clothing_detail}, and {movement_detail}", "{position} featuring {transition_act}, {body_contact}, {clothing_detail}, and {movement_detail}",
"{transition_act} on {surface}, with {hand_detail}, {body_contact}, and {visibility}", "{transition_act} on {surface}, with {hand_detail}, {body_contact}, and {visibility}",
"position transition: {position}, {transition_act}, {movement_detail}, and {clothing_detail}", "position transition with {position}, {transition_act}, {movement_detail}, and {clothing_detail}",
"{position} while {transition_act}, with {hand_detail}, {body_contact}, and {visibility}" "{position} while {transition_act}, with {hand_detail}, {body_contact}, and {visibility}"
], ],
"item_axes": { "item_axes": {
@@ -561,7 +561,7 @@
"{control_act} in {position}, with {hand_detail}, {body_contact}, and {visibility}", "{control_act} in {position}, with {hand_detail}, {body_contact}, and {visibility}",
"{position} featuring {control_act}, {power_detail}, {hand_detail}, and {reaction_detail}", "{position} featuring {control_act}, {power_detail}, {hand_detail}, and {reaction_detail}",
"{control_act} on {surface}, with {body_contact}, {power_detail}, and {visibility}", "{control_act} on {surface}, with {body_contact}, {power_detail}, and {visibility}",
"consensual control setup: {position}, {control_act}, {hand_detail}, and {reaction_detail}", "consensual control setup with {position}, {control_act}, {hand_detail}, and {reaction_detail}",
"{position} while {control_act}, with {power_detail}, {body_contact}, and {visibility}" "{position} while {control_act}, with {power_detail}, {body_contact}, and {visibility}"
], ],
"item_axes": { "item_axes": {
@@ -675,7 +675,7 @@
"{performance_act} in {position}, with {presentation_detail}, {hand_detail}, and {visibility}", "{performance_act} in {position}, with {presentation_detail}, {hand_detail}, and {visibility}",
"{position} featuring {performance_act}, {camera_detail}, {presentation_detail}, and {reaction_detail}", "{position} featuring {performance_act}, {camera_detail}, {presentation_detail}, and {reaction_detail}",
"{performance_act} on {surface}, with {hand_detail}, {camera_detail}, and {visibility}", "{performance_act} on {surface}, with {hand_detail}, {camera_detail}, and {visibility}",
"creator-performance setup: {position}, {performance_act}, {presentation_detail}, and {reaction_detail}", "creator-performance setup with {position}, {performance_act}, {presentation_detail}, and {reaction_detail}",
"{position} while {performance_act}, with {camera_detail}, {hand_detail}, and {visibility}" "{position} while {performance_act}, with {camera_detail}, {hand_detail}, and {visibility}"
], ],
"item_axes": { "item_axes": {
@@ -783,7 +783,7 @@
"{coordination_act} with {arrangement}, {touch_detail}, {reaction_detail}, and {visibility}", "{coordination_act} with {arrangement}, {touch_detail}, {reaction_detail}, and {visibility}",
"{arrangement} featuring {coordination_act}, {body_contact}, {watching_detail}, and {visibility}", "{arrangement} featuring {coordination_act}, {body_contact}, {watching_detail}, and {visibility}",
"{coordination_act} on {surface}, with {touch_detail}, {watching_detail}, and {body_contact}", "{coordination_act} on {surface}, with {touch_detail}, {watching_detail}, and {body_contact}",
"group coordination setup: {arrangement}, {coordination_act}, {watching_detail}, and {visibility}", "group coordination setup with {arrangement}, {coordination_act}, {watching_detail}, and {visibility}",
"{arrangement} while {coordination_act}, with {touch_detail}, {reaction_detail}, and {visibility}" "{arrangement} while {coordination_act}, with {touch_detail}, {reaction_detail}, and {visibility}"
], ],
"item_axes": { "item_axes": {
@@ -889,7 +889,7 @@
"{aftercare_act} in {position}, with {cleanup_detail}, {body_contact}, and {visibility}", "{aftercare_act} in {position}, with {cleanup_detail}, {body_contact}, and {visibility}",
"{position} featuring {aftercare_act}, {touch_detail}, {cleanup_detail}, and {reaction_detail}", "{position} featuring {aftercare_act}, {touch_detail}, {cleanup_detail}, and {reaction_detail}",
"{aftercare_act} on {surface}, with {body_contact}, {touch_detail}, and {visibility}", "{aftercare_act} on {surface}, with {body_contact}, {touch_detail}, and {visibility}",
"post-sex aftermath setup: {position}, {aftercare_act}, {cleanup_detail}, and {reaction_detail}", "post-sex aftermath setup with {position}, {aftercare_act}, {cleanup_detail}, and {reaction_detail}",
"{position} while {aftercare_act}, with {touch_detail}, {body_contact}, and {visibility}" "{position} while {aftercare_act}, with {touch_detail}, {body_contact}, and {visibility}"
], ],
"item_axes": { "item_axes": {
@@ -1178,7 +1178,7 @@
"{oral_act} on {surface}, {hand_detail}, {mouth_detail}, and {climax_hint}", "{oral_act} on {surface}, {hand_detail}, {mouth_detail}, and {climax_hint}",
"{angle} view of {oral_act}, with {visibility}, {body_contact}, and {expression_detail}", "{angle} view of {oral_act}, with {visibility}, {body_contact}, and {expression_detail}",
"{position} while {oral_act}, with {saliva_detail}, {hand_detail}, and {climax_hint}", "{position} while {oral_act}, with {saliva_detail}, {hand_detail}, and {climax_hint}",
"explicit mouth-to-genitals pose: {oral_act}, {mouth_detail}, {body_contact}, and {visibility}" "explicit mouth-to-genitals pose with {oral_act}, {mouth_detail}, {body_contact}, and {visibility}"
], ],
"item_axes": { "item_axes": {
"angle": [ "angle": [
@@ -1330,7 +1330,7 @@
}, },
"{position} featuring {outer_act}, {body_contact}, {texture_detail}, seen from a {angle} view", "{position} featuring {outer_act}, {body_contact}, {texture_detail}, seen from a {angle} view",
"{angle} view of {outer_act}, with {visibility}, {contact_detail}, and {expression_detail}", "{angle} view of {outer_act}, with {visibility}, {contact_detail}, and {expression_detail}",
"explicit non-penetrative sex pose: {outer_act}, {position}, {contact_detail}, and {visibility}", "explicit non-penetrative sex pose with {outer_act}, {position}, {contact_detail}, and {visibility}",
"{outer_act} on {surface}, with {hand_detail}, {body_contact}, and {texture_detail}", "{outer_act} on {surface}, with {hand_detail}, {body_contact}, and {texture_detail}",
"{position} while {outer_act}, with {texture_detail}, {hand_detail}, and {visibility}" "{position} while {outer_act}, with {texture_detail}, {hand_detail}, and {visibility}"
], ],
@@ -1479,7 +1479,7 @@
"{double_act} on {surface}, with {leg_detail}, {intensity}, and {climax_hint}", "{double_act} on {surface}, with {leg_detail}, {intensity}, and {climax_hint}",
"{angle} view of {double_act}, {body_arrangement}, {mouth_detail}, and {visibility}", "{angle} view of {double_act}, {body_arrangement}, {mouth_detail}, and {visibility}",
"{anal_act} with {thrust_detail}, {hand_detail}, {body_contact}, and {climax_hint}", "{anal_act} with {thrust_detail}, {hand_detail}, {body_contact}, and {climax_hint}",
"explicit double-contact sex pose: {double_act}, {leg_detail}, {visibility}, and {intensity}" "explicit double-contact sex pose with {double_act}, {leg_detail}, {visibility}, and {intensity}"
], ],
"item_axes": { "item_axes": {
"anal_act": [ "anal_act": [
@@ -1698,7 +1698,7 @@
"{threesome_act} with {body_arrangement}, {oral_detail}, {penetration_detail}, and {visibility}", "{threesome_act} with {body_arrangement}, {oral_detail}, {penetration_detail}, and {visibility}",
"{body_arrangement} while {threesome_act}, with {hand_detail}, {mouth_detail}, and {climax_hint}", "{body_arrangement} while {threesome_act}, with {hand_detail}, {mouth_detail}, and {climax_hint}",
"{angle} threesome view featuring {threesome_act}, {body_contact}, {penetration_detail}, and {visibility}", "{angle} threesome view featuring {threesome_act}, {body_contact}, {penetration_detail}, and {visibility}",
"hardcore threesome pose: {threesome_act}, {body_arrangement}, {oral_detail}, and {climax_hint}", "hardcore threesome pose with {threesome_act}, {body_arrangement}, {oral_detail}, and {climax_hint}",
"{threesome_act} on {surface}, with {hand_detail}, {body_contact}, and {visibility}", "{threesome_act} on {surface}, with {hand_detail}, {body_contact}, and {visibility}",
"{angle} view of {body_arrangement}, {penetration_detail}, {mouth_detail}, and {intensity}", "{angle} view of {body_arrangement}, {penetration_detail}, {mouth_detail}, and {intensity}",
"three-body explicit sex pose with {threesome_act}, {oral_detail}, {hand_detail}, and {visibility}", "three-body explicit sex pose with {threesome_act}, {oral_detail}, {hand_detail}, and {visibility}",
@@ -1885,7 +1885,7 @@
"{group_act} with {arrangement}, {contact_detail}, {fluid_detail}, and {visibility}", "{group_act} with {arrangement}, {contact_detail}, {fluid_detail}, and {visibility}",
"{arrangement} featuring {group_act}, {oral_detail}, {penetration_detail}, and {intensity}", "{arrangement} featuring {group_act}, {oral_detail}, {penetration_detail}, and {intensity}",
"{angle} group-sex view with {group_act}, {contact_detail}, {climax_detail}, and {visibility}", "{angle} group-sex view with {group_act}, {contact_detail}, {climax_detail}, and {visibility}",
"hardcore orgy pose: {arrangement}, {group_act}, {oral_detail}, and {fluid_detail}", "hardcore orgy pose with {arrangement}, {group_act}, {oral_detail}, and {fluid_detail}",
"{group_act} on {surface}, with {penetration_detail}, {contact_detail}, and {visibility}", "{group_act} on {surface}, with {penetration_detail}, {contact_detail}, and {visibility}",
"{angle} view of {arrangement}, {fluid_detail}, {intensity}, and {climax_detail}", "{angle} view of {arrangement}, {fluid_detail}, {intensity}, and {climax_detail}",
"explicit adult group pile with {group_act}, {oral_detail}, {penetration_detail}, and {visibility}", "explicit adult group pile with {group_act}, {oral_detail}, {penetration_detail}, and {visibility}",
@@ -36,6 +36,9 @@ The map audit currently sees:
- Insta/OF side-target training captions no longer prepend shared cast - Insta/OF side-target training captions no longer prepend shared cast
descriptors when the selected side row already emits its own cast prose, and descriptors when the selected side row already emits its own cast prose, and
route simulation flags repeated cast descriptors. route simulation flags repeated cast descriptors.
- Route simulation now has an opt-in multi-seed sweep, and the smoke suite runs
a three-seed sweep so representative route/noise checks are not proven by one
lucky seed only.
## Architectural Finding ## Architectural Finding
+5 -1
View File
@@ -1021,9 +1021,13 @@ issues for:
scene, soft outfit, and composition axes stable; scene, soft outfit, and composition axes stable;
- pose-axis rerolls changing cast/scene metadata or failing to move pose/action - pose-axis rerolls changing cast/scene metadata or failing to move pose/action
metadata. metadata.
- multi-seed route sweeps that repeat the same route/noise/seed checks across
spaced seeds to catch random-pool drift hidden by a single clean seed.
Use `--json --include-prompts` when you need the exact raw and formatted text Use `--json --include-prompts` when you need the exact raw and formatted text
for debugging a route. for debugging a route. Use `--sweep-count 5 --seed-step 101` when changing pool
selection, route terms, or formatter noise rules and you need more than one
seed of evidence.
## Editing Cheatsheet ## Editing Cheatsheet
+4
View File
@@ -114,6 +114,10 @@ AUDIT_DOC_SNIPPETS: tuple[tuple[str, str], ...] = (
"docs/prompt-pool-routing-map.md", "docs/prompt-pool-routing-map.md",
"repeated cast descriptors in training-caption formatter output", "repeated cast descriptors in training-caption formatter output",
), ),
(
"docs/prompt-pool-routing-map.md",
"multi-seed route sweeps",
),
) )
PROMPT_ROW_READ_SCAN_GLOBS: tuple[str, ...] = ( PROMPT_ROW_READ_SCAN_GLOBS: tuple[str, ...] = (
+80 -6
View File
@@ -308,6 +308,7 @@ HARDCORE_ROUTE_CASES = (
"subcategory": "Foreplay and teasing", "subcategory": "Foreplay and teasing",
"focus": "foreplay_only", "focus": "foreplay_only",
"family": "foreplay", "family": "foreplay",
"positions": ("undressing",),
"expected_route": {"action_family": "foreplay", "position_family": "foreplay"}, "expected_route": {"action_family": "foreplay", "position_family": "foreplay"},
"expected_terms": { "expected_terms": {
"krea": ("clothing",), "krea": ("clothing",),
@@ -317,12 +318,13 @@ HARDCORE_ROUTE_CASES = (
}, },
{ {
"name": "hardcore.single.interaction", "name": "hardcore.single.interaction",
"subcategory": "Aftercare and cleanup", "subcategory": "Clothing and position transitions",
"focus": "interaction_only", "focus": "interaction_only",
"family": "interaction", "family": "interaction",
"positions": ("position_transition",),
"expected_route": {"action_family": "foreplay", "position_family": "interaction"}, "expected_route": {"action_family": "foreplay", "position_family": "interaction"},
"expected_terms": { "expected_terms": {
"krea": ("mid-transition",), "krea": ("clothing",),
"sdxl": ("interaction",), "sdxl": ("interaction",),
"caption": ("interaction beat",), "caption": ("interaction beat",),
}, },
@@ -332,9 +334,10 @@ HARDCORE_ROUTE_CASES = (
"subcategory": "Anal and double penetration", "subcategory": "Anal and double penetration",
"focus": "anal_only", "focus": "anal_only",
"family": "anal", "family": "anal",
"positions": ("doggy", "face_down_ass_up"),
"expected_route": {"action_family": "anal", "position_family": "anal"}, "expected_route": {"action_family": "anal", "position_family": "anal"},
"expected_terms": { "expected_terms": {
"krea": ("anal",), "krea": ("ass",),
"sdxl": ("anal sex",), "sdxl": ("anal sex",),
"caption": ("anal action",), "caption": ("anal action",),
}, },
@@ -853,7 +856,13 @@ def _regular_single_case(seed: int) -> dict[str, Any]:
) )
def _hardcore_single_case(seed: int, subcategory: str, focus: str, family: str) -> dict[str, Any]: def _hardcore_single_case(
seed: int,
subcategory: str,
focus: str,
family: str,
positions: list[str] | tuple[str, ...] | str = (),
) -> dict[str, Any]:
women_count, men_count, character_cast = { women_count, men_count, character_cast = {
"threesome": (1, 2, _character_cast_subjects(("woman", "man", "man"))), "threesome": (1, 2, _character_cast_subjects(("woman", "man", "man"))),
"group": (2, 2, _character_cast_subjects(("woman", "woman", "man", "man"))), "group": (2, 2, _character_cast_subjects(("woman", "woman", "man", "man"))),
@@ -881,7 +890,7 @@ def _hardcore_single_case(seed: int, subcategory: str, focus: str, family: str)
women_count=women_count, women_count=women_count,
men_count=men_count, men_count=men_count,
character_cast=character_cast, character_cast=character_cast,
hardcore_position_config=_position_filter(focus, family, []), hardcore_position_config=_position_filter(focus, family, positions),
location_config=_coworking_location_config(), location_config=_coworking_location_config(),
camera_config=_orbit_camera(horizontal_angle=35, vertical_angle=0, zoom=6.5), camera_config=_orbit_camera(horizontal_angle=35, vertical_angle=0, zoom=6.5),
) )
@@ -1404,6 +1413,7 @@ def run_simulation(seed: int = 3901, *, include_prompts: bool = False) -> dict[s
str(route_case["subcategory"]), str(route_case["subcategory"]),
str(route_case["focus"]), str(route_case["focus"]),
str(route_case["family"]), str(route_case["family"]),
route_case.get("positions") or (),
) )
cases.append( cases.append(
_case_report( _case_report(
@@ -1459,6 +1469,38 @@ def run_simulation(seed: int = 3901, *, include_prompts: bool = False) -> dict[s
} }
def run_simulation_sweep(
seed: int = 3901,
*,
count: int = 3,
seed_step: int = 101,
include_prompts: bool = False,
) -> dict[str, Any]:
count = max(1, int(count))
seed_step = int(seed_step)
seeds = [seed + index * seed_step for index in range(count)]
runs = [run_simulation(seed=current_seed, include_prompts=include_prompts) for current_seed in seeds]
issues: list[dict[str, Any]] = []
for run in runs:
run_seed = (run.get("summary") or {}).get("seed")
issues.extend({"seed": run_seed, **issue} for issue in run.get("issues") or [])
return {
"summary": {
"seed": seed,
"seed_step": seed_step,
"seeds": seeds,
"runs": len(runs),
"cases": sum((run.get("summary") or {}).get("cases", 0) for run in runs),
"coverage_checks": sum((run.get("summary") or {}).get("coverage_checks", 0) for run in runs),
"axis_checks": sum((run.get("summary") or {}).get("axis_checks", 0) for run in runs),
"pair_seed_checks": sum((run.get("summary") or {}).get("pair_seed_checks", 0) for run in runs),
"issues": len(issues),
},
"issues": issues,
"runs": runs,
}
def _print_text_report(report: dict[str, Any]) -> None: def _print_text_report(report: dict[str, Any]) -> None:
summary = report.get("summary") or {} summary = report.get("summary") or {}
print( print(
@@ -1490,17 +1532,49 @@ def _print_text_report(report: dict[str, Any]) -> None:
print(f" ISSUE {issue}") print(f" ISSUE {issue}")
def _print_sweep_report(report: dict[str, Any]) -> None:
summary = report.get("summary") or {}
seeds = ", ".join(str(seed) for seed in (summary.get("seeds") or []))
print(
f"Prompt route simulation sweep: seed={summary.get('seed')} "
f"seed_step={summary.get('seed_step')} runs={summary.get('runs')} "
f"seeds={seeds} cases={summary.get('cases')} coverage_checks={summary.get('coverage_checks')} "
f"axis_checks={summary.get('axis_checks')} pair_seed_checks={summary.get('pair_seed_checks')} "
f"issues={summary.get('issues')}"
)
for run in report.get("runs") or []:
run_summary = run.get("summary") or {}
print(
f"- seed {run_summary.get('seed')}: "
f"cases={run_summary.get('cases')} issues={run_summary.get('issues')}"
)
for issue in run.get("issues") or []:
print(f" ISSUE {issue.get('case')}: {issue.get('issue')}")
def main(argv: list[str] | None = None) -> int: def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--seed", type=int, default=3901, help="Base seed for deterministic simulations.") parser.add_argument("--seed", type=int, default=3901, help="Base seed for deterministic simulations.")
parser.add_argument("--sweep-count", type=int, default=1, help="Run this many seed-spaced simulations.")
parser.add_argument("--seed-step", type=int, default=101, help="Seed increment used by --sweep-count.")
parser.add_argument("--json", action="store_true", help="Print the full JSON report.") parser.add_argument("--json", action="store_true", help="Print the full JSON report.")
parser.add_argument("--include-prompts", action="store_true", help="Include raw and formatted prompt text in the report.") parser.add_argument("--include-prompts", action="store_true", help="Include raw and formatted prompt text in the report.")
parser.add_argument("--fail-on-issues", action="store_true", help="Exit with code 1 when any issue is reported.") parser.add_argument("--fail-on-issues", action="store_true", help="Exit with code 1 when any issue is reported.")
args = parser.parse_args(argv) args = parser.parse_args(argv)
report = run_simulation(seed=args.seed, include_prompts=args.include_prompts) if args.sweep_count > 1:
report = run_simulation_sweep(
seed=args.seed,
count=args.sweep_count,
seed_step=args.seed_step,
include_prompts=args.include_prompts,
)
else:
report = run_simulation(seed=args.seed, include_prompts=args.include_prompts)
if args.json: if args.json:
print(json.dumps(report, ensure_ascii=True, indent=2, sort_keys=True)) print(json.dumps(report, ensure_ascii=True, indent=2, sort_keys=True))
elif args.sweep_count > 1:
_print_sweep_report(report)
else: else:
_print_text_report(report) _print_text_report(report)
return 1 if args.fail_on_issues and report.get("issues") else 0 return 1 if args.fail_on_issues and report.get("issues") else 0
+6
View File
@@ -8025,6 +8025,12 @@ def smoke_prompt_route_simulation_policy() -> None:
pair_seed_checks["pair_seed.pose_reroll"].get("changed") is True, pair_seed_checks["pair_seed.pose_reroll"].get("changed") is True,
"Pair pose reroll should prove hard action can reroll while soft/cast/scene axes stay locked", "Pair pose reroll should prove hard action can reroll while soft/cast/scene axes stay locked",
) )
sweep = prompt_route_simulation.run_simulation_sweep(seed=3901, count=3, seed_step=101, include_prompts=False)
sweep_summary = sweep.get("summary") or {}
_expect(sweep_summary.get("runs") == 3, "Prompt route simulation sweep lost run coverage")
_expect(sweep_summary.get("seeds") == [3901, 4002, 4103], "Prompt route simulation sweep seed sequence changed")
_expect(sweep_summary.get("cases") == 42, "Prompt route simulation sweep case count changed")
_expect(sweep_summary.get("issues") == 0, f"Prompt route simulation sweep reported issues: {sweep.get('issues')}")
def smoke_node_camera_registration() -> None: def smoke_node_camera_registration() -> None: