#!/usr/bin/env python3
"""Smoke-test core prompt routes without importing ComfyUI.

The checks here are intentionally lightweight invariants, not golden prompt
snapshots. They prove that representative rows still carry structured metadata
and that the Krea2, SDXL, and caption formatter paths consume metadata instead
of silently falling back to raw text parsing.
"""

from __future__ import annotations

import argparse
import json
import random
import re
import subprocess
import sys
import tempfile
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Callable


ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) in sys.path:
    sys.path.remove(str(ROOT))
sys.path.insert(0, str(ROOT))

import caption_naturalizer  # noqa: E402
import caption_format_route  # noqa: E402
import caption_metadata_routes  # noqa: E402
import caption_policy  # noqa: E402
import caption_text_policy  # noqa: E402
import builder_config_route  # noqa: E402
import builder_prompt_route  # noqa: E402
import cast_context  # noqa: E402
import category_extensions  # noqa: E402
import category_template_metadata  # noqa: E402
import character_appearance  # noqa: E402
import character_config  # noqa: E402
import character_profile  # noqa: E402
import character_slot  # noqa: E402
import category_cast_config  # noqa: E402
import category_library  # noqa: E402
import filter_config  # noqa: E402
import formatter_detail  # noqa: E402
import formatter_input  # noqa: E402
import formatter_target  # noqa: E402
import hardcore_action_metadata  # noqa: E402
import hardcore_position_config  # noqa: E402
import __init__ as sxcp_nodes  # noqa: E402
import generation_profile_config  # noqa: E402
import hardcore_role_interaction  # noqa: E402
import hardcore_role_oral  # noqa: E402
import hardcore_role_outercourse  # noqa: E402
import index_switch_policy  # noqa: E402
import item_axis_policy  # noqa: E402
import node_tooltips  # noqa: E402
import krea_cast  # noqa: E402
import krea_action_details  # noqa: E402
import krea_action_context  # noqa: E402
import krea_pov_actions  # noqa: E402
import krea_configured_cast_formatter  # noqa: E402
import krea_format_route  # noqa: E402
import krea_formatter  # noqa: E402
import krea_normal_formatter  # noqa: E402
import krea_pair_formatter  # noqa: E402
import krea2_eval_log  # noqa: E402
import krea2_pose_variant_catalog  # noqa: E402
import krea2_tuning_report  # noqa: E402
import krea_row_fields  # noqa: E402
import location_config  # noqa: E402
import loop_nodes  # noqa: E402
import pair_builder  # noqa: E402
import pair_camera  # noqa: E402
import pair_cast  # noqa: E402
import pair_clothing  # noqa: E402
import pair_rows  # noqa: E402
import prompt_hygiene  # noqa: E402
import prompt_builder as pb  # noqa: E402
import outercourse_action_policy  # noqa: E402
import pov_policy  # noqa: E402
import row_normalization  # noqa: E402
import row_assembly  # noqa: E402
import route_metadata  # noqa: E402
import row_camera  # noqa: E402
import row_category_route  # noqa: E402
import row_expression  # noqa: E402
import row_generation  # noqa: E402
import row_item  # noqa: E402
import row_location  # noqa: E402
import row_pools  # noqa: E402
import row_prompt_axes  # noqa: E402
import row_rendering  # noqa: E402
import row_role_graph  # noqa: E402
import row_route_metadata  # noqa: E402
import row_subject_route  # noqa: E402
import scene_camera_adapters  # noqa: E402
import server_routes  # noqa: E402
import sdxl_formatter  # noqa: E402
import sdxl_format_route  # noqa: E402
import sdxl_presets  # noqa: E402
import sdxl_tag_policy  # noqa: E402
import sdxl_tag_routes  # noqa: E402
import seed_config  # noqa: E402
import krea_pov  # noqa: E402
import subject_context  # noqa: E402
from tools import prompt_route_simulation  # noqa: E402
from tools import sxcp_prompt_batch  # noqa: E402


Trigger = "sxcppnl7"
SdxlTrigger = "mythp0rt"


@dataclass
class SmokeReport:
    verbose: bool = True
    passed: list[str] = field(default_factory=list)
    failed: list[str] = field(default_factory=list)

    def ok(self, name: str) -> None:
        self.passed.append(name)
        if self.verbose:
            print(f"PASS {name}")

    def fail(self, name: str, message: str) -> None:
        detail = f"{name}: {message}"
        self.failed.append(detail)
        print(f"FAIL {detail}")


def _clean_key(value: str) -> str:
    return re.sub(r"[^a-z0-9]+", " ", str(value or "").lower()).strip()


def _json(value: Any) -> str:
    return json.dumps(value, ensure_ascii=True, sort_keys=True)


def _expect(condition: bool, message: str) -> None:
    if not condition:
        raise AssertionError(message)


def _expect_text(name: str, value: Any, min_len: int = 8) -> str:
    text = str(value or "").strip()
    _expect(len(text) >= min_len, f"{name} is empty or too short")
    _expect("None" not in text, f"{name} leaked None")
    _expect("  " not in text, f"{name} has repeated spaces")
    _expect(" ," not in text and " ." not in text, f"{name} has bad punctuation spacing")
    return text


def _expect_no_duplicate_comma_items(name: str, value: Any) -> None:
    items = [_clean_key(part) for part in str(value or "").split(",")]
    items = [part for part in items if part]
    duplicates = sorted({part for part in items if items.count(part) > 1})
    _expect(not duplicates, f"{name} has duplicate comma items: {duplicates[:5]}")


def _expect_no_softcore_noise(name: str, value: Any) -> None:
    text = str(value or "").lower()
    noisy = (
        "the image focuses",
        "softcore version",
        "non-explicit teaser setup",
        "no sex act",
        "genital contact",
        "keep the softcore version",
        "focused on woman a alone",
    )
    found = [phrase for phrase in noisy if phrase in text]
    _expect(not found, f"{name} has softcore prompt noise: {found}")


def _trigger_count(text: str, trigger: str) -> int:
    return len(re.findall(rf"(?<![a-z0-9_]){re.escape(trigger)}(?![a-z0-9_])", text, flags=re.IGNORECASE))


def _expect_trigger_once(name: str, value: Any, trigger: str) -> None:
    text = str(value or "")
    count = _trigger_count(text, trigger)
    _expect(count == 1, f"{name} should contain trigger {trigger!r} exactly once, got {count}")


def _expect_row_base(row: dict[str, Any], name: str) -> None:
    _expect(isinstance(row, dict), f"{name} did not return a metadata row")
    _expect_text(f"{name}.prompt", row.get("prompt"), 20)
    _expect_text(f"{name}.negative_prompt", row.get("negative_prompt"), 8)
    _expect_no_duplicate_comma_items(f"{name}.negative_prompt", row.get("negative_prompt"))
    _expect(json.loads(_json(row)) == row, f"{name} is not JSON-stable")


def _expect_custom_row(row: dict[str, Any], name: str) -> None:
    _expect_row_base(row, name)
    _expect(row.get("source") == "json_category", f"{name}.source should be json_category")
    _expect_text(f"{name}.item", row.get("item"), 8)
    _expect_text(f"{name}.scene_text", row.get("scene_text"), 8)
    _expect_text(f"{name}.composition", row.get("composition"), 8)
    _expect_text(f"{name}.role_graph", row.get("source_role_graph") or row.get("role_graph"), 8)
    _expect(isinstance(row.get("item_axis_values"), dict), f"{name}.item_axis_values missing")
    _expect(isinstance(row.get("formatter_hints"), dict), f"{name}.formatter_hints missing")


def _expect_formatter_outputs(row: dict[str, Any], name: str, *, target: str = "auto") -> None:
    metadata = _json(row)
    krea = krea_formatter.format_krea2_prompt("", metadata_json=metadata, target=target)
    _expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata: {krea.get('method')}")
    _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 20)
    _expect_no_duplicate_comma_items(f"{name}.krea_negative", krea.get("negative_prompt"))

    sdxl = sdxl_formatter.format_sdxl_prompt(
        "",
        metadata_json=metadata,
        target=target,
        trigger=SdxlTrigger,
        prepend_trigger=True,
    )
    _expect("metadata" in sdxl.get("method", ""), f"{name}.sdxl did not use metadata: {sdxl.get('method')}")
    _expect_text(f"{name}.sdxl_prompt", sdxl.get("sdxl_prompt"), 20)
    _expect_trigger_once(f"{name}.sdxl_prompt", sdxl.get("sdxl_prompt"), SdxlTrigger)
    _expect_no_duplicate_comma_items(f"{name}.sdxl_negative", sdxl.get("negative_prompt"))

    caption, method = caption_naturalizer.naturalize_caption(
        "",
        metadata_json=metadata,
        target=target,
        trigger=Trigger,
        include_trigger=True,
    )
    _expect("metadata" in method, f"{name}.caption did not use metadata: {method}")
    _expect_text(f"{name}.caption", caption, 20)
    _expect_trigger_once(f"{name}.caption", caption, Trigger)


def _expect_krea_normal_route_parity(row: dict[str, Any], name: str, method: str) -> None:
    typed_route = krea_normal_formatter.format_normal_row_result(
        krea_formatter._krea_normal_row_request_from_row(row, "balanced", "preserve"),
        krea_formatter._krea_normal_row_dependencies(),
    )
    legacy_route = krea_formatter._normal_row_to_krea(row, "balanced", "preserve")
    _expect(
        typed_route.as_tuple() == legacy_route,
        f"{name} typed Krea normal formatter route should match legacy wrapper output",
    )
    _expect(typed_route.method == method, f"{name} typed Krea normal formatter method changed")
    _expect_text(f"{name}.typed_krea_prompt", typed_route.prompt, 20)


def _character_cast(*, pov_man: bool = False) -> str:
    cast = pb.build_character_slot_json(
        subject_type="woman",
        label="A",
        age="25-year-old adult",
        ethnicity="western_european",
        figure="balanced",
        body="slim",
        descriptor_detail="full",
        expression_intensity=0.65,
        softcore_expression_intensity=0.45,
        hardcore_expression_intensity=0.85,
    )["character_cast"]
    return pb.build_character_slot_json(
        subject_type="man",
        label="A",
        age="40-year-old adult",
        ethnicity="western_european",
        figure="balanced",
        body="average",
        descriptor_detail="compact",
        expression_intensity=0.55,
        softcore_expression_intensity=0.35,
        hardcore_expression_intensity=0.75,
        presence_mode="pov" if pov_man else "visible",
        character_cast=cast,
    )["character_cast"]


def _character_cast_two_men(*, pov_first_man: bool = False) -> str:
    cast = _character_cast(pov_man=pov_first_man)
    return pb.build_character_slot_json(
        subject_type="man",
        label="B",
        age="41-year-old adult",
        ethnicity="western_european",
        figure="balanced",
        body="average",
        descriptor_detail="compact",
        expression_intensity=0.55,
        softcore_expression_intensity=0.35,
        hardcore_expression_intensity=0.75,
        character_cast=cast,
    )["character_cast"]


def _character_cast_subjects(subjects: list[str] | tuple[str, ...]) -> str:
    cast = ""
    counts = {"woman": 0, "man": 0}
    for subject in subjects:
        subject = str(subject)
        counts[subject] += 1
        label = chr(ord("A") + counts[subject] - 1)
        cast = pb.build_character_slot_json(
            subject_type=subject,
            label=label,
            age="25-year-old adult" if subject == "woman" else "40-year-old adult",
            ethnicity="western_european",
            figure="balanced",
            body="slim" if subject == "woman" else "average",
            descriptor_detail="compact",
            expression_intensity=0.55,
            softcore_expression_intensity=0.35,
            hardcore_expression_intensity=0.75,
            character_cast=cast,
        )["character_cast"]
    return cast


def _exact_subcategory_selector(category: dict[str, Any], subcategory: dict[str, Any]) -> str:
    return category_library.exact_subcategory_selector(category, subcategory)


def _matrix_cast_for_route(category: dict[str, Any], subcategory: dict[str, Any]) -> tuple[int, int, str]:
    subject_type = str(subcategory.get("subject_type") or category.get("subject_type") or "woman")
    if subject_type == "woman":
        return 1, 0, ""
    if subject_type == "man":
        return 0, 1, ""
    if subject_type == "couple":
        return 1, 1, ""

    min_people = int(subcategory.get("min_people") or 0)
    women_count = int(subcategory.get("min_women") or 0)
    men_count = int(subcategory.get("min_men") or 0)
    if min_people <= 1 and not women_count and not men_count:
        women_count = 1
    elif min_people >= 4:
        women_count = max(women_count, 2)
        men_count = max(men_count, min_people - women_count)
    elif min_people >= 3:
        women_count = max(women_count, 1)
        men_count = max(men_count, min_people - women_count)
    elif min_people >= 2:
        women_count = max(women_count, 1)
        men_count = max(men_count, min_people - women_count)
    required_total = max(1, min_people)
    if women_count + men_count < required_total:
        men_count += required_total - (women_count + men_count)
    if women_count == 0 and men_count == 0:
        women_count = 1

    cast = _character_cast_subjects(["woman"] * women_count + ["man"] * men_count)
    return women_count, men_count, cast


def _action_filter(focus: str, hardcore_position_config: str | dict[str, Any] | None = "") -> str:
    kwargs = {
        "allow_toys": False,
        "allow_double": False,
        "allow_penetration": focus in ("penetration_only", "keep_pool"),
        "allow_foreplay": focus in ("foreplay_only", "keep_pool"),
        "allow_interaction": focus in ("interaction_only", "keep_pool"),
        "allow_manual": focus in ("manual_only", "keep_pool"),
        "allow_oral": focus in ("oral_only", "keep_pool"),
        "allow_outercourse": focus in ("outercourse_only", "keep_pool"),
        "allow_anal": focus in ("anal_only", "keep_pool"),
        "allow_climax": focus in ("climax_only", "keep_pool"),
    }
    return pb.build_hardcore_action_filter_json(
        hardcore_position_config=hardcore_position_config,
        focus=focus,
        **kwargs,
    )


def _broad_hardcore_filter() -> str:
    return pb.build_hardcore_action_filter_json(
        focus="keep_pool",
        allow_toys=True,
        allow_double=True,
        allow_penetration=True,
        allow_foreplay=True,
        allow_interaction=True,
        allow_manual=True,
        allow_oral=True,
        allow_outercourse=True,
        allow_anal=True,
        allow_climax=True,
    )


def _position_filter(focus: str, family: str, positions: list[str] | tuple[str, ...] | str) -> str:
    position_config = pb.build_hardcore_position_pool_json(
        combine_mode="replace",
        family=family,
        selected_positions=positions,
    )
    return _action_filter(focus, position_config)


def _anal_double_filter(positions: list[str] | tuple[str, ...] | str) -> str:
    position_config = pb.build_hardcore_position_pool_json(
        combine_mode="replace",
        family="anal",
        selected_positions=positions,
    )
    return pb.build_hardcore_action_filter_json(
        hardcore_position_config=position_config,
        focus="anal_only",
        allow_toys=True,
        allow_double=True,
        allow_penetration=True,
        allow_foreplay=False,
        allow_interaction=False,
        allow_manual=False,
        allow_oral=False,
        allow_outercourse=False,
        allow_anal=True,
        allow_climax=False,
    )


def _coworking_location_config() -> str:
    return pb.build_location_pool_json(
        enabled=True,
        combine_mode="replace",
        preset="custom_only",
        custom_locations=(
            "coworking_smoke: coworking lounge with tall windows, warm desks, "
            "laptop tables, glass partition seams, repeated desk rows, plants, "
            "and soft shared-office depth"
        ),
    )


def _classical_library_theme_configs() -> tuple[str, str]:
    return _thematic_location_configs("classical_library")


def _thematic_location_configs(theme: str) -> tuple[str, str]:
    location_config, composition_config, _summary = pb.build_thematic_location_json(
        enabled=True,
        combine_mode="replace",
        theme=theme,
    )
    return location_config, composition_config


def _orbit_camera(
    *,
    horizontal_angle: int,
    vertical_angle: int,
    zoom: float,
    subject_focus: str = "auto",
    camera_detail: str = "compact",
) -> str:
    return pb.build_camera_orbit_config_json(
        enabled=True,
        camera_mode="standard",
        horizontal_angle=horizontal_angle,
        vertical_angle=vertical_angle,
        zoom=zoom,
        framing="from_zoom",
        subject_focus=subject_focus,
        lens="auto",
        orientation="auto",
        phone_visibility="auto",
        priority="strong",
        camera_detail=camera_detail,
        include_degrees=True,
    )


def _prompt_row(
    *,
    name: str,
    category: str,
    subcategory: str,
    seed: int,
    seed_config: str | dict[str, Any] | None = None,
    character_cast: str = "",
    women_count: int = 1,
    men_count: int = 1,
    hardcore_position_config: str = "",
    camera_config: str | dict[str, Any] | None = "",
    location_config: str | dict[str, Any] | None = "",
    composition_config: str | dict[str, Any] | None = "",
    style_config: str | dict[str, Any] | None = "",
) -> dict[str, Any]:
    row = pb.build_prompt(
        category=category,
        subcategory=subcategory,
        row_number=1,
        start_index=1,
        seed=seed,
        clothing="random",
        ethnicity="any",
        poses="random",
        backside_bias=0.35,
        figure="random",
        no_plus_women=False,
        no_black=False,
        minimal_clothing_ratio=0.5,
        standard_pose_ratio=0.5,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        extra_positive="",
        extra_negative="",
        seed_config=seed_config,
        character_cast=character_cast,
        women_count=women_count,
        men_count=men_count,
        expression_enabled=True,
        expression_intensity=0.6,
        hardcore_position_config=hardcore_position_config,
        camera_config=camera_config,
        location_config=location_config,
        composition_config=composition_config,
        style_config=style_config,
    )
    _expect_row_base(row, name)
    return row


def _fixture_hardcore_row(**overrides: Any) -> dict[str, Any]:
    row: dict[str, Any] = {
        "source": "json_category",
        "prompt": "Fixture explicit adult prompt for metadata route.",
        "caption": "fixture caption",
        "negative_prompt": "low quality, bad anatomy",
        "main_category": "Hardcore sexual poses",
        "subcategory": "Penetrative sex",
        "category_slug": "hardcore_sexual_poses",
        "subcategory_slug": "penetrative_sex",
        "subject_type": "configured_cast",
        "subject_phrase": "1 adult woman and 1 adult man",
        "cast_summary": "1 woman, 1 man",
        "cast_descriptor_text": (
            "Woman A: 25-year-old adult woman, slim figure, fair skin, blonde hair, blue eyes; "
            "Man A: 40-year-old adult man, average figure, tan skin, dark hair"
        ),
        "cast_descriptors": [
            "Woman A: 25-year-old adult woman, slim figure, fair skin, blonde hair, blue eyes",
            "Man A: 40-year-old adult man, average figure, tan skin, dark hair",
        ],
        "women_count": 1,
        "men_count": 1,
        "person_count": 2,
        "item": (
            "missionary position while full-body penetrative sex, hands gripping the ass, "
            "mouth close to the ear, and explicit genital contact visible"
        ),
        "custom_item": "Penetrative sex",
        "item_label": "Sexual pose",
        "item_axis_values": {
            "position": "missionary position",
            "penetration_act": "full-body penetrative sex",
            "mouth_detail": "mouth close to the ear",
        },
        "item_template_metadata": {},
        "formatter_hints": {},
        "scene_text": "private studio room with warm light",
        "scene_kind": "explicit adult sex scene",
        "pose": "configured explicit pose",
        "composition": "front-facing full-body frame",
        "source_composition": "front-facing full-body frame",
        "role_graph": (
            "Woman A lies on her back with legs open around Man A's hips while Man A is above her between her thighs; "
            "Man A's hips press close and Man A's penis thrusts into her pussy."
        ),
        "source_role_graph": (
            "Woman A lies on her back with legs open around Man A's hips while Man A is above her between her thighs; "
            "Man A's hips press close and Man A's penis thrusts into her pussy."
        ),
        "expression": "focused adult expression",
        "action_family": "penetration",
        "position_family": "penetrative",
        "position_key": "missionary",
        "position_keys": ["missionary"],
    }
    row.update(overrides)
    return row


def smoke_builtin_single() -> None:
    row = _prompt_row(name="builtin_single_woman", category="woman", subcategory="random", seed=1001, men_count=0)
    _expect(row.get("source") == "built_in_generator", "builtin row should come from built-in generator")
    _expect(row.get("subject_type") == "woman", "builtin single row lost normalized subject_type")
    _expect(row.get("subject_phrase") == "woman", "builtin single row lost normalized subject_phrase")
    _expect(row.get("women_count") == 1 and row.get("men_count") == 0, "builtin single row lost normalized cast counts")
    _expect(row.get("person_count") == 1, "builtin single row lost normalized person count")
    _expect_text("builtin_single_woman.scene_text", row.get("scene_text"), 12)
    _expect(row.get("scene_slug") == row.get("scene"), "builtin single row lost legacy scene slug metadata")
    _expect(row.get("scene_entry", {}).get("slug") == row.get("scene"), "builtin single row lost scene_entry slug")
    item = _expect_text("builtin_single_woman.item", row.get("item"), 8)
    pose = _expect_text("builtin_single_woman.pose", row.get("pose"), 8)
    body_phrase = _expect_text("builtin_single_woman.body_phrase", row.get("body_phrase"), 8)
    skin = _expect_text("builtin_single_woman.skin", row.get("skin"), 8)
    hair = _expect_text("builtin_single_woman.hair", row.get("hair"), 5)
    eyes = _expect_text("builtin_single_woman.eyes", row.get("eyes"), 4)
    _expect(row.get("item_label") == "Clothing", "builtin single row lost item label")
    _expect(row.get("clothing") == item, "builtin single row did not mirror clothing into item metadata")
    _expect("fashion editorial styling" not in item.lower(), "builtin single item kept generic styling suffix")
    _expect("cast_summary" not in row, "builtin single row should not masquerade as configured cast")
    _expect_trigger_once("builtin_single_woman.prompt", row.get("prompt"), Trigger)
    krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(row), target="single")
    _expect(row.get("scene_text") in str(krea.get("krea_prompt", "")), "builtin single Krea route used scene slug instead of scene text")
    sdxl = sdxl_formatter.format_sdxl_prompt("", metadata_json=_json(row), target="single", trigger=SdxlTrigger, prepend_trigger=True)
    _expect("1woman" in str(sdxl.get("sdxl_prompt", "")).lower(), "builtin single SDXL route lost normalized woman count")
    sdxl_scene_text = str(sdxl.get("sdxl_prompt", "")).lower()
    scene_words = re.findall(r"[a-z0-9]+", str(row.get("scene_text", "")).split(",", 1)[0].lower())
    scene_anchor = " ".join(scene_words[:2])
    _expect(scene_anchor in sdxl_scene_text, "builtin single SDXL route lost readable scene text")
    _expect(str(row.get("scene_slug", "")).lower() not in sdxl_scene_text, "builtin single SDXL route leaked raw scene slug")
    caption, caption_method = caption_naturalizer.naturalize_caption("", metadata_json=_json(row), target="single", trigger=Trigger, include_trigger=True)
    _expect(caption_method.endswith("metadata(single)"), "builtin single caption route did not use single metadata branch")
    _expect("woman" in caption.lower(), "builtin single caption route lost normalized subject")
    _expect(row.get("scene_text") in caption, "builtin single caption route used scene slug instead of scene text")

    metadata_only = dict(row)
    metadata_only["prompt"] = ""
    metadata_only["caption"] = ""
    krea_metadata = krea_formatter.format_krea2_prompt("", metadata_json=_json(metadata_only), target="single")
    sdxl_metadata = sdxl_formatter.format_sdxl_prompt("", metadata_json=_json(metadata_only), target="single", trigger=SdxlTrigger, prepend_trigger=True)
    caption_metadata, caption_metadata_method = caption_naturalizer.naturalize_caption(
        "",
        metadata_json=_json(metadata_only),
        target="single",
        trigger=Trigger,
        include_trigger=True,
    )
    _expect(item in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost explicit item")
    _expect(pose in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost explicit pose")
    krea_metadata_prompt = str(krea_metadata.get("krea_prompt", ""))
    if re.search(r"\b(?:body|build|figure|frame|physique|silhouette)\s+with\b", body_phrase, flags=re.IGNORECASE):
        body_head, body_detail = body_phrase.split(" with ", 1)
        _expect(
            body_head in krea_metadata_prompt and body_detail in krea_metadata_prompt,
            "Krea metadata-only built-in route lost normalized body phrase",
        )
    else:
        _expect(body_phrase in krea_metadata_prompt, "Krea metadata-only built-in route lost body phrase")
    _expect(skin in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost skin")
    _expect(hair in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost hair")
    _expect(eyes in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost eyes")
    item_anchor = " ".join(re.findall(r"[a-z0-9]+", item.lower())[:3])
    pose_anchor = " ".join(re.findall(r"[a-z0-9]+", pose.lower())[:4])
    sdxl_metadata_prompt = str(sdxl_metadata.get("sdxl_prompt", "")).lower()
    _expect(item_anchor in sdxl_metadata_prompt, "SDXL metadata-only built-in route lost explicit item")
    _expect(pose_anchor in sdxl_metadata_prompt, "SDXL metadata-only built-in route lost explicit pose")
    for body_tag in sdxl_tag_policy.split_tag_text(body_phrase):
        _expect(body_tag.lower() in sdxl_metadata_prompt, f"SDXL metadata-only built-in route lost body tag: {body_tag}")
    _expect(skin.lower() in sdxl_metadata_prompt, "SDXL metadata-only built-in route lost skin")
    _expect(hair.lower() in sdxl_metadata_prompt, "SDXL metadata-only built-in route lost hair")
    _expect(eyes.lower() in sdxl_metadata_prompt, "SDXL metadata-only built-in route lost eyes")
    _expect(caption_metadata_method.endswith("metadata(single)"), "Caption metadata-only built-in route did not use single metadata branch")
    _expect(item in caption_metadata and pose in caption_metadata, "Caption metadata-only built-in route lost explicit item or pose")
    _expect(body_phrase in caption_metadata and skin in caption_metadata, "Caption metadata-only built-in route lost appearance")
    _expect(hair in caption_metadata and eyes in caption_metadata, "Caption metadata-only built-in route lost hair or eyes")
    _expect_formatter_outputs(row, "builtin_single_woman", target="single")


def smoke_builtin_couple_metadata() -> None:
    row = _prompt_row(name="builtin_couple", category="couple", subcategory="random", seed=1003, men_count=0)
    _expect(row.get("source") == "built_in_generator", "builtin couple row should come from built-in generator")
    _expect(row.get("subject_type") == "couple", "builtin couple row lost normalized subject_type")
    _expect(row.get("women_count") + row.get("men_count") == row.get("person_count"), "builtin couple row lost count consistency")
    _expect(row.get("person_count") == 2, "builtin couple row lost normalized person count")
    subject = _expect_text("builtin_couple.subject_phrase", row.get("subject_phrase"), 5)
    age = _expect_text("builtin_couple.age_band", row.get("age_band"), 8)
    body = _expect_text("builtin_couple.body_type", row.get("body_type"), 5)
    scene = _expect_text("builtin_couple.scene_text", row.get("scene_text"), 12)
    item = _expect_text("builtin_couple.item", row.get("item"), 8)
    pose = _expect_text("builtin_couple.pose", row.get("pose"), 8)
    expression = _expect_text("builtin_couple.expression", row.get("expression"), 8)
    composition = _expect_text("builtin_couple.composition", row.get("composition"), 8)

    metadata_only = dict(row)
    metadata_only["prompt"] = ""
    metadata_only["caption"] = ""
    metadata = _json(metadata_only)
    krea = krea_formatter.format_krea2_prompt("", metadata_json=metadata, target="auto")
    sdxl = sdxl_formatter.format_sdxl_prompt(
        "",
        metadata_json=metadata,
        target="auto",
        trigger=SdxlTrigger,
        prepend_trigger=True,
    )
    caption, caption_method = caption_naturalizer.naturalize_caption(
        "",
        metadata_json=metadata,
        target="auto",
        trigger=Trigger,
        include_trigger=True,
    )

    clean_age = caption_text_policy.clean_age_phrase(age)
    krea_prompt = str(krea.get("krea_prompt", ""))
    sdxl_prompt = str(sdxl.get("sdxl_prompt", "")).lower()
    _expect("metadata(couple)" in krea.get("method", ""), "Krea built-in couple route did not use metadata")
    _expect(subject.capitalize() in krea_prompt and clean_age in krea_prompt, "Krea built-in couple route lost subject or age")
    _expect("all visibly adult" not in krea_prompt.lower(), "Krea built-in couple route kept redundant adult noise")
    _expect("Age detail:" not in krea_prompt, "Krea built-in couple route kept age helper label")
    for value in (body, scene, item, pose, expression, composition):
        _expect(value in krea_prompt, f"Krea built-in couple route lost metadata value: {value}")

    _expect("2women" in sdxl_prompt or "1woman, 1man" in sdxl_prompt or "2men" in sdxl_prompt, "SDXL built-in couple route lost count tags")
    for value in (body, scene, item, pose, expression, composition):
        for tag in sdxl_tag_policy.split_tag_text(value):
            _expect(tag.lower() in sdxl_prompt, f"SDXL built-in couple route lost metadata tag: {tag}")

    _expect(caption_method.endswith("metadata(couple)"), "Caption built-in couple route did not use metadata")
    _expect(clean_age in caption and scene in caption, "Caption built-in couple route lost age or scene")
    _expect("The age detail is" not in caption, "Caption built-in couple route kept age helper sentence")
    for value in (body, item, pose, expression, composition):
        _expect(value in caption, f"Caption built-in couple route lost metadata value: {value}")


def smoke_camera_scene_single() -> None:
    row = _prompt_row(
        name="camera_scene_single",
        category="woman",
        subcategory="random",
        seed=1051,
        men_count=0,
        camera_config=_orbit_camera(
            horizontal_angle=45,
            vertical_angle=-30,
            zoom=5.0,
            subject_focus="environment",
        ),
        location_config=_coworking_location_config(),
    )
    scene_directive = _expect_text("camera_scene_single.camera_scene_directive", row.get("camera_scene_directive"), 40)
    camera_directive = _expect_text("camera_scene_single.camera_directive", row.get("camera_directive"), 20)
    _expect("Coworking camera layout" in scene_directive, "single camera-scene adapter did not identify coworking layout")
    _expect("front-right quarter view" in scene_directive, "single camera scene missed orbit direction")
    _expect("low-angle shot" in scene_directive, "single camera scene missed orbit elevation")
    _expect("45-degree front-right quarter view" in camera_directive, "single camera directive missed custom orbit prompt")
    krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(row), target="single")
    prompt = krea.get("krea_prompt") or ""
    _expect("Coworking camera layout" in prompt, "Krea single prompt lost camera-scene directive")
    _expect("45-degree front-right quarter view" in prompt, "Krea single prompt lost camera directive")
    _expect_formatter_outputs(row, "camera_scene_single", target="single")

    custom_location = pb.build_location_pool_json(
        enabled=True,
        combine_mode="replace",
        preset="custom_only",
        custom_locations=json.dumps(
            {
                "slug": "greenhouse_suite",
                "prompt": "private room with soft daylight",
                "camera_profile": {
                    "key": "glass_conservatory",
                    "family": "greenhouse",
                    "layout_label": "Glass conservatory camera layout",
                    "place": "glass conservatory",
                    "foreground": "plant shelf edge, fern leaves, and iron table corner",
                    "midground": "glass panes, iron ribs, and potted palms",
                    "background": "hanging vines, greenhouse windows, and layered plant depth",
                    "detail_label": "conservatory details",
                    "composition": {
                        "woman": "glass conservatory frame with the woman beside fern leaves and greenhouse depth behind her",
                        "default": "glass conservatory frame with the subjects beside fern leaves and greenhouse depth behind them",
                    },
                },
            },
            sort_keys=True,
        ),
    )
    custom_composition = pb.build_composition_pool_json(
        enabled=True,
        combine_mode="replace",
        preset="custom_only",
        custom_compositions=json.dumps({"prompt": "polished mirror view with bag and shoes visible"}, sort_keys=True),
    )
    custom_row = _prompt_row(
        name="camera_scene_custom_inline_profile",
        category="woman",
        subcategory="random",
        seed=1061,
        men_count=0,
        camera_config=_orbit_camera(
            horizontal_angle=45,
            vertical_angle=30,
            zoom=5.0,
            subject_focus="environment",
        ),
        location_config=custom_location,
        composition_config=custom_composition,
    )
    custom_scene = _expect_text(
        "camera_scene_custom_inline_profile.camera_scene_directive",
        custom_row.get("camera_scene_directive"),
        40,
    )
    custom_composition_text = _expect_text(
        "camera_scene_custom_inline_profile.composition",
        custom_row.get("composition"),
        20,
    )
    _expect("Glass conservatory camera layout" in custom_scene, "custom Location Pool JSON camera profile did not drive scene layout")
    _expect(custom_row.get("scene_camera_profile_key") == "glass_conservatory", "custom Location Pool JSON profile key was not exposed")
    _expect(custom_row.get("scene_entry", {}).get("camera_profile", {}).get("family") == "greenhouse", "custom Location Pool JSON profile metadata was not preserved")
    _expect("glass conservatory" in custom_composition_text.lower(), "custom Location Pool JSON profile did not clean composition")
    _expect("bag" not in custom_composition_text.lower() and "shoes" not in custom_composition_text.lower(), "custom inline profile composition leaked unrelated props")


def smoke_scene_camera_adapter_pov_profile_policy() -> None:
    parsed_camera = {
        "camera_mode": "standard",
        "camera_detail": "compact",
        "orbit_direction": "front-right quarter view",
        "orbit_elevation_label": "elevated shot",
        "orbit_distance_label": "medium shot",
        "custom_camera_prompt": "front-right quarter view, elevated shot, medium shot",
    }
    for profile in scene_camera_adapters.SCENE_CAMERA_PROFILES:
        key = str(profile.get("key") or "")
        foreground = str(profile.get("foreground") or "")
        non_pov = _expect_text(
            f"scene_camera_adapter_pov_profile_policy.{key}.non_pov",
            scene_camera_adapters.scene_camera_directive(
                "",
                parsed_camera,
                pov_labels=[],
                subject_kind="couple",
                profile_key=key,
            ),
            40,
        )
        pov = _expect_text(
            f"scene_camera_adapter_pov_profile_policy.{key}.pov",
            scene_camera_adapters.scene_camera_directive(
                "",
                parsed_camera,
                pov_labels=["Man A"],
                subject_kind="couple",
                profile_key=key,
            ),
            40,
        )
        _expect(foreground and foreground in non_pov, f"{key} non-POV scene directive lost profile foreground anchor")
        _expect(foreground not in pov, f"{key} POV scene directive reused profile foreground anchor as viewer-side text")
        _expect("from POV" in pov, f"{key} POV scene directive lost POV marker")
        _expect(
            "POV body or hand cues stay in the lower foreground" in pov,
            f"{key} POV scene directive lost lower-foreground body-cue reservation",
        )
    plural_directive = scene_camera_adapters.scene_camera_directive(
        "",
        parsed_camera,
        pov_labels=[],
        subject_kind="subjects",
        profile_key="coworking_lounge",
    )
    _expect(
        "the subjects are placed" in plural_directive,
        "scene camera adapter used singular grammar for plural subjects",
    )
    _expect("the subjects is" not in plural_directive, "scene camera adapter leaked 'the subjects is'")


def smoke_row_camera_policy() -> None:
    row = {
        "prompt": "A generated adult prompt. Composition: vertical office-lobby walking composition. Avoid: low quality.",
        "caption": "sxcppnl7, generated adult prompt, office-lobby walking composition, illustration",
        "scene_text": "coworking lounge with tall windows, warm desks, and a polished outfit-check angle",
        "composition": "office-lobby walking composition",
        "subject_type": "configured_cast",
        "women_count": 1,
        "men_count": 1,
        "pov_character_labels": ["Man A"],
    }
    updated = row_camera.apply_camera_config(
        row,
        _orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5),
        compact_labels=pb.CAMERA_COMPACT_LABELS,
    )
    _expect(updated.get("camera_directive") == "", "POV row camera policy should suppress normal camera directive")
    scene_directive = _expect_text("row_camera_policy.camera_scene_directive", updated.get("camera_scene_directive"), 40)
    _expect("Coworking camera layout from POV" in scene_directive, "row camera policy missed POV coworking layout")
    _expect("POV body or hand cues stay in the lower foreground" in scene_directive, "row camera policy lost POV foreground cue")
    _expect("use the multiangle camera" not in scene_directive, "row camera policy leaked internal multiangle instruction")
    _expect("Camera:" not in updated.get("prompt", ""), "row camera policy should not add normal Camera label")
    _expect("45-degree front-right quarter view" not in updated.get("caption", ""), "POV row camera policy should not append camera caption")
    _expect(
        "coworking lounge frame with the couple near a desk edge" in updated.get("composition", ""),
        "row camera policy did not adapt coworking composition for couple rows",
    )
    pov_action_row = dict(row)
    pov_action_row["composition"] = (
        "first-person rear-view frame looking down at the woman's raised ass, "
        "with foreground hands and rear-entry contact readable"
    )
    pov_action_row["prompt"] = (
        "A generated adult prompt. Framed as first-person rear-view frame looking down at the woman's raised ass, "
        "with foreground hands and rear-entry contact readable. Avoid: low quality."
    )
    updated_pov_action = row_camera.apply_camera_config(
        pov_action_row,
        _orbit_camera(horizontal_angle=180, vertical_angle=-30, zoom=7.5),
        compact_labels=pb.CAMERA_COMPACT_LABELS,
    )
    _expect(
        "first-person rear-view frame" in updated_pov_action.get("composition", ""),
        "row camera policy replaced explicit POV action composition with generic location composition",
    )
    _expect(
        "couple's raised ass" not in updated_pov_action.get("composition", ""),
        "row camera policy rewrote explicit female action anatomy as couple anatomy",
    )
    _expect(
        "coworking lounge frame with the couple near a desk edge" not in updated_pov_action.get("composition", ""),
        "row camera policy leaked generic coworking composition into explicit POV action frame",
    )
    _expect(
        str(updated_pov_action.get("camera_scene_directive", "")).count(";") <= 3,
        "POV camera scene directive became too noisy",
    )
    already_matching_row = dict(row)
    already_matching_row["pov_character_labels"] = []
    already_matching_row["composition"] = "coworking lounge frame with the subjects near a desk edge and tall-window depth behind them"
    already_matching_row["prompt"] = (
        "A generated adult prompt. Framed as coworking lounge frame with the subjects near a desk edge and tall-window depth behind them. "
        "Avoid: low quality."
    )
    updated_matching = row_camera.apply_camera_config(
        already_matching_row,
        _orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5),
        compact_labels=pb.CAMERA_COMPACT_LABELS,
    )
    _expect(
        "the couple near a desk edge" in str(updated_matching.get("composition", "")),
        "row camera policy did not adapt generic matching composition subject wording",
    )
    _expect(
        "the subjects near a desk edge" not in str(updated_matching.get("prompt", "")),
        "row camera policy left generic matching composition subject wording in prompt",
    )
    pre_normalized_row = dict(row)
    pre_normalized_row["pov_character_labels"] = []
    pre_normalized_row["composition"] = "coworking lounge frame with the woman near a desk edge and tall-window depth behind them"
    pre_normalized_row["prompt"] = (
        "A generated adult prompt. Framed as coworking lounge frame with the woman near a desk edge and tall-window depth behind them. "
        "Avoid: low quality."
    )
    updated_pre_normalized = row_camera.apply_camera_config(
        pre_normalized_row,
        _orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5),
        compact_labels=pb.CAMERA_COMPACT_LABELS,
    )
    _expect(
        "the couple near a desk edge" in str(updated_pre_normalized.get("composition", "")),
        "row camera policy did not adapt pre-normalized woman composition for couple rows",
    )
    _expect(
        "the woman near a desk edge" not in str(updated_pre_normalized.get("prompt", "")),
        "row camera policy left pre-normalized woman composition wording in prompt",
    )
    stale_internal_row = dict(row)
    stale_internal_row["pov_character_labels"] = []
    stale_internal_row["composition"] = "camera-aware coworking lounge frame with subjects near a desk edge"
    stale_internal_row["prompt"] = (
        "A generated adult prompt. Framed as camera-aware coworking lounge frame with subjects near a desk edge. "
        "Avoid: low quality."
    )
    updated_stale_internal = row_camera.apply_camera_config(
        stale_internal_row,
        _orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5),
        compact_labels=pb.CAMERA_COMPACT_LABELS,
    )
    _expect(
        "camera-aware" not in str(updated_stale_internal.get("composition", "")).lower(),
        "row camera policy leaked internal camera-aware composition wording",
    )
    _expect(
        "camera-aware" not in str(updated_stale_internal.get("prompt", "")).lower(),
        "row camera policy left internal camera-aware wording in prompt",
    )
    library_row = {
        "prompt": "A generated adult prompt. Composition: vertical polished mirror view with bag and shoes visible. Avoid: low quality.",
        "caption": "sxcppnl7, generated adult prompt, polished mirror view with bag and shoes visible, illustration",
        "scene_text": "grand classical library hall with towering dark-wood bookshelves, carved columns, rolling ladders, marble floor, and warm brass lamps",
        "composition": "polished mirror view with bag and shoes visible",
        "subject_type": "woman",
        "women_count": 1,
        "men_count": 0,
    }
    updated_library = row_camera.apply_camera_config(
        library_row,
        _orbit_camera(horizontal_angle=315, vertical_angle=0, zoom=5.0),
        compact_labels=pb.CAMERA_COMPACT_LABELS,
    )
    library_scene = _expect_text("row_camera_policy.library_scene", updated_library.get("camera_scene_directive"), 40)
    library_composition = _expect_text("row_camera_policy.library_composition", updated_library.get("composition"), 20)
    _expect("Library camera layout" in library_scene, "row camera policy missed library layout")
    _expect("front-left quarter view" in library_scene, "row camera library layout missed orbit direction")
    _expect("bookshelf" in library_scene.lower() or "bookshelves" in library_scene.lower(), "row camera library layout missed shelf anchors")
    _expect("bag" not in library_composition.lower(), "row camera library composition leaked bag wording")
    _expect("shoes" not in library_composition.lower(), "row camera library composition leaked shoes wording")
    _expect("library" in library_composition.lower(), "row camera library composition did not become location-aware")
    semi_public_row = {
        "prompt": "A generated adult prompt. Composition: vertical polished mirror view with bag and shoes visible. Avoid: low quality.",
        "caption": "sxcppnl7, generated adult prompt, polished mirror view with bag and shoes visible, illustration",
        "scene_text": "upscale hotel corridor with repeating numbered doors, patterned carpet, brass wall lamps, luggage carts, and a secluded corner near a service alcove",
        "scene_entry": {
            "slug": "hotel_corridor_affair",
            "prompt": "upscale hotel corridor with repeating numbered doors, patterned carpet, brass wall lamps, luggage carts, and a secluded corner near a service alcove",
            "theme": "semi_public_affair",
        },
        "scene_theme": "semi_public_affair",
        "composition": "polished mirror view with bag and shoes visible",
        "subject_type": "configured_cast",
        "women_count": 1,
        "men_count": 1,
        "pov_character_labels": ["Man A"],
    }
    updated_semi_public = row_camera.apply_camera_config(
        semi_public_row,
        _orbit_camera(horizontal_angle=180, vertical_angle=30, zoom=7.5),
        compact_labels=pb.CAMERA_COMPACT_LABELS,
    )
    semi_public_scene = _expect_text("row_camera_policy.semi_public_scene", updated_semi_public.get("camera_scene_directive"), 40)
    semi_public_composition = _expect_text(
        "row_camera_policy.semi_public_composition",
        updated_semi_public.get("composition"),
        20,
    )
    _expect("Hotel corridor camera layout from POV" in semi_public_scene, "row camera semi-public scene did not use hotel corridor profile")
    _expect("back view" not in semi_public_scene, "POV camera scene should not inject orbit back view as body orientation")
    _expect("partner's back" not in semi_public_scene, "POV camera scene should not force the visible partner's back")
    _expect("POV body or hand cues stay in the lower foreground" in semi_public_scene, "row camera semi-public POV scene lost foreground cue")
    _expect(updated_semi_public.get("scene_camera_profile_key") == "hotel_corridor", "row camera semi-public scene did not expose text-matched profile key")
    _expect("hotel corridor" in semi_public_composition.lower(), "row camera semi-public composition did not become location-aware")
    _expect("bag" not in semi_public_composition.lower() and "shoes" not in semi_public_composition.lower(), "row camera semi-public composition leaked outfit-check props")
    metadata_profile_row = {
        "prompt": "A generated adult prompt. Composition: vertical polished mirror view with bag and shoes visible. Avoid: low quality.",
        "caption": "sxcppnl7, generated adult prompt, polished mirror view with bag and shoes visible, illustration",
        "scene_text": "private themed room with neutral walls and warm lamps",
        "scene_entry": {
            "slug": "library_by_metadata",
            "prompt": "private themed room with neutral walls and warm lamps",
            "theme": "classical_library",
        },
        "scene_theme": "classical_library",
        "composition": "polished mirror view with bag and shoes visible",
        "subject_type": "woman",
        "women_count": 1,
        "men_count": 0,
    }
    updated_metadata_profile = row_camera.apply_camera_config(
        metadata_profile_row,
        _orbit_camera(horizontal_angle=315, vertical_angle=0, zoom=5.0),
        compact_labels=pb.CAMERA_COMPACT_LABELS,
    )
    metadata_scene = _expect_text(
        "row_camera_policy.metadata_scene",
        updated_metadata_profile.get("camera_scene_directive"),
        40,
    )
    _expect("Library camera layout" in metadata_scene, "row camera should prefer scene theme metadata over generic scene text")
    _expect(
        updated_metadata_profile.get("scene_camera_profile_key") == "classical_library",
        "row camera should expose metadata-selected profile key",
    )
    _expect(
        "library" in str(updated_metadata_profile.get("composition", "")).lower(),
        "row camera metadata-selected profile did not clean composition",
    )
    explicit_profile_row = {
        "prompt": "A generated adult prompt. Composition: vertical polished mirror view with bag and shoes visible. Avoid: low quality.",
        "caption": "sxcppnl7, generated adult prompt, polished mirror view with bag and shoes visible, illustration",
        "scene_text": "coworking lounge with tall windows, warm desks, and glass partitions",
        "scene_camera_profile_key": "classical_library",
        "composition": "polished mirror view with bag and shoes visible",
        "subject_type": "woman",
        "women_count": 1,
        "men_count": 0,
    }
    updated_explicit_profile = row_camera.apply_camera_config(
        explicit_profile_row,
        _orbit_camera(horizontal_angle=315, vertical_angle=0, zoom=5.0),
        compact_labels=pb.CAMERA_COMPACT_LABELS,
    )
    _expect(
        "Library camera layout" in str(updated_explicit_profile.get("camera_scene_directive", "")),
        "explicit scene_camera_profile_key should override text-matched scene profile",
    )
    inline_profile_row = {
        "prompt": "A generated adult prompt. Composition: vertical polished mirror view with bag and shoes visible. Avoid: low quality.",
        "caption": "sxcppnl7, generated adult prompt, polished mirror view with bag and shoes visible, illustration",
        "scene_text": "private room with soft daylight",
        "scene_entry": {
            "slug": "greenhouse_room",
            "prompt": "private room with soft daylight",
            "camera_profile": {
                "key": "glass_conservatory",
                "family": "greenhouse",
                "layout_label": "Glass conservatory camera layout",
                "place": "glass conservatory",
                "foreground": "plant shelf edge, fern leaves, and iron table corner",
                "midground": "glass panes, iron ribs, and potted palms",
                "background": "hanging vines, greenhouse windows, and layered plant depth",
                "detail_label": "conservatory details",
                "composition": {
                    "woman": "glass conservatory frame with the woman beside fern leaves and greenhouse depth behind her",
                    "default": "glass conservatory frame with the subjects beside fern leaves and greenhouse depth behind them",
                },
            },
        },
        "composition": "polished mirror view with bag and shoes visible",
        "subject_type": "woman",
        "women_count": 1,
        "men_count": 0,
    }
    updated_inline_profile = row_camera.apply_camera_config(
        inline_profile_row,
        _orbit_camera(horizontal_angle=45, vertical_angle=30, zoom=5.0),
        compact_labels=pb.CAMERA_COMPACT_LABELS,
    )
    inline_scene = _expect_text(
        "row_camera_policy.inline_profile_scene",
        updated_inline_profile.get("camera_scene_directive"),
        40,
    )
    inline_composition = _expect_text(
        "row_camera_policy.inline_profile_composition",
        updated_inline_profile.get("composition"),
        20,
    )
    inline_profile = updated_inline_profile.get("scene_camera_profile") if isinstance(updated_inline_profile.get("scene_camera_profile"), dict) else {}
    _expect("Glass conservatory camera layout" in inline_scene, "inline scene camera profile did not drive camera layout")
    _expect(updated_inline_profile.get("scene_camera_profile_key") == "glass_conservatory", "inline profile key was not exposed")
    _expect(inline_profile.get("family") == "greenhouse", "inline profile family was not exposed")
    _expect("glass conservatory" in inline_composition.lower(), "inline profile did not drive composition cleanup")
    _expect("bag" not in inline_composition.lower() and "shoes" not in inline_composition.lower(), "inline profile composition leaked unrelated props")

    beach_profile = scene_camera_adapters.scene_camera_profile(
        "beach cafe table with woven chairs, linen shade, and ocean light in the background",
        scene_entry={"slug": "beach_cafe_table"},
    )
    subway_profile = scene_camera_adapters.scene_camera_profile(
        "clean subway platform with tiled walls, overhead lights, and a quiet selfie corner",
        scene_entry={"slug": "subway_tile_selfie_corner"},
    )
    apartment_profile = scene_camera_adapters.scene_camera_profile(
        "sunny apartment corner with bookshelves, a warm rug, and a phone on a small tripod",
        scene_entry={"slug": "sunny_apartment_phone_tripod"},
    )
    _expect(not beach_profile, "scene camera resolver should not classify beach cafe as business cafe")
    _expect(not subway_profile, "scene camera resolver should not classify subway tile as station lockers")
    _expect(not apartment_profile, "scene camera resolver should not classify apartment bookshelves as classical library")

    mirror_profile = scene_camera_adapters.scene_camera_profile(
        "large bedroom mirror with the phone visible, bed behind the subject, and warm side lamps",
        scene_entry={"slug": "large_bedroom_mirror_selfie"},
    )
    studio_profile = scene_camera_adapters.scene_camera_profile(
        "dark private studio with glossy black floor reflections, rim light, and a phone tripod",
        scene_entry={"slug": "black_latex_studio_floor"},
    )
    _expect(mirror_profile.get("key") == "mirror_room", "scene slug resolver missed mirror-room profile")
    _expect(studio_profile.get("key") == "private_studio", "scene slug resolver missed private-studio profile")


def smoke_config_route_location_theme() -> None:
    location_config, composition_config = _classical_library_theme_configs()
    row = pb.build_prompt_from_configs(
        row_number=1,
        start_index=1,
        seed=3301,
        category_config=pb.build_category_config_json("hardcore_pose", "Foreplay and teasing"),
        cast_config=pb.build_cast_config_json("mixed_couple"),
        generation_profile=pb.build_generation_profile_json(
            profile="hardcore_intense",
            trigger_policy="prepend_trigger",
        ),
        filter_config=pb.build_ethnicity_list_json(
            include_french_european=True,
            strict_excludes=True,
        )["filter_config"],
        seed_config=pb.build_seed_lock_config_json(
            base_seed=3301,
            reroll_axis="pose",
            reroll_seed=3302,
        ),
        camera_config=_orbit_camera(
            horizontal_angle=315,
            vertical_angle=0,
            zoom=5.0,
            subject_focus="action",
        ),
        character_cast=_character_cast(),
        hardcore_position_config=_action_filter("foreplay_only"),
        location_config=location_config,
        composition_config=composition_config,
    )
    _expect_custom_row(row, "config_route_location_theme")
    _expect(row.get("subcategory") == "Foreplay and teasing", "config route did not preserve requested subcategory")
    _expect(row.get("subject_type") == "configured_cast", "config route did not apply character cast")
    scene = _expect_text("config_route_location_theme.scene_text", row.get("scene_text"), 20)
    composition = _expect_text("config_route_location_theme.composition", row.get("composition"), 10)
    camera = _expect_text("config_route_location_theme.camera_directive", row.get("camera_directive"), 20)
    scene_directive = _expect_text("config_route_location_theme.camera_scene_directive", row.get("camera_scene_directive"), 40)
    scene_profile = row.get("scene_camera_profile") if isinstance(row.get("scene_camera_profile"), dict) else {}
    _expect("library" in scene.lower() or "bookshelves" in scene.lower(), "location theme did not drive scene")
    _expect("books" in composition.lower() or "shelf" in composition.lower() or "library" in composition.lower(), "location theme did not drive composition")
    _expect(row.get("location_theme") == "classical_library", "location theme did not survive into row metadata")
    _expect(row.get("scene_theme") == "classical_library", "selected scene theme did not survive into row metadata")
    _expect(row.get("composition_theme") == "classical_library", "composition theme did not survive into row metadata")
    _expect(row.get("scene_entry", {}).get("theme") == "classical_library", "selected scene entry lost theme metadata")
    _expect("Library camera layout" in scene_directive, "location theme did not drive library camera-scene adapter")
    _expect(row.get("scene_camera_profile_key") == "classical_library", "row lost scene camera profile key")
    _expect(scene_profile.get("family") == "library", "row lost scene camera profile family")
    _expect("front-left quarter view" in scene_directive, "library camera-scene adapter missed orbit direction")
    _expect("bag" not in composition.lower() and "shoes" not in composition.lower(), "location theme composition leaked outfit-check props")
    _expect("315-degree front-left quarter view" in camera, "config route did not preserve orbit camera directive")
    seed_config = row.get("seed_config") if isinstance(row.get("seed_config"), dict) else {}
    _expect(seed_config.get("pose_seed") == 3302, "seed lock did not reroll pose axis")
    _expect(seed_config.get("role_seed") == 3302, "seed lock did not reroll role axis")
    _expect(row.get("trigger") == "sxcpinup_coloredpencil", "generation profile trigger did not apply")
    _expect_trigger_once("config_route_location_theme.prompt", row.get("prompt"), "sxcpinup_coloredpencil")
    typed_route = krea_configured_cast_formatter.format_configured_cast_result(
        krea_formatter._krea_configured_cast_request_from_row(row, "balanced", "preserve"),
        krea_formatter._krea_configured_cast_dependencies(),
    )
    legacy_route = krea_formatter._normal_row_to_krea(row, "balanced", "preserve")
    _expect(
        typed_route.as_tuple() == legacy_route,
        "Typed Krea configured-cast formatter route should match legacy wrapper output",
    )
    krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(row), target="single")
    prompt = krea.get("krea_prompt") or ""
    _expect("library" in prompt.lower() or "bookshelves" in prompt.lower(), "Krea config route lost theme scene")
    _expect("Library camera layout" in prompt, "Krea config route lost library camera-scene directive")
    _expect("315-degree front-left quarter view" in prompt, "Krea config route lost camera directive")
    _expect_formatter_outputs(row, "config_route_location_theme", target="single")

    parking_location_config, parking_composition_config = _thematic_location_configs("parking_garage")
    parking_row = pb.build_prompt_from_configs(
        row_number=1,
        start_index=1,
        seed=3311,
        category_config=pb.build_category_config_json("woman", "random"),
        cast_config=pb.build_cast_config_json("solo_woman", 1, 0),
        generation_profile=pb.build_generation_profile_json(profile="balanced"),
        camera_config=_orbit_camera(
            horizontal_angle=135,
            vertical_angle=-30,
            zoom=4.0,
            subject_focus="environment",
        ),
        location_config=parking_location_config,
        composition_config=parking_composition_config,
    )
    _expect_row_base(parking_row, "config_route_location_theme.parking")
    parking_scene = _expect_text("config_route_location_theme.parking_scene", parking_row.get("scene_text"), 20)
    parking_composition = _expect_text("config_route_location_theme.parking_composition", parking_row.get("composition"), 10)
    parking_directive = _expect_text(
        "config_route_location_theme.parking_camera_scene_directive",
        parking_row.get("camera_scene_directive"),
        40,
    )
    parking_profile = parking_row.get("scene_camera_profile") if isinstance(parking_row.get("scene_camera_profile"), dict) else {}
    _expect("parking" in parking_scene.lower() or "garage" in parking_scene.lower(), "parking theme did not drive scene")
    _expect("parking" in parking_composition.lower() or "garage" in parking_composition.lower() or "pillar" in parking_composition.lower(), "parking theme did not drive composition")
    _expect(parking_row.get("location_theme") == "parking_garage", "parking location theme did not survive")
    _expect(parking_row.get("scene_theme") == "parking_garage", "parking scene theme did not survive")
    _expect(parking_row.get("main_category") == "woman", "parking built-in woman preset fell back to another category")
    _expect(parking_row.get("primary_subject") == "woman", "parking built-in woman preset did not generate a woman")
    _expect(parking_row.get("scene_camera_profile_key") == "parking_garage", "parking theme did not expose camera profile key")
    _expect(parking_profile.get("family") == "semi_public", "parking camera profile family should be semi_public")
    _expect("Parking garage camera layout" in parking_directive, "parking theme did not drive camera-scene adapter")
    _expect("back-right quarter view" in parking_directive, "parking camera-scene adapter missed orbit direction")
    _expect("low-angle shot" in parking_directive, "parking camera-scene adapter missed elevation")

    creator_location_config, creator_composition_config = _thematic_location_configs("creator_bedroom")
    creator_row = pb.build_prompt_from_configs(
        row_number=1,
        start_index=1,
        seed=3321,
        category_config=pb.build_category_config_json("woman", "random"),
        cast_config=pb.build_cast_config_json("solo_woman", 1, 0),
        generation_profile=pb.build_generation_profile_json(profile="balanced"),
        camera_config=_orbit_camera(
            horizontal_angle=45,
            vertical_angle=0,
            zoom=5.5,
            subject_focus="environment",
        ),
        location_config=creator_location_config,
        composition_config=creator_composition_config,
    )
    _expect_row_base(creator_row, "config_route_location_theme.creator")
    creator_scene = _expect_text("config_route_location_theme.creator_scene", creator_row.get("scene_text"), 20)
    creator_composition = _expect_text("config_route_location_theme.creator_composition", creator_row.get("composition"), 10)
    creator_directive = _expect_text(
        "config_route_location_theme.creator_camera_scene_directive",
        creator_row.get("camera_scene_directive"),
        40,
    )
    creator_profile = creator_row.get("scene_camera_profile") if isinstance(creator_row.get("scene_camera_profile"), dict) else {}
    _expect("creator" in creator_scene.lower() or "phone" in creator_scene.lower(), "creator theme did not drive scene")
    _expect(
        any(token in creator_composition.lower() for token in ("creator", "tripod", "phone", "bed")),
        "creator theme did not drive composition",
    )
    _expect(creator_row.get("location_theme") == "creator_bedroom", "creator location theme did not survive")
    _expect(creator_row.get("scene_theme") == "creator_bedroom", "creator scene theme did not survive")
    _expect(creator_row.get("main_category") == "woman", "creator built-in woman preset fell back to another category")
    _expect(creator_row.get("primary_subject") == "woman", "creator built-in woman preset did not generate a woman")
    _expect(creator_row.get("scene_camera_profile_key") == "creator_bedroom", "creator theme did not expose camera profile key")
    _expect(creator_profile.get("family") == "private_creator", "creator camera profile family should be private_creator")
    _expect("Creator room camera layout" in creator_directive, "creator theme did not drive camera-scene adapter")
    _expect("front-right quarter view" in creator_directive, "creator camera-scene adapter missed orbit direction")
    creator_krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(creator_row), target="single")
    creator_prompt = creator_krea.get("krea_prompt") or ""
    _expect("Creator room camera layout" in creator_prompt, "Krea config route lost creator camera-scene directive")


def smoke_builder_prompt_route_policy() -> None:
    def legacy_from_request(request: builder_prompt_route.PromptBuildRequest) -> dict[str, Any]:
        return pb.build_prompt(
            category=request.category,
            subcategory=request.subcategory,
            row_number=request.row_number,
            start_index=request.start_index,
            seed=request.seed,
            clothing=request.clothing,
            ethnicity=request.ethnicity,
            poses=request.poses,
            backside_bias=request.backside_bias,
            figure=request.figure,
            no_plus_women=request.no_plus_women,
            no_black=request.no_black,
            minimal_clothing_ratio=request.minimal_clothing_ratio,
            standard_pose_ratio=request.standard_pose_ratio,
            trigger=request.trigger,
            prepend_trigger_to_prompt=request.prepend_trigger_to_prompt,
            extra_positive=request.extra_positive,
            extra_negative=request.extra_negative,
            seed_config=request.seed_config,
            women_count=request.women_count,
            men_count=request.men_count,
            camera_config=request.camera_config,
            expression_intensity=request.expression_intensity,
            character_profile=request.character_profile,
            character_cast=request.character_cast,
            expression_enabled=request.expression_enabled,
            expression_phase=request.expression_phase,
            hardcore_position_config=request.hardcore_position_config,
            location_config=request.location_config,
            composition_config=request.composition_config,
        )

    seed_config_json = pb.build_seed_lock_config_json(base_seed=3501, reroll_axis="content", reroll_seed=3502)
    request = builder_prompt_route.PromptBuildRequest(
        category="Casual clothes",
        subcategory="Casual clothes / Smart casual",
        row_number=3,
        start_index=8,
        seed=3501,
        clothing="random",
        ethnicity="french_european",
        poses="random",
        backside_bias=0.2,
        figure="random",
        no_plus_women=False,
        no_black=False,
        minimal_clothing_ratio=0.3,
        standard_pose_ratio=0.4,
        trigger="sxcpinup_coloredpencil",
        prepend_trigger_to_prompt=True,
        extra_positive="typed builder route marker",
        extra_negative="typed builder negative marker",
        seed_config=seed_config_json,
        women_count=1,
        men_count=0,
        camera_config=_orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5),
        expression_intensity=0.6,
        expression_enabled=True,
    )
    typed_route = builder_prompt_route.build_prompt_result(request, pb._prompt_build_dependencies())
    legacy_row = legacy_from_request(request)
    _expect(typed_route.row == legacy_row, "Typed builder prompt route should match public wrapper output")
    _expect(typed_route.category == "Casual clothes", "Builder prompt route changed category")
    _expect(typed_route.subcategory == "Casual clothes / Smart casual", "Builder prompt route changed subcategory")
    _expect(typed_route.branch == "custom", "Builder prompt route should use custom branch for category JSON route")
    _expect(typed_route.parsed_seed_config.get("content_seed") == 3502, "Builder prompt route lost seed config")
    custom_trace = typed_route.row.get("generation_trace")
    _expect(isinstance(custom_trace, dict), "Builder custom route lost generation_trace")
    _expect(custom_trace.get("branch") == "custom", "Builder custom generation_trace lost branch")
    _expect(custom_trace.get("source") == "json_category", "Builder custom generation_trace lost source")
    _expect(custom_trace.get("category_slug") == "casual_clothes", "Builder custom generation_trace lost category slug")
    _expect(custom_trace.get("content_seed_axis") == "content", "Builder custom generation_trace lost content axis")
    _expect(custom_trace.get("seed_axes", {}).get("content", {}).get("source") == "configured", "Builder custom generation_trace lost configured content seed")
    _expect(custom_trace.get("seed_axes", {}).get("content", {}).get("seed") == 3502, "Builder custom generation_trace lost content seed value")
    _expect("typed builder route marker" in typed_route.row.get("prompt", ""), "Builder prompt route lost extra positive")
    _expect("typed builder negative marker" in typed_route.row.get("negative_prompt", ""), "Builder prompt route lost extra negative")
    _expect(
        "45-degree front-right quarter view" in typed_route.row.get("camera_directive", ""),
        "Builder prompt route lost camera config",
    )
    _expect_trigger_once("builder_prompt_route_policy.prompt", typed_route.row.get("prompt"), "sxcpinup_coloredpencil")

    built_in_request = builder_prompt_route.PromptBuildRequest(
        category="woman",
        subcategory="random",
        row_number=1,
        start_index=1,
        seed=3503,
        clothing="full",
        ethnicity="any",
        poses="standard",
        backside_bias=0.0,
        figure="curvy",
        no_plus_women=False,
        no_black=False,
        minimal_clothing_ratio=0.0,
        standard_pose_ratio=1.0,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        extra_positive="built-in route marker",
        extra_negative="built-in route negative",
        expression_intensity=0.5,
        expression_enabled=False,
    )
    built_in_route = builder_prompt_route.build_prompt_result(built_in_request, pb._prompt_build_dependencies())
    _expect(built_in_route.row == legacy_from_request(built_in_request), "Builder built-in route should match public wrapper")
    _expect(built_in_route.branch == "built_in", "Builder prompt route lost built-in branch")
    _expect(built_in_route.row.get("source") == "built_in_generator", "Builder built-in branch changed source")
    built_in_trace = built_in_route.row.get("generation_trace")
    _expect(isinstance(built_in_trace, dict), "Builder built-in route lost generation_trace")
    _expect(built_in_trace.get("branch") == "built_in", "Builder built-in generation_trace lost branch")
    _expect(built_in_trace.get("source") == "built_in_generator", "Builder built-in generation_trace lost source")
    _expect(built_in_trace.get("seed_axes", {}).get("person", {}).get("source") == "main", "Builder built-in generation_trace should follow main seed")
    _expect(built_in_route.row.get("expression_disabled") is True, "Builder built-in branch lost expression disable")
    _expect("built-in route marker" in built_in_route.row.get("prompt", ""), "Builder built-in branch lost extra positive")

    auto_weighted_request = builder_prompt_route.PromptBuildRequest(
        category="auto_weighted",
        subcategory="random",
        row_number=2,
        start_index=10,
        seed=3504,
        clothing="random",
        ethnicity="any",
        poses="random",
        backside_bias=0.35,
        figure="random",
        no_plus_women=False,
        no_black=False,
        minimal_clothing_ratio=0.4,
        standard_pose_ratio=0.6,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        extra_positive="auto route marker",
        extra_negative="auto route negative",
        seed_config=pb.build_seed_lock_config_json(base_seed=3504, reroll_axis="person", reroll_seed=3505),
        expression_intensity=0.7,
        expression_enabled=True,
    )
    auto_route = builder_prompt_route.build_prompt_result(auto_weighted_request, pb._prompt_build_dependencies())
    _expect(auto_route.row == legacy_from_request(auto_weighted_request), "Builder auto-weighted route should match public wrapper")
    _expect(auto_route.branch == "auto_weighted", "Builder prompt route lost auto-weighted branch")
    _expect(auto_route.parsed_seed_config.get("person_seed") == 3505, "Builder auto-weighted branch lost person seed lock")
    auto_trace = auto_route.row.get("generation_trace")
    _expect(isinstance(auto_trace, dict), "Builder auto-weighted route lost generation_trace")
    _expect(auto_trace.get("branch") == "auto_weighted", "Builder auto-weighted generation_trace lost branch")
    _expect(auto_trace.get("seed_axes", {}).get("person", {}).get("source") == "configured", "Builder auto-weighted trace lost configured person seed")
    _expect(auto_trace.get("seed_axes", {}).get("person", {}).get("seed") == 3505, "Builder auto-weighted trace lost person seed")
    _expect("auto route marker" in auto_route.row.get("prompt", ""), "Builder auto-weighted branch lost extra positive")


def smoke_builder_config_route_policy() -> None:
    category_config = pb.build_category_config_json("women_casual", "Casual clothes / Smart casual")
    cast_config = pb.build_cast_config_json("solo_woman")
    generation_profile = pb.build_generation_profile_json(
        profile="casual_clean",
        trigger_policy="prepend_trigger",
    )
    filter_config = pb.build_filter_config_json(
        ethnicity="french_european",
        figure="balanced",
    )
    seed_config_json = pb.build_seed_lock_config_json(base_seed=3401, reroll_axis="scene", reroll_seed=3402)
    request = builder_config_route.PromptFromConfigsRequest(
        row_number=2,
        start_index=5,
        seed=3401,
        category_config=category_config,
        cast_config=cast_config,
        generation_profile=generation_profile,
        filter_config=filter_config,
        seed_config=seed_config_json,
        extra_positive="clean route marker",
        extra_negative="bad route marker",
    )
    typed_route = builder_config_route.build_prompt_from_configs_result(
        request,
        pb._prompt_from_configs_dependencies(),
    )
    legacy_row = pb.build_prompt_from_configs(
        row_number=request.row_number,
        start_index=request.start_index,
        seed=request.seed,
        category_config=category_config,
        cast_config=cast_config,
        generation_profile=generation_profile,
        filter_config=filter_config,
        seed_config=seed_config_json,
        extra_positive=request.extra_positive,
        extra_negative=request.extra_negative,
    )
    _expect(typed_route.row == legacy_row, "Prompt Builder From Configs route should match public wrapper output")
    _expect(typed_route.category == "Casual clothes", "Config route lost category preset")
    _expect(typed_route.subcategory == "Casual clothes / Smart casual", "Config route lost requested subcategory")
    _expect(typed_route.cast["women_count"] == 1 and typed_route.cast["men_count"] == 0, "Config route lost cast preset")
    _expect(typed_route.profile["trigger"] == "sxcpinup_coloredpencil", "Config route lost generation profile trigger")
    _expect(typed_route.filters["ethnicity"] == "french_european", "Config route lost filter ethnicity")
    config_trace = typed_route.row.get("generation_trace")
    _expect(isinstance(config_trace, dict), "Config route row lost generation_trace")
    _expect(config_trace.get("branch") == "custom", "Config route generation_trace lost builder branch")
    _expect(config_trace.get("seed_axes", {}).get("scene", {}).get("source") == "configured", "Config route generation_trace lost scene seed lock")
    _expect(config_trace.get("seed_axes", {}).get("scene", {}).get("seed") == 3402, "Config route generation_trace lost scene reroll seed")
    kwargs = typed_route.build_kwargs
    _expect(kwargs["category"] == typed_route.category, "Config route build kwargs category drifted")
    _expect(kwargs["subcategory"] == typed_route.subcategory, "Config route build kwargs subcategory drifted")
    _expect(kwargs["women_count"] == 1 and kwargs["men_count"] == 0, "Config route build kwargs cast counts drifted")
    _expect(kwargs["seed_config"] == seed_config_json, "Config route build kwargs seed config drifted")
    _expect(kwargs["extra_positive"] == "clean route marker", "Config route build kwargs extra positive drifted")
    _expect("clean route marker" in typed_route.row.get("prompt", ""), "Config route row lost extra positive")
    _expect("bad route marker" in typed_route.row.get("negative_prompt", ""), "Config route row lost extra negative")


def smoke_krea_normal_row_routes() -> None:
    single = {
        "subject_type": "woman",
        "primary_subject": "woman",
        "age_band": "25-year-old adult",
        "body_phrase": "slim figure",
        "skin": "fair skin",
        "hair": "long blonde hair",
        "eyes": "blue eyes",
        "item": "silk dress",
        "pose": "standing beside a window",
        "scene_text": "quiet studio with warm daylight",
        "camera_scene_directive": "Camera-aware studio layout with window depth.",
        "expression": "soft smile",
        "composition": "vertical centered portrait",
        "camera_directive": "Camera: eye-level medium shot",
        "style": "realistic creator-shot photography",
    }
    _expect_krea_normal_route_parity(single, "krea_normal_single", "metadata(single)")
    single_figure_note = dict(
        single,
        body_phrase="slim busty figure with soft, natural fullness up top and a small waist",
    )
    figure_note_prompt, figure_note_method = krea_formatter._normal_row_to_krea(
        single_figure_note,
        "balanced",
        "preserve",
    )
    _expect(figure_note_method == "metadata(single)", "Krea single figure-note route changed method")
    _expect(
        "adult woman, with slim busty figure with" not in figure_note_prompt,
        "Krea single route kept old comma-with figure grammar",
    )
    _expect(
        "adult woman with a slim busty figure defined by soft" in figure_note_prompt,
        "Krea single route did not attach figure-note appearance to the subject",
    )
    _expect(
        "adult woman with a slim busty figure with" not in figure_note_prompt,
        "Krea single route kept nested figure-with wording",
    )
    _expect(
        "adult woman, showing slim busty figure with" not in figure_note_prompt,
        "Krea single route kept awkward showing figure-note grammar",
    )
    _expect(
        "warm daylight, Camera-aware studio layout" not in figure_note_prompt,
        "Krea single route joined camera-scene directive to scene with a comma",
    )
    _expect(
        "warm daylight. Camera-aware studio layout" in figure_note_prompt,
        "Krea single route did not split camera-scene directive into its own sentence",
    )
    style_metadata = dict(
        single,
        style="metadata style phrase",
        positive_suffix="metadata suffix phrase",
        prompt="Use: stale prompt suffix phrase.",
    )
    style_prompt, style_method = krea_formatter._normal_row_to_krea(style_metadata, "balanced", "preserve")
    _expect(style_method == "metadata(single)", "Krea style metadata route changed method")
    style_prompt_lower = style_prompt.lower()
    _expect("metadata style phrase" in style_prompt_lower, "Krea metadata route lost structured style")
    _expect("metadata suffix phrase" in style_prompt_lower, "Krea metadata route lost structured positive suffix")
    _expect("stale prompt suffix" not in style_prompt_lower, "Krea metadata route parsed stale Use prompt text")

    couple = {
        "subject_type": "couple",
        "primary_subject": "a woman and a man",
        "subject_phrase": "woman and man",
        "age": "25-year-old adult and 40-year-old adult",
        "body": "slim and average builds",
        "item": "Partner A wears black dress; Partner B wears dark shirt",
        "pose": "standing close together",
        "scene_text": "private lounge with soft lamps",
        "expression": "shared confident gaze",
        "composition": "two-person editorial frame",
        "camera_directive": "Camera: front view, medium shot",
        "style": "realistic social photo",
    }
    _expect_krea_normal_route_parity(couple, "krea_normal_couple", "metadata(couple)")

    generic = {
        "subject_type": "location",
        "primary_subject": "adult editorial scene",
        "item": "polished lounge styling",
        "scene_text": "hotel hallway with warm wall sconces",
        "expression": "quiet atmosphere",
        "composition": "wide establishing frame",
        "camera_directive": "Camera: wide shot",
        "style": "clean photographic realism",
    }
    _expect_krea_normal_route_parity(generic, "krea_normal_generic", "metadata(generic)")

    configured_with_descriptor = _fixture_hardcore_row(
        prompt="Characters: stale prompt subject, stale body, stale skin, stale hair, stale eyes.",
        cast_descriptor_text=(
            "Woman A: 30-year-old adult woman, toned figure, fair skin, red hair, gray eyes; "
            "Man A: 45-year-old adult man, average figure, tan skin, dark hair"
        ),
    )
    descriptor_prompt, descriptor_method = krea_formatter._normal_row_to_krea(
        configured_with_descriptor,
        "balanced",
        "preserve",
    )
    _expect(descriptor_method == "metadata(configured_cast)", "Krea configured-cast route changed method")
    _expect("30-year-old adult woman" in descriptor_prompt, "Krea configured-cast route lost descriptor metadata")
    _expect("toned figure" in descriptor_prompt, "Krea configured-cast route lost descriptor body metadata")
    _expect("stale" not in descriptor_prompt, "Krea configured-cast route parsed stale prompt character labels")

    configured_without_descriptor = _fixture_hardcore_row(
        prompt="Characters: stale prompt subject, stale body, stale skin, stale hair, stale eyes.",
        cast_descriptor_text="",
        cast_descriptors=[],
    )
    no_descriptor_prompt, no_descriptor_method = krea_formatter._normal_row_to_krea(
        configured_without_descriptor,
        "balanced",
        "preserve",
    )
    _expect(no_descriptor_method == "metadata(configured_cast)", "Krea configured-cast no-descriptor route changed method")
    _expect("stale" not in no_descriptor_prompt, "Krea configured-cast route should not parse prompt labels without metadata")
    _expect("private studio room with warm light" in no_descriptor_prompt, "Krea configured-cast route lost structured scene")


def smoke_krea_action_details_policy() -> None:
    _expect(
        krea_action_details.strip_redundant_position_detail(
            "kneeling penis-licking position while slow tongue licking on the underside of the penis"
        )
        == "slow tongue licking on the underside of the penis",
        "Krea action detail cleanup should remove leading position-while scaffolding",
    )
    _expect(
        krea_action_details.strip_redundant_position_detail(
            "raised edge fingering position featuring mutual masturbation with both bodies touching themselves"
        )
        == "mutual masturbation with both bodies touching themselves",
        "Krea action detail cleanup should remove leading position-featuring scaffolding",
    )
    _expect(
        krea_action_details.strip_redundant_position_detail(
            "footjob with toes curled around the penis shaft in seated footjob position"
        )
        == "footjob with toes curled around the penis shaft",
        "Krea action detail cleanup should remove trailing in-position scaffolding",
    )
    _expect(
        "position while"
        not in krea_action_details.dedupe_outercourse_detail(
            "kneeling penis-licking position while slow tongue licking on the underside of the penis",
            "the woman bends forward between the man's open thighs",
            "penis licking",
            {"position": "kneeling penis-licking position"},
        ).lower(),
        "Krea outercourse detail cleanup leaked position-while scaffolding",
    )
    _expect(
        "position featuring"
        not in krea_action_details.sanitize_foreplay_detail(
            "raised edge fingering position featuring mutual masturbation with both bodies touching themselves",
            "the woman and man sit close facing each other",
        ).lower(),
        "Krea foreplay/manual detail cleanup leaked position-featuring scaffolding",
    )
    _expect(
        krea_action_details.dedupe_anchor_detail(
            "side-lying anal position, one leg lifted high",
            "side-lying rear-entry anal pose",
        )
        == "one leg lifted high",
        "Krea anchored detail cleanup should remove repeated anal position prefix",
    )


def smoke_outercourse_action_policy() -> None:
    _expect(
        outercourse_action_policy.infer_outercourse_action_kind("kneeling boobjob position")
        == outercourse_action_policy.OUTERCOURSE_BOOBJOB,
        "Outercourse classifier lost boobjob position detection",
    )
    _expect(
        outercourse_action_policy.infer_outercourse_action_kind("bent-over balls-licking position")
        == outercourse_action_policy.OUTERCOURSE_TESTICLE,
        "Outercourse classifier lost testicle/balls position detection",
    )
    _expect(
        outercourse_action_policy.infer_outercourse_action_kind("tongue runs along the underside of the penis")
        == outercourse_action_policy.OUTERCOURSE_PENIS_LICKING,
        "Outercourse classifier lost penis-licking detail detection",
    )
    _expect(
        outercourse_action_policy.infer_outercourse_action_kind("two-handed handjob with thumb and fingers around the penis")
        == outercourse_action_policy.OUTERCOURSE_HANDJOB,
        "Outercourse classifier lost handjob detection",
    )
    _expect(
        outercourse_action_policy.infer_outercourse_action_kind("footjob with toes curled around the penis")
        == outercourse_action_policy.OUTERCOURSE_FOOTJOB,
        "Outercourse classifier lost footjob detection",
    )
    axis_text = krea_action_context.axis_values_text(
        {
            "outer_act": "handjob with one hand wrapped around the penis",
            "contact_detail": "hand wrapped around the penis shaft with the glans visible",
            "visibility": "hand and penis centered in frame",
        }
    )
    _expect("handjob" in axis_text and "hand and penis" in axis_text, "Krea action context lost outercourse axes")
    role_graph = hardcore_role_outercourse.build_outercourse_role_graph(
        "Woman A",
        "Man A",
        "testicle sucking with lips around the balls",
        {"position": "bent-over testicle-sucking position"},
        ["Man A"],
    )
    lower_role = role_graph.lower()
    _expect("side-pelvis pov" in lower_role, "POV testicle role graph lost side-low geometry")
    _expect("cheek against the pov viewer's inner thigh" in lower_role, "POV testicle role graph lost cheek/thigh geometry")
    _expect("scrotum is the mouth surface" in lower_role, "POV testicle role graph lost scrotum-mouth surface target")
    _expect("scrotal skin is the nearest mouth surface" in lower_role, "POV testicle role graph lost scrotal-skin nearest-surface target")
    _expect("testicles resting across her open lips" in lower_role, "POV testicle role graph lost open-lips testicle target")
    _expect("both testicles rest against her tongue from below" in lower_role, "POV testicle role graph lost tongue-from-below target")
    deduped = krea_action_details.dedupe_outercourse_detail(
        "testicle sucking with lips around the balls, balls and mouth contact visible, wet lips and tongue contact",
        role_graph,
        "testicle sucking with lips around the balls",
        {"position": "bent-over testicle-sucking position"},
    ).lower()
    _expect("testicle" not in deduped and "balls and mouth" not in deduped, "Krea outercourse dedupe kept redundant testicle clauses")
    _expect("wet lips" in deduped, "Krea outercourse dedupe removed useful texture clause")


def smoke_item_axis_policy() -> None:
    axis_values = {
        "ignored": "random",
        "position": "kneeling oral position",
        "contact_detail": {"text": "mouth contact at hip height"},
        "nested": {"unused": "fallback body detail"},
        "list_detail": ["hands on hips", "auto"],
        "unprioritized_detail": "extra unprioritized cue",
    }
    texts = item_axis_policy.axis_value_texts(axis_values)
    _expect("kneeling oral position" in texts, "Item axis policy lost position value")
    _expect("mouth contact at hip height" in texts, "Item axis policy lost preferred dict text")
    _expect("fallback body detail" in texts, "Item axis policy lost nested fallback text")
    _expect("hands on hips" in texts, "Item axis policy lost list text")
    _expect("random" not in texts and "auto" not in texts, "Item axis policy leaked placeholder values")
    _expect(
        item_axis_policy.axis_value_texts(axis_values, existing_text="kneeling oral position already present")[0]
        == "mouth contact at hip height",
        "Item axis policy should skip details already present in existing text",
    )
    context_text = item_axis_policy.action_context_text(axis_values)
    _expect("kneeling oral position" in context_text, "Item axis policy context lost priority position")
    _expect("mouth contact at hip height" in context_text, "Item axis policy context lost priority contact")
    _expect("extra unprioritized cue" not in context_text, "Item axis policy context should ignore unprioritized values")
    _expect(
        krea_action_context.axis_values_text(axis_values) == context_text,
        "Krea action context should delegate to shared item axis policy",
    )


def smoke_krea_row_fields_policy() -> None:
    row = {
        "subject_type": "configured_cast",
        "primary_subject": "woman",
        "cast_summary": "1 woman, 1 man",
        "item": "lace bodysuit fashion editorial styling",
        "pose": "standing close together",
        "scene_text": "private room with warm lamps",
        "expression": "soft smile",
        "expression_enabled": False,
        "composition": "vertical tight two-person frame",
        "source_composition": "vertical source action frame",
        "camera_directive": "Camera: eye-level close-up",
        "camera_scene_directive": "Camera-aware scene layout.",
        "style": "realistic social photo",
    }
    fields = krea_row_fields.extract_krea_row_fields(
        row,
        "preserve",
        krea_formatter._krea_row_field_dependencies(),
    )
    normal_request = krea_formatter._krea_normal_row_request_from_row(row, "balanced", "preserve")
    cast_request = krea_formatter._krea_configured_cast_request_from_row(row, "balanced", "preserve")
    _expect(fields.item == "lace bodysuit", "Krea row fields did not strip generic styling suffix")
    _expect(fields.expression == "", "Krea row fields ignored expression disabled flag")
    _expect(fields.composition == "tight two-person frame", "Krea row fields did not normalize composition prefix")
    _expect(fields.source_composition == "source action frame", "Krea row fields did not normalize source composition")
    _expect(normal_request.item == fields.item, "Normal Krea route item extraction drifted")
    _expect(cast_request.item == fields.item, "Configured-cast Krea route item extraction drifted")
    _expect(normal_request.expression == cast_request.expression == fields.expression, "Krea route expression extraction drifted")
    _expect(normal_request.camera == cast_request.camera == fields.camera, "Krea route camera extraction drifted")
    _expect(cast_request.source_composition == fields.source_composition, "Configured-cast source composition drifted")


def smoke_location_config_policy() -> None:
    _expect(pb.LOCATION_POOL_PRESETS is location_config.LOCATION_POOL_PRESETS, "Prompt builder location presets are not delegated")
    _expect(pb.COMPOSITION_POOL_PRESETS is location_config.COMPOSITION_POOL_PRESETS, "Prompt builder composition presets are not delegated")
    _expect("classical_library" in location_config.location_theme_choices(), "Location themes lost classical_library")
    _expect("creator_bedroom" in location_config.location_theme_choices(), "Location themes lost creator_bedroom")
    _expect("mirror_room" in location_config.location_theme_choices(), "Location themes lost mirror_room")
    _expect("workspace_lounge" in location_config.location_theme_choices(), "Location themes lost workspace_lounge")
    _expect("fetish_studio" in location_config.location_theme_choices(), "Location themes lost fetish_studio")

    custom = json.loads(
        pb.build_location_pool_json(
            enabled=True,
            combine_mode="replace",
            preset="custom_only",
            custom_locations="custom_room: a quiet room with warm lamps",
        )
    )
    _expect(custom.get("enabled") is True, "Custom location config should be active")
    _expect(custom.get("apply_mode") == "replace", "Custom location config lost replace mode")
    _expect(custom.get("scene_entries", [{}])[0].get("slug") == "custom_room", "Custom location slug parser changed")
    structured_custom = json.loads(
        pb.build_location_pool_json(
            enabled=True,
            combine_mode="replace",
            preset="custom_only",
            custom_locations=json.dumps(
                {
                    "slug": "structured_room",
                    "text": "structured room with preserved metadata",
                    "camera_profile": {
                        "key": "structured_camera_profile",
                        "foreground": "foreground test anchor",
                        "midground": "middle test anchor",
                        "background": "background test anchor",
                    },
                },
                sort_keys=True,
            ),
        )
    )
    structured_entry = structured_custom.get("scene_entries", [{}])[0]
    _expect(structured_entry.get("slug") == "structured_room", "Structured custom location lost slug")
    _expect(structured_entry.get("prompt") == "structured room with preserved metadata", "Structured custom location did not normalize prompt text")
    _expect(structured_entry.get("camera_profile", {}).get("key") == "structured_camera_profile", "Structured custom location lost camera profile metadata")

    added = json.loads(
        location_config.build_location_pool_json(
            enabled=True,
            combine_mode="add",
            preset="custom_only",
            custom_locations="second_room: another quiet room",
            location_config=custom,
        )
    )
    _expect(added.get("apply_mode") == "replace", "Location add merge should preserve incoming apply_mode")
    _expect(len(added.get("scene_entries") or []) == 2, "Location add merge did not keep both custom locations")

    composition = json.loads(
        pb.build_composition_pool_json(
            enabled=True,
            combine_mode="replace",
            preset="no_outfit_check",
            custom_compositions="manual frame through foreground bookshelves",
        )
    )
    _expect(composition.get("enabled") is True, "Composition config should be active")
    _expect(
        any("outfit-check" in str(entry) for entry in composition.get("composition_entries") or []),
        "Composition inline preset no_outfit_check was not applied",
    )
    structured_composition = json.loads(
        pb.build_composition_pool_json(
            enabled=True,
            combine_mode="replace",
            preset="custom_only",
            custom_compositions=json.dumps({"text": "structured composition frame", "source": "json_line"}, sort_keys=True),
        )
    )
    structured_composition_entry = structured_composition.get("composition_entries", [{}])[0]
    _expect(structured_composition_entry.get("prompt") == "structured composition frame", "Structured custom composition did not normalize prompt text")
    _expect(structured_composition_entry.get("source") == "json_line", "Structured custom composition lost metadata")
    parsed = pb._parse_location_config({"enabled": True, "pool_names": [], "scene_entries": custom["scene_entries"]})
    _expect(pb._location_config_active(parsed), "Prompt builder location parser wrapper is inactive")

    themed_location, themed_composition, theme_summary = pb.build_thematic_location_json(
        enabled=True,
        combine_mode="replace",
        theme="classical_library",
    )
    _expect("classical_library" in theme_summary, "Themed location summary lost theme name")
    themed_location_payload = json.loads(themed_location)
    themed_composition_payload = json.loads(themed_composition)
    _expect(themed_location_payload.get("scene_entries"), "Themed location did not output locations")
    _expect(themed_location_payload.get("theme") == "classical_library", "Themed location config lost theme metadata")
    _expect(
        all(
            not isinstance(entry, dict) or entry.get("theme") == "classical_library"
            for entry in themed_location_payload.get("scene_entries") or []
        ),
        "Themed location entries lost theme metadata",
    )
    _expect(themed_composition_payload.get("composition_entries"), "Themed location did not output compositions")
    _expect(themed_composition_payload.get("theme") == "classical_library", "Themed composition config lost theme metadata")
    parsed_themed = pb._parse_location_config(themed_location_payload)
    _expect(parsed_themed.get("theme") == "classical_library", "Location parser lost theme metadata")
    workspace_location, workspace_composition, workspace_summary = pb.build_thematic_location_json(
        enabled=True,
        combine_mode="replace",
        theme="workspace_lounge",
    )
    workspace_location_payload = json.loads(workspace_location)
    workspace_composition_payload = json.loads(workspace_composition)
    _expect("workspace_lounge" in workspace_summary, "Workspace theme summary lost theme name")
    _expect(
        workspace_location_payload.get("scene_entries", [{}])[0].get("slug") == "coworking_lounge_window",
        "Workspace theme lost coworking lounge location slug",
    )
    _expect(workspace_composition_payload.get("composition_entries"), "Workspace theme did not output compositions")
    workspace_compositions_text = " ".join(str(entry) for entry in workspace_composition_payload.get("composition_entries") or [])
    _expect("camera-aware" not in workspace_compositions_text.lower(), "Workspace theme leaked internal camera-aware wording")
    replaced_after_theme = json.loads(
        location_config.build_location_pool_json(
            enabled=True,
            combine_mode="replace",
            preset="custom_only",
            custom_locations="plain_room: plain room after theme",
            location_config=themed_location_payload,
        )
    )
    _expect(replaced_after_theme.get("theme") == "", "Location replace mode should not inherit upstream theme metadata")
    replaced_composition_after_theme = json.loads(
        location_config.build_composition_pool_json(
            enabled=True,
            combine_mode="replace",
            preset="custom_only",
            custom_compositions="plain composition after theme",
            composition_config=themed_composition_payload,
        )
    )
    _expect(
        replaced_composition_after_theme.get("theme") == "",
        "Composition replace mode should not inherit upstream theme metadata",
    )


def smoke_row_location_policy() -> None:
    location = json.loads(
        location_config.build_location_pool_json(
            combine_mode="replace",
            custom_locations="archive_corner: hidden archive corner with repeated shelves and warm table lamps",
        )
    )
    composition = json.loads(
        location_config.build_composition_pool_json(
            combine_mode="replace",
            custom_compositions="long archive aisle composition",
        )
    )
    row = {
        "source": "built_in_generator",
        "primary_subject": "adult woman",
        "scene": "unknown_old_scene",
        "composition": "old frame",
        "prompt": "A generated adult prompt. Scene: old room. Pose: standing. Composition: vertical old frame. Avoid: low quality.",
        "caption": "sxcppnl7, generated adult prompt, old room, old frame, illustration",
    }
    updated = row_location.apply_location_config_to_legacy_row(dict(row), location, {}, 123, 1)
    updated = row_location.apply_composition_config_to_legacy_row(updated, composition, {}, 123, 1)
    _expect(updated.get("scene") == "archive_corner", "Row location policy did not select forced custom scene slug")
    _expect(
        updated.get("scene_text") == "hidden archive corner with repeated shelves and warm table lamps",
        "Row location policy did not apply forced custom scene text",
    )
    _expect(updated.get("source_scene") == "unknown_old_scene", "Row location policy lost source scene slug")
    _expect(updated.get("scene_entry", {}).get("slug") == "archive_corner", "Row location policy lost selected scene entry")
    _expect(
        "Scene: hidden archive corner with repeated shelves and warm table lamps. Pose:" in updated.get("prompt", ""),
        "Row location policy did not rewrite prompt scene",
    )
    _expect(updated.get("composition") == "long archive aisle composition", "Row location policy did not apply forced composition")
    _expect(
        updated.get("composition_entry", {}).get("prompt") == "long archive aisle composition",
        "Row location policy lost selected composition entry",
    )
    _expect(
        updated.get("composition_prompt") == "vertical long archive aisle composition",
        "Row location policy did not compute composition prompt",
    )
    _expect(
        "Composition: vertical long archive aisle composition." in updated.get("prompt", ""),
        "Row location policy did not rewrite prompt composition",
    )
    _expect(", long archive aisle composition," in updated.get("caption", ""), "Row location policy did not rewrite caption composition")


def smoke_row_expression_policy() -> None:
    entries = [
        {"text": "quiet calm focus", "weight": 2.0},
        {"text": "heated smirk", "weight": 3.0},
        {"text": "dazed overwhelmed look", "weight": 1.0},
    ]
    _expect(
        pb._expression_entries_for_intensity(entries, 0.25) == row_expression.expression_entries_for_intensity(entries, 0.25),
        "Prompt builder expression intensity wrapper should delegate to row_expression",
    )
    weighted = row_expression.expression_entries_for_intensity(entries, 0.25)
    _expect(weighted[0]["weight"] == 8.0, "Row expression low-intensity weighting changed")
    _expect(weighted[2]["weight"] == 0.05, "Row expression distant-intensity weighting changed")

    row = {
        "prompt": "Person with quiet focus, in room. Facial expression: quiet focus. Keep this.",
        "caption": "sxcppnl7, person with quiet focus, in room",
        "expression": "quiet focus",
        "shared_expression": "quiet focus",
        "character_expressions": ["Woman A has quiet focus"],
        "character_expression_text": "Woman A has quiet focus",
    }
    _expect(
        pb._disable_row_expression(dict(row), "test") == row_expression.disable_row_expression(dict(row), "test"),
        "Prompt builder expression disable wrapper should delegate to row_expression",
    )
    disabled = row_expression.disable_row_expression(dict(row), "test")
    _expect(disabled.get("expression_disabled") is True, "Row expression disable did not set disabled metadata")
    _expect("quiet focus" not in disabled.get("prompt", ""), "Row expression disable did not strip prompt expression text")

    woman_slot = character_slot.normalize_character_slot(
        {
            "subject_type": "woman",
            "label": "A",
            "expression_intensity": 0.8,
            "softcore_expression_intensity": 0.2,
        }
    )
    pov_man_slot = character_slot.normalize_character_slot(
        {
            "subject_type": "man",
            "label": "A",
            "presence_mode": "pov",
            "expression_intensity": 1.0,
        }
    )
    label_map = {"Woman A": woman_slot, "Man A": pov_man_slot}
    _expect(
        pb._cast_expression_intensity_override(0.5, label_map, 1, 1, "softcore")
        == row_expression.cast_expression_intensity_override(0.5, label_map, 1, 1, "softcore"),
        "Prompt builder cast expression override wrapper should delegate to row_expression",
    )
    _expect(
        row_expression.cast_expression_intensity_override(0.5, label_map, 1, 1, "softcore")
        == (0.2, "character_slot:Woman A"),
        "Row expression cast override did not prefer visible slot phase intensity",
    )
    _expect(
        pb._resolve_expression_route(
            expression_enabled=True,
            expression_intensity=0.5,
            expression_intensity_source="input",
            subject_type="woman",
            applied_slot=woman_slot,
            women_count=1,
            men_count=0,
            expression_phase="softcore",
        )
        == row_expression.resolve_expression_route(
            expression_enabled=True,
            expression_intensity=0.5,
            expression_intensity_source="input",
            subject_type="woman",
            applied_slot=woman_slot,
            women_count=1,
            men_count=0,
            expression_phase="softcore",
        ),
        "Prompt builder expression route wrapper should delegate to row_expression",
    )
    route = row_expression.resolve_expression_route(
        expression_enabled=True,
        expression_intensity=0.5,
        expression_intensity_source="input",
        subject_type="woman",
        applied_slot=woman_slot,
        women_count=1,
        men_count=0,
        expression_phase="softcore",
    )
    _expect(route.expression_intensity == 0.2, "Expression route did not apply phase-specific slot intensity")
    _expect(route.expression_intensity_source == "character_slot:Woman A", "Expression route lost slot source")
    _expect(
        pb._character_expression_entries(random.Random(22), entries, 0.5, label_map, 1, 1, "softcore")
        == row_expression.character_expression_entries(random.Random(22), entries, 0.5, label_map, 1, 1, "softcore"),
        "Prompt builder character expression wrapper should delegate to row_expression",
    )

    disabled_slot = character_slot.normalize_character_slot(
        {"subject_type": "woman", "label": "A", "expression_enabled": False}
    )
    _expect(
        row_expression.cast_expression_intensity_override(0.5, {"Woman A": disabled_slot}, 1, 0, "hardcore")
        == (None, "character_slots:disabled"),
        "Row expression cast override did not honor all-slot expression disable",
    )
    global_disabled = row_expression.resolve_expression_route(
        expression_enabled=False,
        expression_intensity=0.8,
        expression_intensity_source="input",
        subject_type="woman",
        applied_slot=woman_slot,
    )
    _expect(global_disabled.expression_disabled is True, "Expression route did not honor global disabled state")
    _expect(global_disabled.expression_intensity == 0.8, "Expression route changed disabled fallback intensity too early")
    _expect(global_disabled.expression_intensity_source == "disabled", "Expression route did not mark global disabled source")
    slot_disabled = row_expression.resolve_expression_route(
        expression_enabled=True,
        expression_intensity=0.5,
        expression_intensity_source="input",
        subject_type="woman",
        applied_slot=disabled_slot,
    )
    _expect(slot_disabled.expression_disabled is True, "Expression route did not honor single-slot disable")
    _expect(
        slot_disabled.expression_intensity_source == "character_slot:Woman A:disabled",
        "Expression route lost single-slot disabled source",
    )
    cast_disabled = row_expression.resolve_expression_route(
        expression_enabled=True,
        expression_intensity=0.5,
        expression_intensity_source="input",
        subject_type="configured_cast",
        character_slots=[disabled_slot],
        character_slot_map={"Woman A": disabled_slot},
        women_count=1,
        men_count=0,
        expression_phase="hardcore",
    )
    _expect(cast_disabled.expression_disabled is True, "Expression route did not honor all-slot cast disable")
    _expect(cast_disabled.expression_intensity is None, "Expression route did not clear all-slot disabled intensity")
    _expect(
        cast_disabled.expression_intensity_source == "character_slots:disabled",
        "Expression route lost all-slot disabled source",
    )
    expression_text = "Woman A has steady focus; Man A has parted lips with saliva"
    context_role = "Woman A performs a handjob while Man A stands close"
    _expect(
        pb._sanitize_character_expression_text_for_action(expression_text, context_role, "", {})
        == row_expression.sanitize_character_expression_text_for_action(expression_text, context_role, "", {})
        == "Woman A has steady focus",
        "Row expression action sanitizer did not remove incompatible partner expression",
    )
    reverse_context = "Man A has mouth pressed to her pussy while Woman A lies back"
    _expect(
        row_expression.sanitize_character_expression_text_for_action(
            "Woman A has tongue out; Man A has focused mouth contact",
            reverse_context,
            "",
            {},
        )
        == "Man A has focused mouth contact",
        "Row expression action sanitizer did not remove incompatible visible-subject expression",
    )


def smoke_row_prompt_axes_policy() -> None:
    category = {
        "name": "Axis Test",
        "slug": "axis_test",
        "scenes": [{"slug": "studio", "prompt": "quiet studio with repeatable anchors"}],
        "poses": ["standing fallback pose"],
        "expressions": ["quiet focus", "heated smirk"],
        "compositions": ["all participants visible centered frame"],
    }
    subcategory = {"name": "Axis Sub", "slug": "axis_sub", "items": ["axis item"]}
    item = "axis item"
    base_kwargs = {
        "category": category,
        "subcategory": subcategory,
        "item": item,
        "subject_type": "woman",
        "context": {"fallback_pose": ""},
        "poses": "standard",
        "women_count": 1,
        "men_count": 0,
        "expression_disabled": True,
        "expression_intensity": 0.5,
        "is_pose_category": False,
        "location_config": {},
        "composition_config": {},
    }
    route = row_prompt_axes.resolve_prompt_axes(
        **base_kwargs,
        scene_rng=random.Random(1),
        pose_rng=random.Random(2),
        expression_rng=random.Random(3),
        composition_rng=random.Random(4),
    )
    route_result = row_prompt_axes.resolve_prompt_axes_result(
        **base_kwargs,
        scene_rng=random.Random(1),
        pose_rng=random.Random(2),
        expression_rng=random.Random(3),
        composition_rng=random.Random(4),
    )
    delegated = pb._prompt_axes_route(
        **base_kwargs,
        scene_rng=random.Random(1),
        pose_rng=random.Random(2),
        expression_rng=random.Random(3),
        composition_rng=random.Random(4),
    )
    typed_delegated = pb._prompt_axes_route_result(
        **base_kwargs,
        scene_rng=random.Random(1),
        pose_rng=random.Random(2),
        expression_rng=random.Random(3),
        composition_rng=random.Random(4),
    )
    _expect(delegated == route, "Prompt builder prompt-axes route should delegate to row_prompt_axes")
    _expect(route_result.as_dict() == route, "Typed prompt axes route should match legacy dict route")
    _expect(typed_delegated == route_result, "Prompt builder typed prompt-axes route should delegate")
    _expect(route_result.scene_slug == "studio", "Typed prompt axes route lost selected scene slug")
    _expect(route["scene_slug"] == "studio", "Prompt axes route lost selected scene slug")
    _expect(route["scene"] == "quiet studio with repeatable anchors", "Prompt axes route lost selected scene text")
    _expect(route["scene_entry"].get("slug") == "studio", "Prompt axes route lost selected scene entry slug")
    _expect(
        route["scene_entry"].get("prompt") == "quiet studio with repeatable anchors",
        "Prompt axes route lost selected scene entry prompt",
    )
    _expect(route["pose"] == "standing fallback pose", "Prompt axes route lost selected fallback pose")
    _expect(route["expression"] == "", "Prompt axes route should omit expression when disabled")
    _expect(route["shared_expression"] == "", "Prompt axes route should omit shared expression when disabled")
    _expect(route["source_composition"] == "all participants visible centered frame", "Prompt axes route lost source composition")
    _expect(
        route["composition_entry"].get("prompt") == "all participants visible centered frame",
        "Prompt axes route lost selected composition entry",
    )

    pov_route = row_prompt_axes.resolve_prompt_axes(
        **{**base_kwargs, "expression_disabled": True},
        scene_rng=random.Random(1),
        pose_rng=random.Random(2),
        expression_rng=random.Random(3),
        composition_rng=random.Random(4),
        pov_character_labels=["Man A"],
    )
    _expect("first-person POV" in pov_route["composition"], "Prompt axes route did not adapt composition for POV")

    woman_slot = character_slot.normalize_character_slot(
        {"subject_type": "woman", "label": "A", "expression_intensity": 0.25}
    )
    configured_route = row_prompt_axes.resolve_prompt_axes(
        **{
            **base_kwargs,
            "subject_type": "configured_cast",
            "context": {"subject_type": "configured_cast"},
            "expression_disabled": False,
            "character_slots": [woman_slot],
            "character_slot_map": {"Woman A": woman_slot},
        },
        scene_rng=random.Random(1),
        pose_rng=random.Random(2),
        expression_rng=random.Random(3),
        composition_rng=random.Random(4),
    )
    _expect(
        configured_route["character_expression_text"].startswith("Woman A has "),
        "Prompt axes route lost configured-cast character expression",
    )
    _expect(
        configured_route["expression"] == configured_route["character_expression_text"],
        "Prompt axes route did not promote character expression text to row expression",
    )


def smoke_row_item_policy() -> None:
    weighted_entries = [
        {"text": "first", "weight": 0.0},
        {"text": "second", "weight": 4.0},
        {"text": "third", "weight": 1.0},
    ]
    _expect(
        pb._weighted_choice(random.Random(44), weighted_entries) == row_item.weighted_choice(random.Random(44), weighted_entries),
        "Prompt builder weighted item choice should delegate to row_item",
    )
    _expect(
        pb._pair_from({"name": "Library Corner", "description": "carved shelves and warm lamps"})
        == row_item.pair_from({"name": "Library Corner", "description": "carved shelves and warm lamps"}),
        "Prompt builder pair parser should delegate to row_item",
    )

    oral_values = [
        "fellatio with penis in mouth",
        "cunnilingus with tongue on pussy",
        "sixty-nine mutual oral",
    ]
    _expect(
        pb._oral_acts_for_position(oral_values, "kneeling oral position")
        == row_item.oral_acts_for_position(oral_values, "kneeling oral position")
        == ["fellatio with penis in mouth"],
        "Row item oral act filtering changed for kneeling oral",
    )
    outer_values = [
        "titjob with compressed breasts",
        "handjob with palm around shaft",
        "footjob with soles and toes",
    ]
    _expect(
        pb._outercourse_acts_for_position(outer_values, "footjob position")
        == row_item.outercourse_acts_for_position(outer_values, "footjob position")
        == ["footjob with soles and toes"],
        "Row item outercourse act filtering changed for footjob",
    )
    texture_values = [
        "fingers pressing around shaft",
        "soles pressing around shaft",
        "tongue wet against shaft",
    ]
    _expect(
        row_item.outercourse_axis_values_for_position(texture_values, "footjob position", "texture_detail")
        == ["soles pressing around shaft"],
        "Row item outercourse texture axis should prefer footjob-compatible details",
    )
    anal_leg_values = [
        "standing with legs braced",
        "one leg lifted high",
        "kneeling with thighs apart",
        "knees pressed to chest",
    ]
    _expect(
        pb._anal_axis_values_for_position(anal_leg_values, "side-lying anal position", "leg_detail")
        == row_item.anal_axis_values_for_position(anal_leg_values, "side-lying anal position", "leg_detail")
        == ["one leg lifted high"],
        "Row item anal leg-detail filtering changed for side-lying anal",
    )

    category = {}
    subcategory = {
        "name": "Oral sex",
        "slug": "oral_sex",
        "item_templates": [
            {
                "template": "{oral_act}; {hand_detail}",
                "action_family": "oral",
                "position_family": "oral",
            }
        ],
        "item_axes": {
            "position": ["kneeling oral position"],
            "oral_act": oral_values,
            "hand_detail": ["hands on hips", "spreading thighs"],
        },
    }
    item = "Oral sex"
    builder_result = pb._compose_item(random.Random(7), category, subcategory, item, women_count=1, men_count=1)
    policy_result = row_item.compose_item(random.Random(7), category, subcategory, item, women_count=1, men_count=1)
    _expect(builder_result == policy_result, "Prompt builder compose item should delegate to row_item")
    item_text, item_name, axis_values, metadata = policy_result
    _expect(item_name == "Oral sex", "Row item compose lost item name")
    _expect("fellatio" in item_text, "Row item compose did not apply oral act compatibility filter")
    _expect(axis_values.get("hand_detail") == "hands on hips", "Row item compose did not apply oral detail filter")
    _expect(metadata.get("action_family") == "oral", "Row item compose lost template metadata")

    anal_subcategory = {
        "name": "Anal and double penetration",
        "slug": "anal_double_penetration",
        "item_templates": [
            {
                "template": "{anal_act} in {position}, with {leg_detail}",
                "action_family": "default",
                "position_family": "anal",
            }
        ],
        "item_axes": {
            "position": ["side-lying anal position"],
            "anal_act": ["penis entering ass"],
            "leg_detail": anal_leg_values,
        },
    }
    anal_text, _anal_name, anal_axis_values, _anal_metadata = row_item.compose_item(
        random.Random(5),
        {},
        anal_subcategory,
        "Anal and double penetration",
        women_count=1,
        men_count=1,
    )
    _expect("standing with legs braced" not in anal_text, "Row item compose leaked standing legs into side-lying anal")
    _expect(anal_axis_values.get("leg_detail") == "one leg lifted high", "Row item compose did not apply anal leg-detail filter")


def smoke_row_category_route_policy() -> None:
    hard_config = hardcore_position_config.parse_hardcore_position_config(_position_filter("oral_only", "oral", ["kneeling"]))
    seed_cfg = seed_config.parse_seed_config({})
    route = row_category_route.select_category_item_route(
        category_choice="custom_random",
        subcategory_choice="Hardcore sexual poses / Oral sex",
        seed_config=seed_cfg,
        seed=2301,
        row_number=1,
        women_count=1,
        men_count=1,
        hardcore_position_config=hard_config,
    )
    route_result = row_category_route.select_category_item_route_result(
        category_choice="custom_random",
        subcategory_choice="Hardcore sexual poses / Oral sex",
        seed_config=seed_cfg,
        seed=2301,
        row_number=1,
        women_count=1,
        men_count=1,
        hardcore_position_config=hard_config,
    )
    delegated = pb._select_category_item_route(
        category_choice="custom_random",
        subcategory_choice="Hardcore sexual poses / Oral sex",
        seed_config=seed_cfg,
        seed=2301,
        row_number=1,
        women_count=1,
        men_count=1,
        hardcore_position_config=hard_config,
    )
    typed_delegated = pb._select_category_item_route_result(
        category_choice="custom_random",
        subcategory_choice="Hardcore sexual poses / Oral sex",
        seed_config=seed_cfg,
        seed=2301,
        row_number=1,
        women_count=1,
        men_count=1,
        hardcore_position_config=hard_config,
    )
    _expect(delegated == route, "Prompt builder category/item route should delegate to row_category_route")
    _expect(route_result.as_dict() == route, "Typed category/item route should match legacy dict route")
    _expect(typed_delegated == route_result, "Prompt builder typed category/item route should delegate")
    _expect(route_result.content_axis == "pose", "Typed category/item route lost content seed axis")
    _expect(route["category"]["slug"] == "hardcore_sexual_poses", "Row category route selected wrong hardcore category")
    _expect(route["subcategory"]["slug"] == "oral_sex", "Row category route selected wrong hardcore subcategory")
    _expect(route["content_axis"] == "pose", "Hardcore pose category should use pose seed axis")
    _expect(route["is_pose_category"] is True, "Hardcore pose category should be marked as pose content")
    _expect(isinstance(route["item_axis_values"], dict), "Row category route lost item axis metadata")
    _expect(isinstance(route["formatter_hints"], dict), "Row category route lost formatter hint metadata")
    _expect(
        pb._is_pose_content_category(route["category"], route["subcategory"])
        == row_category_route.is_pose_content_category(route["category"], route["subcategory"]),
        "Prompt builder pose-content wrapper should delegate",
    )

    casual_route = row_category_route.select_category_item_route(
        category_choice="custom_random",
        subcategory_choice="Casual clothes / Streetwear",
        seed_config=seed_cfg,
        seed=2301,
        row_number=1,
        women_count=1,
        men_count=0,
        hardcore_position_config={},
    )
    _expect(casual_route["category"]["slug"] == "casual_clothes", "Row category route selected wrong casual category")
    _expect(casual_route["content_axis"] == "content", "Non-pose category should use content seed axis")
    _expect(casual_route["is_pose_category"] is False, "Non-pose category should not be marked as pose content")

    exposed_route = row_category_route.select_category_item_route(
        category_choice="custom_random",
        subcategory_choice="Provocative erotic clothes / Sheer exposed",
        seed_config=seed_cfg,
        seed=2302,
        row_number=1,
        women_count=1,
        men_count=0,
        hardcore_position_config={},
    )
    _expect(exposed_route["subcategory"]["slug"] == "sheer_exposed", "Row category route selected wrong exposed category")
    _expect(exposed_route["content_axis"] == "content", "Exposed clothing slug should not be treated as pose content")
    _expect(exposed_route["is_pose_category"] is False, "Exposed clothing slug should not be marked as pose content")


def smoke_row_generation_policy() -> None:
    _expect(pb._ratio_or_none(-1) is None, "Prompt builder ratio helper should treat negative as unset")
    _expect(pb._ratio_or_none(1.5) == row_generation.ratio_or_none(1.5) == 1.0, "Row generation ratio clamp changed")
    _expect(pb._clamped_float("bad", 0.4) == row_generation.clamped_float("bad", 0.4) == 0.4, "Row generation float default changed")

    _expect(
        pb._pick_clothing_mode(random.Random(1), "random", None)
        == row_generation.pick_clothing_mode(random.Random(1), "random", None),
        "Prompt builder clothing mode picker should delegate to row_generation",
    )
    _expect(
        row_generation.pick_pose_mode(random.Random(2), "evocative", 1.0) == "standard",
        "Row generation standard pose ratio override changed",
    )
    _expect(
        pb._pick_figure_bias(random.Random(3), "random") == row_generation.pick_figure_bias(random.Random(3), "random"),
        "Prompt builder figure picker should delegate to row_generation",
    )
    _expect(
        row_generation.pick_expression_intensity(random.Random(4), -1) == (0.24, "random"),
        "Row generation random expression intensity changed",
    )
    _expect(
        pb._pick_expression_intensity(random.Random(4), 2.0) == row_generation.pick_expression_intensity(random.Random(4), 2.0) == (1.0, "input"),
        "Prompt builder expression intensity picker should delegate to row_generation",
    )

    direct_args = dict(
        category="woman",
        row_number=3,
        start_index=41,
        clothing="full",
        ethnicity="any",
        poses="standard",
        backside_bias=0.25,
        figure="curvy",
        no_plus_women=False,
        no_black=False,
        minimal_clothing_ratio=None,
        standard_pose_ratio=None,
        seed=5050,
    )
    _expect(
        pb._build_direct_builtin_row(**direct_args) == row_generation.build_direct_builtin_row(**direct_args),
        "Prompt builder direct built-in row should delegate to row_generation",
    )
    auto_args = dict(
        row_number=2,
        start_index=41,
        clothing="minimal",
        ethnicity="any",
        poses="evocative",
        backside_bias=0.0,
        figure="balanced",
        no_plus_women=False,
        no_black=False,
        minimal_clothing_ratio=None,
        standard_pose_ratio=None,
        seed=6060,
    )
    auto_row = row_generation.build_auto_weighted_row(**auto_args)
    _expect(pb._build_auto_weighted_row(**auto_args) == auto_row, "Prompt builder auto-weighted row should delegate to row_generation")
    _expect(auto_row.get("source") == "built_in_generator", "Row generation auto-weighted row lost source metadata")

    seed_cfg = seed_config.parse_seed_config({"category_seed": 123})
    _expect(
        pb._auto_full_choice(seed_cfg, 7070, 1) == row_generation.auto_full_choice(seed_cfg, 7070, 1),
        "Prompt builder auto-full choice should delegate to row_generation",
    )


def smoke_category_extensions_policy() -> None:
    _expect(pb.BUILTIN_CATEGORIES is category_extensions.BUILTIN_CATEGORIES, "Prompt builder built-in categories should come from category_extensions")
    targets = category_extensions.extension_targets()
    _expect(
        pb._extension_targets().keys() == targets.keys(),
        "Prompt builder extension targets should delegate to category_extensions",
    )
    _expect(targets.get("group_scenes", (None, None))[1] is True, "Group scene extension target should expect scene pairs")
    sample = [{"slug": "a", "prompt": "one"}]
    category_extensions.unique_extend(sample, [{"slug": "a", "prompt": "one"}, {"slug": "b", "prompt": "two"}])
    _expect(len(sample) == 2 and sample[1]["slug"] == "b", "Category extension unique_extend changed")

    pb.apply_pool_extensions()
    category_extensions.apply_pool_extensions()
    _expect(pb.category_choices() == category_extensions.category_choices(), "Prompt builder category choices should delegate")
    _expect(pb.subcategory_choices() == category_extensions.subcategory_choices(), "Prompt builder subcategory choices should delegate")
    _expect("Hardcore sexual poses" in pb.category_choices(), "Category choices lost hardcore JSON category")
    _expect(
        any(slug == "private_suite_group_party" for slug, _prompt in targets["group_scenes"][0]),
        "JSON pool_extensions did not reach legacy group scenes",
    )

    previous_category_dir = category_library.CATEGORY_DIR
    previous_extensions_applied = category_extensions._EXTENSIONS_APPLIED
    try:
        with tempfile.TemporaryDirectory() as temp_dir:
            category_library.CATEGORY_DIR = Path(temp_dir)
            category_extensions._EXTENSIONS_APPLIED = False
            Path(temp_dir, "custom_category.json").write_text(
                json.dumps(
                    {
                        "categories": {
                            "Dev Test Wear": {
                                "prompt_template": (
                                    "{subject_phrase}: clean test style. {item_label}: {item}. "
                                    "Scene: {scene}. Pose: {pose}. Facial expression: {expression}. "
                                    "Composition: {composition}."
                                ),
                                "caption_template": "{subject_phrase}, {item}, {scene}, {composition}",
                                "subcategories": {
                                    "Layered Office": {
                                        "items": ["structured test blazer over dark trousers"],
                                        "scenes": ["temporary showroom with modular shelves"],
                                        "poses": ["standing by a rail"],
                                        "expressions": ["focused calm look"],
                                        "compositions": ["centered catalog frame"],
                                    }
                                },
                            },
                            "Dev / Test Wear": {
                                "prompt_template": (
                                    "{subject_phrase}: slash-name test style. {item_label}: {item}. "
                                    "Scene: {scene}. Pose: {pose}. Facial expression: {expression}. "
                                    "Composition: {composition}."
                                ),
                                "caption_template": "{subject_phrase}, {item}, {scene}, {composition}",
                                "subcategories": {
                                    "Layered / Office": {
                                        "items": ["slash-safe structured blazer over tailored trousers"],
                                        "scenes": ["slash-safe showroom with glass shelving"],
                                        "poses": ["standing beside a mirrored divider"],
                                        "expressions": ["focused exact-route look"],
                                        "compositions": ["slash-safe centered catalog frame"],
                                    }
                                },
                            },
                        }
                    },
                    ensure_ascii=True,
                ),
                encoding="utf-8",
            )
            _expect("Dev Test Wear" in pb.category_choices(), "User-added JSON category did not reach category choices")
            _expect(
                "Dev Test Wear / Layered Office" in pb.subcategory_choices(),
                "User-added JSON subcategory did not reach subcategory choices",
            )
            slash_selector = "Dev / Test Wear / Layered / Office"
            _expect("Dev / Test Wear" in pb.category_choices(), "Slash-bearing user category did not reach category choices")
            _expect(
                slash_selector in pb.subcategory_choices(),
                "Slash-bearing user subcategory did not reach subcategory choices",
            )
            row = pb.build_prompt(
                category="Dev Test Wear",
                subcategory="Dev Test Wear / Layered Office",
                row_number=1,
                start_index=1,
                seed=4109,
                clothing="random",
                ethnicity="any",
                poses="random",
                backside_bias=0.0,
                figure="random",
                no_plus_women=False,
                no_black=False,
                minimal_clothing_ratio=0.0,
                standard_pose_ratio=1.0,
                trigger=Trigger,
                prepend_trigger_to_prompt=True,
                extra_positive="",
                extra_negative="",
                women_count=1,
                men_count=0,
            )
            _expect_row_base(row, "category_extensions_user_added_json")
            _expect(row.get("source") == "json_category", "User-added JSON category did not use custom row route")
            _expect(row.get("main_category") == "Dev Test Wear", "User-added JSON category name drifted")
            _expect(row.get("subcategory") == "Layered Office", "User-added JSON subcategory name drifted")
            _expect(row.get("item") == "structured test blazer over dark trousers", "User-added JSON item did not generate")
            _expect(row.get("scene_text") == "temporary showroom with modular shelves", "User-added JSON scene did not generate")
            _expect(row.get("pose") == "standing by a rail", "User-added JSON pose did not generate")
            _expect(row.get("expression") == "focused calm look", "User-added JSON expression did not generate")
            _expect(row.get("composition") == "centered catalog frame", "User-added JSON composition did not generate")
            _expect_formatter_outputs(row, "category_extensions_user_added_json", target="single")
            slash_row = pb.build_prompt(
                category="Dev / Test Wear",
                subcategory=slash_selector,
                row_number=1,
                start_index=1,
                seed=4110,
                clothing="random",
                ethnicity="any",
                poses="random",
                backside_bias=0.0,
                figure="random",
                no_plus_women=False,
                no_black=False,
                minimal_clothing_ratio=0.0,
                standard_pose_ratio=1.0,
                trigger=Trigger,
                prepend_trigger_to_prompt=True,
                extra_positive="",
                extra_negative="",
                women_count=1,
                men_count=0,
            )
            _expect_row_base(slash_row, "category_extensions_slash_named_json")
            _expect(slash_row.get("main_category") == "Dev / Test Wear", "Slash-bearing JSON category name drifted")
            _expect(slash_row.get("subcategory") == "Layered / Office", "Slash-bearing JSON subcategory name drifted")
            _expect(
                slash_row.get("item") == "slash-safe structured blazer over tailored trousers",
                "Slash-bearing JSON exact subcategory did not generate the intended item",
            )
            _expect_formatter_outputs(slash_row, "category_extensions_slash_named_json", target="single")
    finally:
        category_library.CATEGORY_DIR = previous_category_dir
        category_extensions._EXTENSIONS_APPLIED = previous_extensions_applied


def smoke_category_cast_config_policy() -> None:
    _expect(pb.CATEGORY_PRESETS is category_cast_config.CATEGORY_PRESETS, "Prompt builder category presets are not delegated")
    _expect(pb.CAST_PRESETS is category_cast_config.CAST_PRESETS, "Prompt builder cast presets are not delegated")
    _expect("woman" in category_cast_config.category_preset_choices(), "Category preset choices lost built-in woman")
    _expect("man" in category_cast_config.category_preset_choices(), "Category preset choices lost built-in man")
    _expect("hardcore_pose" in category_cast_config.category_preset_choices(), "Category preset choices lost hardcore_pose")
    _expect("custom_counts" in category_cast_config.cast_preset_choices(), "Cast preset choices lost custom_counts")
    _expect(
        pb._count_phrase(2, "adult woman", "adult women") == cast_context.count_phrase(2, "adult woman", "adult women"),
        "Prompt builder count phrase should delegate to cast_context",
    )
    configured = cast_context.configured_cast_context(1, 2)
    _expect(configured.get("subject_phrase") == "one adult woman and two adult men", "Configured cast subject phrase changed")
    _expect(configured.get("cast_summary") == "1 woman, 2 men, 3 total adults", "Configured cast summary changed")
    _expect(configured.get("scene_kind") == "adult threesome sex scene", "Configured cast scene kind changed")
    _expect(
        pb._configured_cast_context(1, 2) == configured,
        "Prompt builder configured cast context should delegate to cast_context",
    )
    _expect(
        cast_context.couple_type_from_counts(
            random.Random(1),
            2,
            0,
            choose=lambda _rng, pool: pool[0],
            couple_types=[("woman and man", "a woman and a man", "fallback pose")],
        )
        == ("two women", "two women", "close affectionate couple pose", 2, 0),
        "Couple type count override for two women changed",
    )
    _expect(
        pb._subject_context(random.Random(5), "couple", "any", "curvy", False, False, 1, 1)
        == subject_context.subject_context(random.Random(5), "couple", "any", "curvy", False, False, 1, 1),
        "Prompt builder subject context should delegate to subject_context",
    )
    _expect(
        subject_context.subject_context(random.Random(1), "configured_cast", "any", "curvy", False, False, 2, 1).get("cast_summary")
        == "2 women, 1 man, 3 total adults",
        "Configured cast subject context changed",
    )
    group = subject_context.subject_context(random.Random(2), "group", "asian", "curvy", False, False)
    _expect(group.get("subject") == "mixed Asian adult group", "Group subject ethnicity wording changed")
    _expect(
        subject_context.subject_context(random.Random(3), "layout", "any", "curvy", False, False).get("subject")
        == "layout scene",
        "Layout subject context fallback changed",
    )
    label_slots = [
        {"subject_type": "woman", "label": "auto_chain", "name": "older auto"},
        {"subject_type": "woman", "label": "auto_chain", "name": "newer auto"},
        {"subject_type": "man", "label": "B", "name": "explicit man"},
    ]
    label_map = cast_context.character_slot_label_map(label_slots)
    _expect(
        label_map.get("Woman A", {}).get("name") == "newer auto"
        and label_map.get("Woman B", {}).get("name") == "older auto",
        "Character slot auto-chain label order changed",
    )
    _expect(
        label_map.get("Man B", {}).get("name") == "explicit man",
        "Character slot explicit label mapping changed",
    )
    _expect(
        pb._character_slot_label_map(label_slots) == label_map,
        "Prompt builder character slot label map should delegate to cast_context",
    )

    category_config = json.loads(pb.build_category_config_json("hardcore_pose", "Foreplay and teasing"))
    _expect(category_config.get("category") == "Hardcore sexual poses", "Category config lost hardcore category mapping")
    _expect(category_config.get("subcategory") == "Foreplay and teasing", "Category config lost explicit subcategory")
    _expect(pb._parse_category_config(category_config) == ("Hardcore sexual poses", "Foreplay and teasing"), "Category parser wrapper drifted")
    builtin_woman_config = json.loads(pb.build_category_config_json("woman", "random"))
    _expect(builtin_woman_config.get("preset") == "woman", "Built-in woman category preset fell back")
    _expect(builtin_woman_config.get("category") == "woman", "Built-in woman category config lost direct category")
    _expect(pb._parse_category_config(builtin_woman_config) == ("woman", "random"), "Built-in woman category parser drifted")

    fallback_config = json.loads(category_cast_config.build_category_config_json("unknown", "random"))
    _expect(fallback_config.get("preset") == "auto_weighted", "Unknown category preset did not fall back")
    _expect(pb._parse_category_config({"preset": "unknown"}) == ("auto_weighted", "random"), "Unknown category parser fallback changed")

    cast_config = json.loads(pb.build_cast_config_json("mixed_couple", 9, 9))
    _expect((cast_config.get("women_count"), cast_config.get("men_count")) == (1, 1), "Cast preset did not override manual counts")
    custom_cast = json.loads(category_cast_config.build_cast_config_json("custom_counts", -5, 99))
    _expect((custom_cast.get("women_count"), custom_cast.get("men_count")) == (0, 12), "Custom cast counts were not clamped")
    empty_cast = pb._parse_cast_config({"cast_mode": "custom_counts", "women_count": 0, "men_count": 0})
    _expect((empty_cast.get("women_count"), empty_cast.get("men_count")) == (1, 0), "Empty custom cast was not corrected")


def smoke_row_subject_route_policy() -> None:
    seed_cfg = seed_config.parse_seed_config({})
    slot_cast = pb.build_character_slot_json(
        subject_type="woman",
        label="A",
        age="32-year-old adult",
        ethnicity="western_european",
        figure="balanced",
        body="slim",
        hair="short silver bob",
        eyes="gray eyes",
        descriptor_detail="full",
    )["character_cast"]
    profile = {
        "profile_type": "character",
        "subject_type": "woman",
        "age": "45-year-old adult",
        "body": "average",
        "body_phrase": "average figure",
        "skin": "profile skin",
        "hair": "profile hair",
        "eyes": "profile eyes",
    }
    route = row_subject_route.resolve_subject_route(
        subject_type="woman",
        seed_config=seed_cfg,
        seed=501,
        row_number=1,
        ethnicity="any",
        figure="balanced",
        no_plus_women=False,
        no_black=False,
        women_count=1,
        men_count=0,
        character_profile=profile,
        character_cast=slot_cast,
    )
    delegated = pb._subject_route(
        subject_type="woman",
        seed_config=seed_cfg,
        seed=501,
        row_number=1,
        ethnicity="any",
        figure="balanced",
        no_plus_women=False,
        no_black=False,
        women_count=1,
        men_count=0,
        character_profile=profile,
        character_cast=slot_cast,
    )
    _expect(delegated == route, "Prompt builder subject route should delegate to row_subject_route")
    _expect(route["subject_type"] == "woman", "Subject route changed single-woman subject type")
    _expect(route["character_slot_status"] == "applied:Woman A", "Subject route did not apply matching Woman A slot")
    _expect(route["character_profile_status"] == "skipped_character_slot", "Subject route should skip profile when slot applies")
    _expect(route["context"].get("age") == "32-year-old adult", "Subject route lost slot age override")
    _expect(route["context"].get("hair") == "short silver bob", "Subject route lost slot hair override")
    _expect(route["applied_profile"] == {}, "Subject route should not apply profile over matching slot")

    cast_route = row_subject_route.resolve_subject_route(
        subject_type="configured_cast",
        seed_config=seed_cfg,
        seed=502,
        row_number=1,
        ethnicity="western_european",
        figure="balanced",
        no_plus_women=False,
        no_black=False,
        women_count=1,
        men_count=1,
        character_cast=_character_cast(pov_man=True),
    )
    _expect(cast_route["subject_type"] == "configured_cast", "Configured-cast subject route changed subject type")
    _expect(cast_route["pov_character_labels"] == ["Man A"], "Subject route lost configured-cast POV man label")
    _expect("Woman A:" in cast_route["cast_descriptor_text"], "Subject route lost visible woman descriptor")
    _expect("Man A:" not in cast_route["cast_descriptor_text"], "Subject route should not describe POV man as visible cast")


def smoke_generation_profile_config_policy() -> None:
    _expect(
        pb.GENERATION_PROFILE_PRESETS is generation_profile_config.GENERATION_PROFILE_PRESETS,
        "Prompt builder generation profile presets are not delegated",
    )
    _expect("krea2_friendly" in generation_profile_config.generation_profile_choices(), "Generation profile choices lost krea2_friendly")

    profile = json.loads(
        pb.build_generation_profile_json(
            profile="krea2_friendly",
            clothing_override="minimal",
            poses_override="random",
            expression_enabled=False,
            expression_intensity_mode="random",
            expression_intensity=0.8,
            backside_bias=2,
            minimal_clothing_ratio=0.25,
            standard_pose_ratio=0.75,
            trigger_policy="prepend_trigger",
        )
    )
    _expect(profile.get("profile") == "krea2_friendly", "Generation profile output lost selected profile")
    _expect(profile.get("clothing") == "minimal", "Generation profile clothing override failed")
    _expect(profile.get("poses") == "random", "Generation profile poses override failed")
    _expect(profile.get("expression_enabled") is False, "Generation profile expression disable failed")
    _expect(profile.get("expression_intensity") == -1.0, "Generation profile random expression marker changed")
    _expect(profile.get("backside_bias") == 1.0, "Generation profile backside bias clamp changed")
    _expect(profile.get("prepend_trigger_to_prompt") is True, "Generation profile trigger override failed")

    parsed = pb._parse_generation_profile(profile)
    _expect(parsed.get("clothing") == "minimal", "Generation profile parser wrapper lost clothing")
    _expect(parsed.get("expression_enabled") is False, "Generation profile parser wrapper lost expression disable")
    _expect(parsed.get("minimal_clothing_ratio") == 0.25, "Generation profile parser wrapper lost minimal clothing ratio")

    fallback = generation_profile_config.parse_generation_profile({"profile": "unknown", "clothing": "bad", "poses": "bad"})
    _expect(fallback.get("profile") == "unknown", "Generation profile parser should preserve raw profile label")
    _expect(fallback.get("clothing") == "full", "Generation profile parser did not normalize invalid clothing")
    _expect(fallback.get("poses") == "standard", "Generation profile parser did not normalize invalid poses")
    _expect(fallback.get("trigger") == "sxcpinup_coloredpencil", "Generation profile parser lost default trigger")


def smoke_filter_config_policy() -> None:
    _expect(pb.ETHNICITY_FILTER_CHOICES is filter_config.ETHNICITY_FILTER_CHOICES, "Prompt builder ethnicity choices are not delegated")
    _expect("french_european" in filter_config.ETHNICITY_LIST_KEYS, "Ethnicity list keys lost regional choices")

    advanced = json.loads(
        pb.build_filter_config_json(
            include_european=True,
            include_mediterranean_mena=False,
            include_latina=False,
            include_east_asian=False,
            include_southeast_asian=False,
            include_south_asian=False,
            include_black_african=True,
            include_indigenous=False,
            include_mixed=False,
            include_plus_size=False,
            figure="bad",
        )
    )
    _expect(advanced.get("ethnicity_includes") == ["european", "black_african"], "Advanced filter selected ethnicity list changed")
    _expect("exclude_latina" in advanced.get("ethnicity", ""), "Advanced filter ethnicity excludes changed")
    _expect(advanced.get("figure") == "curvy", "Advanced filter invalid figure fallback changed")
    _expect(advanced.get("no_plus_women") is True, "Advanced filter plus-size exclusion changed")

    ethnicity_list = pb.build_ethnicity_list_json(include_french_european=True, include_asian=True, strict_excludes=True)
    _expect("french_european" in ethnicity_list["ethnicity"], "Ethnicity list lost regional include")
    _expect("asian" in ethnicity_list["ethnicity"], "Ethnicity list lost umbrella Asian include")
    _expect("exclude_european" not in ethnicity_list["ethnicity"], "Ethnicity list should protect European when regional Europe is selected")
    _expect("exclude_east_asian" not in ethnicity_list["ethnicity"], "Ethnicity list should protect East Asian when Asian is selected")
    _expect("filter_config" in ethnicity_list, "Ethnicity list lost filter_config output")

    parsed_text = pb._parse_filter_config("french_european")
    _expect(parsed_text.get("ethnicity") == "french_european", "Filter parser text shortcut changed")
    parsed_bad = filter_config.parse_filter_config({"ethnicity": "bad", "figure": "bad"})
    _expect(parsed_bad.get("ethnicity") == "any", "Filter parser invalid ethnicity fallback changed")
    _expect(parsed_bad.get("figure") == "curvy", "Filter parser invalid figure fallback changed")
    _expect(pb.normalize_ethnicity_filter("random", "any", allow_random=True) == "random", "Ethnicity random normalization changed")
    _expect(pb.normalize_ethnicity_filter("random", "any", allow_random=False) == "any", "Ethnicity default normalization changed")


def smoke_character_config_policy() -> None:
    _expect(pb.CHARACTER_LABEL_CHOICES is character_config.CHARACTER_LABEL_CHOICES, "Prompt builder character choices are not delegated")
    _expect("21-year-old adult" in character_config.character_age_choices(), "Character age choices lost adult ages")
    _expect("fat" in character_config.character_man_body_choices(), "Man body pool lost fat option")
    _expect(pb.character_figure_choices() == character_config.character_figure_choices(), "Character figure choices should delegate")
    _expect("platinum_blonde" in character_config.character_hair_color_choices(), "Hair color choices lost platinum blonde")

    traits = json.loads(
        pb.build_characteristics_config_json(
            axis="bodies",
            selected_values=["slim", "bad value", "slim", "fat"],
            combine_mode="replace_axis",
        )
    )
    _expect(traits.get("bodies") == ["slim", "fat"], "Character body trait normalization changed")
    merged_traits = json.loads(
        character_config.build_characteristics_config_json(
            characteristics=traits,
            axis="eyes",
            selected_values=["blue", "gray-brown", "blue"],
            combine_mode="add_to_axis",
        )
    )
    _expect(merged_traits.get("bodies") == ["slim", "fat"], "Character trait merge lost existing axis")
    _expect(merged_traits.get("eyes") == ["blue", "gray_brown"], "Character eye trait normalization changed")
    _expect(pb._characteristic_choice({"ages": ["21-year-old adult"]}, "ages", random.Random(1)) == "21-year-old adult", "Trait choice changed")

    hair = json.loads(
        pb.build_hair_config_json(
            axis="color",
            selected_values=["platinum blonde", "bad", "dark-brown"],
            combine_mode="replace_axis",
        )
    )
    _expect(hair.get("colors") == ["platinum_blonde", "dark_brown"], "Hair color normalization changed")
    hair = json.loads(
        character_config.build_hair_config_json(
            hair_config=hair,
            axis="style",
            selected_values=["messy bun", "straight"],
            combine_mode="add_to_axis",
        )
    )
    _expect(hair.get("styles") == ["messy_bun", "straight"], "Hair style config merge changed")
    _expect(pb._hair_phrase_from_parts("platinum_blonde", "long", "messy_bun") == "long platinum-blonde hair in a messy bun", "Hair phrase helper changed")
    _expect(character_config.normalize_presence_mode("pov", "woman") == "visible", "POV presence should stay man-only")
    pov_slot = {"subject_type": "man", "presence_mode": "pov"}
    visible_slot = {"subject_type": "man", "presence_mode": "visible"}
    _expect(pb._slot_is_pov(pov_slot) is True, "Prompt builder POV slot helper should delegate to POV policy")
    _expect(pov_policy.slot_is_pov(visible_slot) is False, "Visible man slot should not be POV")
    _expect(
        pb._pov_character_labels({"Man A": pov_slot, "Man B": visible_slot}, 2) == ["Man A"],
        "POV label selection should keep only POV men in count order",
    )
    _expect(
        pb._pov_role_graph_prompt("Man A is positioned behind Woman A", ["Man A"])
        == "First-person POV from Man A; the POV camera is positioned behind Woman A",
        "Builder POV role graph prompt should use shared viewer replacement",
    )
    _expect(
        pb._pov_composition_prompt("wide group-sex composition with all bodies visible", ["Man A"])
        == "first-person group-sex POV composition with visible partners readable",
        "Builder POV composition prompt should use shared POV composition replacements",
    )
    _expect(
        krea_pov.pov_composition_text(
            "wide group-sex composition with all bodies visible, adapted for first-person POV with the POV participant kept off-camera",
            ["Man A"],
        )
        == "first-person group-sex POV composition with visible partners readable",
        "Krea POV composition cleanup should delegate shared replacements and strip builder annotation",
    )
    _expect(character_config.normalize_slot_seed(0xFFFFFFFF + 99) == 0xFFFFFFFF, "Slot seed clamp changed")
    slot_result = character_slot.build_character_slot_json(
        subject_type="man",
        label="Man B",
        slot_seed=123,
        age="manual",
        manual_age="44-year-old adult",
        ethnicity="western_european",
        figure="balanced",
        body="manual",
        manual_body="stocky",
        descriptor_detail="compact",
        expression_intensity=1.5,
        softcore_expression_intensity=0.25,
        hardcore_expression_intensity=-1,
        presence_mode="pov",
        hair_color="dark brown",
        hair_length="short",
        hair_style="straight",
        softcore_outfit="buttoned shirt",
        hardcore_clothing="shirt pushed open",
    )
    _expect(
        pb.build_character_slot_json(
            subject_type="man",
            label="Man B",
            slot_seed=123,
            age="manual",
            manual_age="44-year-old adult",
            ethnicity="western_european",
            figure="balanced",
            body="manual",
            manual_body="stocky",
            descriptor_detail="compact",
            expression_intensity=1.5,
            softcore_expression_intensity=0.25,
            hardcore_expression_intensity=-1,
            presence_mode="pov",
            hair_color="dark brown",
            hair_length="short",
            hair_style="straight",
            softcore_outfit="buttoned shirt",
            hardcore_clothing="shirt pushed open",
        )
        == slot_result,
        "Prompt builder character slot JSON should delegate to character_slot",
    )
    slot = json.loads(slot_result["character_slot"])
    _expect(slot.get("age") == "44-year-old adult", "Character slot manual age normalization changed")
    _expect(slot.get("body") == "stocky", "Character slot manual body normalization changed")
    _expect(slot.get("presence_mode") == "pov", "Character slot POV presence normalization changed")
    _expect(slot.get("expression_intensity") == 1.0, "Character slot expression intensity clamp changed")
    _expect(
        character_slot.slot_expression_intensity_for_phase(slot, "softcore") == 0.25
        and character_slot.slot_expression_intensity_for_phase(slot, "hardcore") == 1.0,
        "Character slot phase expression fallback changed",
    )
    _expect(
        pb._slot_effective_figure({"slot_seed": 123, "figure": "random"}, "woman", "curvy")
        == character_slot.slot_effective_figure({"slot_seed": 123, "figure": "random"}, "woman", "curvy"),
        "Prompt builder seeded slot figure should delegate to character_slot",
    )
    _expect(
        pb._appearance_for_subject(random.Random(9), "woman", "western_european", "balanced", False, False)
        == character_appearance.appearance_for_subject(random.Random(9), "woman", "western_european", "balanced", False, False),
        "Prompt builder appearance selection should delegate to character_appearance",
    )
    _expect(
        pb._context_from_character_slot(random.Random(11), slot, "man", "any", "curvy", False, False)
        == character_appearance.context_from_character_slot(random.Random(11), slot, "man", "any", "curvy", False, False),
        "Prompt builder slot context should delegate to character_appearance",
    )
    _expect(
        pb._row_from_character_slot(slot_result["character_slot"])
        == character_appearance.row_from_character_slot(slot_result["character_slot"]),
        "Prompt builder slot row conversion should delegate to character_appearance",
    )
    row = character_appearance.apply_character_context_to_row({}, {"age": "44-year-old adult", "body": "stocky"})
    _expect(row.get("age_band") == "44-year-old adult" and row.get("body") == "stocky", "Character context row application changed")


def smoke_character_profile_policy() -> None:
    _expect(pb.CHARACTER_MANUAL_FIELDS is character_profile.CHARACTER_MANUAL_FIELDS, "Prompt builder manual fields are not delegated")
    _expect(pb.PROFILE_DIR == character_profile.PROFILE_DIR, "Prompt builder profile dir is not delegated")
    _expect(pb._body_phrase("curvy", "hourglass figure") == "curvy build and hourglass figure", "Body phrase helper changed")
    _expect(pb._safe_profile_name("bad name!*") == "bad_name", "Profile name sanitizer changed")

    manual = json.loads(
        pb.build_character_manual_config_json(
            combine_mode="merge_nonempty",
            manual_age="31-year-old adult",
            body_phrase="custom body",
            skin="warm skin",
            softcore_outfit="red dress",
        )
    )
    _expect(manual.get("manual_age") == "31-year-old adult", "Manual config lost age")
    _expect(manual.get("softcore_outfit") == "red dress", "Manual config lost outfit")
    _expect("manual_age=31-year-old adult" in manual.get("summary", ""), "Manual config summary changed")

    metadata_row = {
        "subject_type": "woman",
        "age": "28-year-old adult",
        "body": "curvy",
        "body_phrase": "curvy figure with full hips",
        "skin": "warm skin",
        "hair": "long black hair",
        "eyes": "brown eyes",
        "figure": "balanced",
        "descriptor_detail": "medium",
    }
    profile_result = character_profile.build_character_profile_json(
        profile_name="smoke profile",
        source="metadata_json",
        metadata_json=metadata_row,
    )
    profile = json.loads(profile_result["profile_json"])
    _expect(profile.get("profile_name") == "smoke_profile", "Profile name normalization changed")
    _expect(profile.get("age") == "28-year-old adult", "Profile metadata extraction lost age")
    _expect("long black hair" in profile_result["descriptor"], "Profile descriptor lost hair at medium detail")

    loaded = pb.load_character_profile_json(
        profile_name="manual",
        fallback_profile_json=profile_result["profile_json"],
        override_age="35-year-old adult",
        override_descriptor_detail="compact",
    )
    loaded_profile = json.loads(loaded["profile_json"])
    _expect(loaded.get("status") == "fallback", "Profile fallback load status changed")
    _expect(loaded_profile.get("age") == "35-year-old adult", "Profile override age did not apply")
    _expect(loaded_profile.get("descriptor_detail") == "compact", "Profile override descriptor detail did not apply")

    context = {"subject_type": "woman", "subject": "woman", "subject_phrase": "woman", "age": "21-year-old adult"}
    applied_context, applied_profile, status = pb._apply_character_profile_to_context(context, loaded_profile)
    _expect(status == "applied", "Profile context application changed")
    _expect(applied_context.get("age") == "35-year-old adult", "Profile context application lost age")
    _expect(applied_profile.get("profile_type") == "character", "Profile context returned wrong profile")


def smoke_row_normalization_policy() -> None:
    _expect(
        pb._prepend_trigger("base prompt", Trigger, True) == row_normalization.prepend_trigger("base prompt", Trigger, True),
        "Prompt builder trigger helper should delegate to row normalization policy",
    )
    _expect(
        pb._combined_negative("bad anatomy", "low quality") == row_normalization.combined_negative("bad anatomy", "low quality"),
        "Prompt builder negative helper should delegate to row normalization policy",
    )

    row = row_normalization.normalize_prompt_row(
        {
            "prompt": f"{Trigger}, {Trigger}, base prompt.",
            "caption": f"{Trigger}, {Trigger}, base caption.",
            "negative_prompt": "bad anatomy, bad anatomy",
        },
        active_trigger=Trigger,
        prepend_trigger_to_prompt=True,
        extra_positive="extra detail",
        extra_negative="low quality, bad anatomy",
        default_negative="bad anatomy",
    )
    _expect_trigger_once("row_normalization.prompt", row.get("prompt"), Trigger)
    _expect_trigger_once("row_normalization.caption", row.get("caption"), Trigger)
    _expect("extra detail" in row.get("prompt", ""), "Row normalization lost extra positive text")
    _expect(row.get("trigger") == Trigger, "Row normalization lost active trigger")
    _expect_no_duplicate_comma_items("row_normalization.negative", row.get("negative_prompt"))

    legacy_couple = row_normalization.normalize_prompt_row(
        {
            "source": "built_in_generator",
            "primary_subject": "two women",
            "scene": "office",
            "prompt": (
                "Two adults in a clean legacy prompt. Scene: old room. "
                "Pose: standing close, affectionate and flirtatious but non-explicit. "
                "Facial expressions: one with a calm smile, the other with a side glance. "
                "Clothing: coordinated satin outfits, resort styling. Composition: vertical old frame."
            ),
            "caption": "legacy couple caption",
            "negative_prompt": "bad anatomy",
        },
        active_trigger=Trigger,
        prepend_trigger_to_prompt=False,
    )
    _expect(legacy_couple.get("subject_type") == "couple", "Legacy couple row lost normalized subject_type")
    _expect(legacy_couple.get("women_count") == 2 and legacy_couple.get("men_count") == 0, "Legacy couple row lost normalized counts")
    _expect(legacy_couple.get("person_count") == 2, "Legacy couple row lost normalized person count")
    _expect(legacy_couple.get("scene_slug") == "office", "Legacy couple row lost scene slug")
    _expect("cozy office desk" in str(legacy_couple.get("scene_text", "")), "Legacy couple row lost readable scene text")
    _expect(legacy_couple.get("scene_entry", {}).get("slug") == "office", "Legacy couple row lost scene entry")
    _expect(legacy_couple.get("pose") == "standing close", "Legacy couple row did not clean pose suffix")
    _expect(legacy_couple.get("item") == "coordinated satin outfits", "Legacy couple row did not clean clothing suffix")
    _expect(legacy_couple.get("item_label") == "Clothing", "Legacy couple row lost item label")
    _expect("calm smile" in str(legacy_couple.get("expression", "")), "Legacy couple row lost expression metadata")
    _expect("cast_summary" not in legacy_couple, "Legacy couple row should not gain configured-cast summary")

    legacy_single = row_normalization.normalize_prompt_row(
        {
            "source": "built_in_generator",
            "primary_subject": "woman",
            "scene": "studio",
            "age_band": "25-year-old adult",
            "body_type": "curvy",
            "figure": "soft curves, defined waist",
            "caption": (
                f"{Trigger}, woman, 25-year-old adult, curvy figure with soft curves, defined waist, "
                "warm skin, short blonde hair, blue eyes, pose, expression, clothing, scene, composition"
            ),
            "prompt": (
                "A woman. Scene: old studio. Pose: standing calmly. "
                "Facial expression: direct look. Clothing: fitted dress, fashion editorial styling. "
                "Composition: vertical portrait."
            ),
            "negative_prompt": "bad anatomy",
        },
        active_trigger=Trigger,
        prepend_trigger_to_prompt=False,
    )
    _expect(legacy_single.get("body_phrase") == "curvy figure with soft curves, defined waist", "Legacy single row lost body phrase")
    _expect(legacy_single.get("skin") == "warm skin", "Legacy single row lost skin metadata")
    _expect(legacy_single.get("hair") == "short blonde hair", "Legacy single row lost hair metadata")
    _expect(legacy_single.get("eyes") == "blue eyes", "Legacy single row lost eye metadata")

    legacy_group = row_normalization.normalize_prompt_row(
        {
            "source": "built_in_generator",
            "primary_subject": "mixed adult group",
            "prompt": "Group legacy prompt.",
            "caption": "legacy group caption",
            "negative_prompt": "bad anatomy",
        },
        active_trigger=Trigger,
        prepend_trigger_to_prompt=False,
    )
    _expect(legacy_group.get("subject_type") == "group", "Legacy group row lost normalized subject_type")
    _expect(legacy_group.get("women_count") == 2 and legacy_group.get("men_count") == 2, "Legacy group row lost fallback counts")
    _expect(legacy_group.get("person_count") == 4, "Legacy group row lost normalized person count")

    legacy_layout = row_normalization.normalize_prompt_row(
        {
            "source": "built_in_generator",
            "primary_subject": "layout scene",
            "prompt": "Layout legacy prompt.",
            "caption": "legacy layout caption",
            "negative_prompt": "bad anatomy",
        },
        active_trigger=Trigger,
        prepend_trigger_to_prompt=False,
    )
    _expect(legacy_layout.get("subject_type") == "layout", "Legacy layout row lost normalized subject_type")
    _expect("women_count" not in legacy_layout and "men_count" not in legacy_layout, "Legacy layout row should not invent cast counts")

    outputs = row_normalization.normalize_pair_text_outputs(
        active_trigger=Trigger,
        prepend_trigger_to_prompt=True,
        extra_positive="pair extra",
        extra_negative="low quality, bad anatomy",
        soft_prompt="soft prompt.",
        hard_prompt="hard prompt.",
        soft_negative_base="bad anatomy, bad anatomy",
        hard_negative_base="bad anatomy, low quality",
        soft_caption_parts=[Trigger, "soft caption"],
        hard_caption_parts=[Trigger, "hard caption"],
    )
    _expect_trigger_once("row_normalization.soft_prompt", outputs.get("soft_prompt"), Trigger)
    _expect_trigger_once("row_normalization.hard_prompt", outputs.get("hard_prompt"), Trigger)
    _expect_trigger_once("row_normalization.soft_caption", outputs.get("soft_caption"), Trigger)
    _expect_trigger_once("row_normalization.hard_caption", outputs.get("hard_caption"), Trigger)
    _expect_no_duplicate_comma_items("row_normalization.soft_negative", outputs.get("soft_negative"))
    _expect_no_duplicate_comma_items("row_normalization.hard_negative", outputs.get("hard_negative"))

    pair = row_normalization.normalize_pair_metadata(
        {
            "softcore_prompt": f"{Trigger}, {Trigger}, soft pair.",
            "hardcore_prompt": f"{Trigger}, {Trigger}, hard pair.",
            "softcore_caption": f"{Trigger}, {Trigger}, soft caption.",
            "hardcore_caption": f"{Trigger}, {Trigger}, hard caption.",
            "softcore_negative_prompt": "bad anatomy, bad anatomy",
            "hardcore_negative_prompt": "bad anatomy, low quality, bad anatomy",
            "softcore_partner_styling": {"outfits": ["partner outfit"], "pose": "partner pose"},
            "hardcore_clothing_state": "structured hard clothing state",
            "character_hardcore_clothing": ["Woman A custom hard clothing"],
            "default_man_hardcore_clothing": ["Man A default hard clothing"],
            "hardcore_detail_density": "dense",
            "hardcore_position_config": {"family": "oral"},
            "softcore_camera_config": {"camera_mode": "standard", "camera_detail": "compact"},
            "hardcore_camera_config": {"camera_mode": "pov", "camera_detail": "dense"},
            "softcore_camera_directive": "Camera: soft front view.",
            "hardcore_camera_directive": "Camera: hard side view.",
            "softcore_camera_scene_directive": "Soft camera-aware scene layout.",
            "hardcore_camera_scene_directive": "Hard camera-aware scene layout.",
            "softcore_row": {
                "prompt": f"{Trigger}, {Trigger}, embedded soft.",
                "caption": f"{Trigger}, {Trigger}, embedded soft caption.",
                "negative_prompt": "bad anatomy, bad anatomy",
                "camera_directive": "stale soft camera",
            },
            "hardcore_row": {
                "prompt": f"{Trigger}, {Trigger}, embedded hard.",
                "caption": f"{Trigger}, {Trigger}, embedded hard caption.",
                "negative_prompt": "low quality, bad anatomy, low quality",
                "camera_scene_directive": "stale hard camera scene",
            },
        },
        active_trigger=Trigger,
    )
    _expect_trigger_once("row_normalization.pair.softcore_prompt", pair.get("softcore_prompt"), Trigger)
    _expect_trigger_once("row_normalization.pair.hardcore_prompt", pair.get("hardcore_prompt"), Trigger)
    _expect_trigger_once("row_normalization.pair.softcore_row.prompt", pair["softcore_row"].get("prompt"), Trigger)
    _expect_trigger_once("row_normalization.pair.hardcore_row.caption", pair["hardcore_row"].get("caption"), Trigger)
    _expect(
        pair["softcore_row"].get("prompt") == pair.get("softcore_prompt"),
        "Pair normalization left stale soft row prompt text",
    )
    _expect(
        pair["hardcore_row"].get("caption") == pair.get("hardcore_caption"),
        "Pair normalization left stale hard row caption text",
    )
    _expect(
        pair["softcore_row"].get("softcore_partner_styling") == pair.get("softcore_partner_styling"),
        "Pair normalization left stale soft side metadata",
    )
    _expect(
        pair["hardcore_row"].get("hardcore_clothing_state") == pair.get("hardcore_clothing_state"),
        "Pair normalization left stale hard clothing metadata",
    )
    _expect(
        pair["hardcore_row"].get("default_man_hardcore_clothing") == pair.get("default_man_hardcore_clothing"),
        "Pair normalization left stale hard default clothing metadata",
    )
    _expect(
        pair["softcore_row"].get("camera_config") == pair.get("softcore_camera_config"),
        "Pair normalization left stale soft camera config",
    )
    _expect(
        pair["hardcore_row"].get("camera_config") == pair.get("hardcore_camera_config"),
        "Pair normalization left stale hard camera config",
    )
    _expect(
        pair["softcore_row"].get("camera_directive") == pair.get("softcore_camera_directive"),
        "Pair normalization left stale soft camera directive",
    )
    _expect(
        pair["hardcore_row"].get("camera_scene_directive") == pair.get("hardcore_camera_scene_directive"),
        "Pair normalization left stale hard camera scene directive",
    )
    _expect_no_duplicate_comma_items("row_normalization.pair.soft_negative", pair.get("softcore_negative_prompt"))
    _expect_no_duplicate_comma_items("row_normalization.pair.hard_row_negative", pair["hardcore_row"].get("negative_prompt"))

    reverse_pair = row_normalization.normalize_pair_metadata(
        {
            "softcore_row": {
                "prompt": f"{Trigger}, {Trigger}, embedded-only soft.",
                "caption": f"{Trigger}, {Trigger}, embedded-only soft caption.",
                "negative_prompt": "bad anatomy, bad anatomy",
                "softcore_partner_styling": {"outfits": ["row partner outfit"], "pose": "row partner pose"},
                "camera_config": {"camera_mode": "standard"},
                "camera_directive": "Camera: row soft front view.",
                "camera_scene_directive": "Row soft scene camera layout.",
            },
            "hardcore_row": {
                "prompt": f"{Trigger}, {Trigger}, embedded-only hard.",
                "caption": f"{Trigger}, {Trigger}, embedded-only hard caption.",
                "negative_prompt": "low quality, low quality",
                "hardcore_clothing_state": "row hard clothing state",
                "character_hardcore_clothing": ["Woman A row hard clothing"],
                "default_man_hardcore_clothing": ["Man A row default hard clothing"],
                "hardcore_detail_density": "concise",
                "hardcore_position_config": {"family": "outercourse"},
                "camera_config": {"camera_mode": "pov"},
                "camera_directive": "Camera: row hard side view.",
                "camera_scene_directive": "Row hard scene camera layout.",
            },
        },
        active_trigger=Trigger,
    )
    _expect_trigger_once("row_normalization.reverse.soft_prompt", reverse_pair.get("softcore_prompt"), Trigger)
    _expect_trigger_once("row_normalization.reverse.hard_caption", reverse_pair.get("hardcore_caption"), Trigger)
    _expect(
        reverse_pair.get("softcore_partner_styling") == reverse_pair["softcore_row"].get("softcore_partner_styling"),
        "Pair normalization did not lift soft side metadata from embedded row",
    )
    _expect(
        reverse_pair.get("hardcore_clothing_state") == reverse_pair["hardcore_row"].get("hardcore_clothing_state"),
        "Pair normalization did not lift hard side metadata from embedded row",
    )
    _expect(
        reverse_pair.get("softcore_camera_config") == reverse_pair["softcore_row"].get("camera_config"),
        "Pair normalization did not lift soft camera config from embedded row",
    )
    _expect(
        reverse_pair.get("hardcore_camera_scene_directive") == reverse_pair["hardcore_row"].get("camera_scene_directive"),
        "Pair normalization did not lift hard camera scene from embedded row",
    )
    _expect_no_duplicate_comma_items("row_normalization.reverse.hard_negative", reverse_pair.get("hardcore_negative_prompt"))


def smoke_prompt_hygiene_policy() -> None:
    merged = prompt_hygiene.combine_negative_text(
        "bad anatomy, bad anatomy",
        "low quality",
        "bad anatomy",
        "",
    )
    _expect(merged == "bad anatomy, low quality", "Prompt hygiene negative merge/dedupe changed")
    _expect(
        row_normalization.combined_negative("bad anatomy, bad anatomy", "low quality, bad anatomy") == merged,
        "Row normalization negative merge should delegate to prompt hygiene",
    )
    _expect(
        krea_formatter._combine_negative("bad anatomy, bad anatomy", "low quality", "bad anatomy") == merged,
        "Krea negative merge should delegate to prompt hygiene",
    )
    _expect(
        prompt_hygiene.sanitize_prose_text("Scene: . A sentence. A sentence.") == "A sentence.",
        "Prompt hygiene prose cleanup changed",
    )


def smoke_row_rendering_policy() -> None:
    _expect(pb.SINGLE_TEMPLATE == row_rendering.SINGLE_TEMPLATE, "Prompt builder single template should delegate to row_rendering")
    _expect(
        pb._format("Known {known}, missing {missing}", {"known": 7})
        == row_rendering.format_template("Known {known}, missing {missing}", {"known": 7}),
        "Prompt builder safe formatter should delegate to row_rendering",
    )
    _expect(
        row_rendering.format_template("Known {known}, missing {missing}", {"known": 7}) == "Known 7, missing {missing}",
        "Row rendering changed missing-field preservation",
    )
    _expect(
        row_rendering.prompt_template_for({}, {}, {}, "woman") == row_rendering.SINGLE_TEMPLATE,
        "Row rendering default woman template changed",
    )
    _expect(
        row_rendering.prompt_template_for({}, {}, {}, "group") == row_rendering.GROUP_TEMPLATE,
        "Row rendering default group template changed",
    )
    category_text = {
        "name": "Category Label",
        "negative_prompt": "category negative",
        "positive_suffix": "category suffix",
        "style": "category style",
        "item_label": "Category Item",
    }
    subcategory_text = {
        "negative_prompt": "subcategory negative",
        "positive_suffix": "subcategory suffix",
        "style": "subcategory style",
    }
    item_text = {
        "negative_prompt": "item negative",
        "style": "item style",
    }
    _expect(
        pb._row_text_fields(category_text, subcategory_text, item_text)
        == row_rendering.resolve_row_text_fields(category_text, subcategory_text, item_text),
        "Prompt builder row text field wrapper should delegate to row_rendering",
    )
    text_fields = row_rendering.resolve_row_text_fields(category_text, subcategory_text, item_text)
    _expect(text_fields.negative_prompt == "item negative", "Row text fields did not prefer item negative prompt")
    _expect(text_fields.positive_suffix == "subcategory suffix", "Row text fields did not prefer subcategory suffix")
    _expect(text_fields.style == "item style", "Row text fields did not prefer item style")
    _expect(text_fields.item_label == "Category Item", "Row text fields did not fall back to category item label")
    default_text_fields = row_rendering.resolve_row_text_fields({"name": "Default Category"}, {}, {})
    _expect(default_text_fields.negative_prompt, "Row text fields lost default negative prompt")
    _expect(default_text_fields.positive_suffix == row_rendering.GENERIC_POSITIVE_SUFFIX, "Row text fields lost suffix default")
    _expect(default_text_fields.style == row_rendering.DEFAULT_STYLE, "Row text fields lost style default")
    _expect(default_text_fields.item_label == "Default Category", "Row text fields lost category-name label default")
    style_config = pb.build_style_config_json(preset="comic_pinup_colored_pencil")
    styled_fields = row_rendering.resolve_row_text_fields(category_text, subcategory_text, item_text, style_config)
    _expect("comic pin-up" in styled_fields.style, "Style Pool did not override row style")
    _expect("comic linework" in styled_fields.positive_suffix, "Style Pool did not override row style suffix")
    negative_style_config = pb.build_style_config_json(
        preset="realistic_photo",
        custom_negative="flat vector, comic paper texture",
    )
    negative_fields = row_rendering.resolve_row_text_fields(category_text, subcategory_text, item_text, negative_style_config)
    _expect("comic paper texture" in negative_fields.negative_prompt, "Style Pool did not merge style negatives")

    context = {
        "trigger": Trigger,
        "subject": "configured cast",
        "subject_phrase": "configured adult cast",
        "age": "adult",
        "body": "varied",
        "body_phrase": "varied",
        "skin": "",
        "hair": "",
        "eyes": "",
        "item_label": "Scene",
        "item": "shared action",
        "scene": "warm room",
        "pose": "standing close",
        "expression": "focused look",
        "composition": "centered frame",
        "composition_prompt": "vertical centered frame",
        "positive_suffix": "clear readable bodies.",
        "negative_prompt": "bad anatomy",
        "cast_descriptors": "Woman A: adult woman; Man A: adult man",
    }
    rendered = row_rendering.render_prompt_caption(
        item={},
        subcategory={
            "prompt_template": "Scene: {item}. Composition: {composition_prompt}. Avoid: {negative_prompt}.",
            "caption_template": "{trigger}, {item}, {scene}",
        },
        category={},
        subject_type="configured_cast",
        context=context,
        cast_descriptor_text="Woman A: adult woman; Man A: adult man",
        pov_prompt_directive="First-person POV from Man A.",
    )
    prompt = rendered["prompt"]
    caption = rendered["caption"]
    _expect("Characters: Woman A: adult woman; Man A: adult man." in prompt, "Row rendering lost configured-cast descriptors")
    _expect("First-person POV from Man A." in prompt, "Row rendering lost configured-cast POV directive")
    _expect(
        prompt.index("Characters:") < prompt.index("First-person POV") < prompt.index("Avoid:"),
        "Row rendering did not insert configured-cast directives before negative prompt",
    )
    _expect(
        caption.endswith("Woman A: adult woman; Man A: adult man"),
        "Row rendering did not append descriptors to captions without descriptor placeholders",
    )


def smoke_row_role_graph_policy() -> None:
    empty_route = row_role_graph.resolve_role_graph_route(
        rng=random.Random(51),
        subcategory={"slug": "penetration"},
        context={"subject_type": "woman"},
        item_axis_values={"position": "missionary"},
        pov_character_labels=[],
        is_pose_category=True,
    )
    _expect(empty_route == row_role_graph.RoleGraphRoute("", ""), "Role graph route should stay empty outside configured cast")

    context = {
        "subject_type": "configured_cast",
        "women_count": "1",
        "men_count": "1",
    }
    subcategory = {"slug": "cumshot_climax", "name": "Cumshot and climax"}
    axis_values = {"position": "lying at the bed edge with thighs open"}
    route = row_role_graph.resolve_role_graph_route(
        rng=random.Random(52),
        subcategory=subcategory,
        context=context,
        item_axis_values=axis_values,
        pov_character_labels=[],
        is_pose_category=True,
    )
    delegated = pb._role_graph_route(
        rng=random.Random(52),
        subcategory=subcategory,
        context=context,
        item_axis_values=axis_values,
        pov_character_labels=[],
        is_pose_category=True,
    )
    _expect(route == delegated, "Prompt builder role graph route wrapper should delegate to row_role_graph")
    _expect("raised edge" in route.source_role_graph, "Role graph route did not sanitize bed-edge environment anchor")
    _expect("bed edge" not in route.source_role_graph.lower(), "Role graph route leaked bed-edge environment anchor")
    _expect(route.role_graph == route.source_role_graph, "Role graph route changed non-POV role graph text")

    pov_route = row_role_graph.resolve_role_graph_route(
        rng=random.Random(53),
        subcategory={"slug": "oral", "name": "Oral"},
        context=context,
        item_axis_values={"position": "standing oral", "act": "blowjob"},
        pov_character_labels=["Man A"],
        is_pose_category=False,
    )
    _expect(pov_route.source_role_graph, "Role graph route lost POV source role graph")
    _expect(
        pov_route.role_graph.startswith("First-person POV from Man A;"),
        "Role graph route did not prepend POV role graph directive",
    )
    structured_axis_route = row_role_graph.resolve_role_graph_route(
        rng=random.Random(54),
        subcategory={"slug": "oral", "name": "Oral"},
        context=context,
        item_axis_values={
            "position": {"text": "standing oral position"},
            "oral_act": ["blowjob", "auto"],
            "contact_detail": {"unused": "mouth contact at hip height"},
        },
        pov_character_labels=[],
        is_pose_category=False,
    )
    _expect(
        "kneels in front" in structured_axis_route.source_role_graph,
        "Role graph route should read structured/list axis values through item_axis_policy",
    )


def smoke_row_assembly_policy() -> None:
    context = {
        "subject": "configured cast",
        "subject_phrase": "configured adult cast",
        "age": "21+ adults",
        "body": "varied adult builds",
        "body_phrase": "varied adult builds",
        "figure": "balanced cast",
        "skin": "warm skin tones",
        "hair": "dark hair",
        "eyes": "brown eyes",
        "cast_summary": "one woman and one man",
        "scene_kind": "configured_cast",
        "women_count": "1",
        "men_count": "1",
        "person_count": "2",
    }
    count_adjustment = {"requested_women_count": 1, "requested_men_count": 1}
    kwargs = {
        "row_number": 2,
        "start_index": 10,
        "category": {"name": "Axis Test", "slug": "axis_test"},
        "subcategory": {
            "name": "Custom Scene",
            "slug": "custom_scene",
            "prompt_template": "Scene: {item}. Composition: {composition_prompt}. Avoid: {negative_prompt}.",
            "caption_template": "{trigger}, {item}, {scene}",
        },
        "item": {"text": "shared structured action"},
        "context": context,
        "subject_type": "configured_cast",
        "item_text": "shared structured action",
        "item_name": "shared_action",
        "item_axis_values": {"action_family": "test_action"},
        "item_template_metadata": {"position_key": "test_position"},
        "formatter_hints": {"krea": ["test_hint"]},
        "item_label": "Scene",
        "style": "clean test style",
        "positive_suffix": "clear readable composition.",
        "negative_prompt": "bad anatomy",
        "scene_slug": "test_room",
        "scene": "warm test room",
        "scene_entry": {"slug": "test_room", "prompt": "warm test room", "theme": "fixture_theme"},
        "pose": "standing close",
        "expression": "focused look",
        "shared_expression": "focused look",
        "character_expressions": ["Woman A has focused look"],
        "character_expression_text": "Woman A has focused look",
        "expression_disabled": True,
        "expression_intensity": 0.7,
        "expression_intensity_source": "disabled",
        "composition": "centered frame",
        "source_composition": "centered frame",
        "composition_entry": {"prompt": "centered frame"},
        "role_graph": "the visible partner stays centered",
        "source_role_graph": "Man A stays centered",
        "action_family": "test_action",
        "position_family": "standing",
        "position_key": "test_position",
        "position_keys": ["test_position"],
        "pov_character_labels": ["Man A"],
        "cast_descriptors": ["Woman A: adult woman", "Man A: adult man"],
        "cast_descriptor_text": "Woman A: adult woman; Man A: adult man",
        "seed_config": {"content_seed": 123},
        "hardcore_position_config": {"family": "standing"},
        "location_config": {"location": "test_room", "theme": "fixture_theme", "apply_mode": "replace"},
        "composition_config": {"composition": "centered", "theme": "fixture_theme"},
        "content_seed_axis": "pose",
        "count_adjustment": count_adjustment,
        "applied_profile": {"name": "profile_a"},
        "profile_status": "applied",
        "applied_slot": {"label": "Woman A"},
        "slot_status": "applied",
        "character_slots": [{"label": "Woman A"}, {"label": "Man A"}],
    }
    request = row_assembly.CustomRowAssemblyRequest(**kwargs)
    row = row_assembly.assemble_custom_row(request)
    delegated = pb._assemble_custom_row(request)

    _expect(row == delegated, "Prompt builder row assembly wrapper should delegate without changing output")
    _expect(request.content_seed_axis == "pose", "Row assembly request lost seed-axis metadata")
    _expect(row["id"] == "sxcp_0011", "Row assembly changed row indexing")
    _expect(row["batch"] == "batch_001", "Row assembly changed batch calculation")
    _expect(row["source"] == "json_category", "Row assembly lost source marker")
    _expect(row["figure"] == "balanced cast", "Row assembly lost figure metadata")
    _expect(row["formatter_hints"] == {"krea": ["test_hint"]}, "Row assembly lost formatter hints")
    _expect(row["scene_entry"].get("slug") == "test_room", "Row assembly lost selected scene entry")
    _expect(row["location_theme"] == "fixture_theme", "Row assembly lost location theme")
    _expect(row["scene_theme"] == "fixture_theme", "Row assembly lost selected scene theme")
    _expect(row["composition_entry"].get("prompt") == "centered frame", "Row assembly lost selected composition entry")
    _expect(row["composition_theme"] == "fixture_theme", "Row assembly lost composition theme")
    _expect(row["cast_count_adjustment"] == count_adjustment, "Row assembly lost configured-cast count adjustment")
    _expect(row["content_seed_axis"] == "pose", "Row assembly lost content seed axis")
    _expect("POV participant: Man A" in row["prompt"], "Row assembly lost POV prompt directive")
    _expect("Characters: Woman A: adult woman; Man A: adult man." in row["prompt"], "Row assembly lost cast descriptor insertion")
    _expect(row["caption"].endswith("Woman A: adult woman; Man A: adult man"), "Row assembly lost caption descriptor append")
    _expect(row["expression"] == "", "Disabled expression should clear row expression")
    _expect(row["expression_enabled"] is False, "Disabled expression should mark expression disabled")
    _expect(row["expression_intensity"] is None, "Disabled expression should clear intensity")
    _expect(row["expression_intensity_source"] == "disabled", "Disabled expression should preserve disabled source")


def smoke_formatter_input_policy() -> None:
    source_row = {
        "prompt": "A simple adult portrait. Setting: quiet studio. Pose: standing calmly. Avoid: low quality.",
        "caption": "adult portrait, quiet studio",
        "negative_prompt": "low quality",
        "subject_type": "woman",
        "primary_subject": "woman",
        "age": "25-year-old adult",
        "body_phrase": "average figure",
        "skin": "warm skin",
        "hair": "dark hair",
        "eyes": "brown eyes",
        "item": "black dress",
        "scene_text": "quiet studio",
        "pose": "standing calmly",
        "composition": "centered portrait",
        "trigger": Trigger,
    }
    source_json = _json(source_row)

    _expect(
        formatter_input.input_hint_choices(text_hint=formatter_input.INPUT_HINT_PROMPT) == ["auto", "metadata_json", "prompt"],
        "Formatter prompt input-hint choices changed",
    )
    _expect(
        formatter_input.input_hint_choices(text_hint=formatter_input.INPUT_HINT_CAPTION_OR_PROMPT)
        == ["auto", "metadata_json", "caption_or_prompt"],
        "Formatter caption input-hint choices changed",
    )
    _expect(
        formatter_input.normalize_input_hint("bad_hint") == "auto",
        "Formatter input-hint policy should normalize invalid values to auto",
    )
    _expect(
        formatter_input.normalize_input_hint("caption", text_hint=formatter_input.INPUT_HINT_CAPTION_OR_PROMPT)
        == "caption_or_prompt",
        "Formatter input-hint policy lost caption alias",
    )
    _expect(
        formatter_input.normalize_input_hint("caption_or_prompt", text_hint=formatter_input.INPUT_HINT_PROMPT) == "prompt",
        "Formatter input-hint policy should map text hints to the route's text mode",
    )

    row, method = formatter_input.row_from_inputs(source_json, "", "auto")
    _expect(method == "source_json", "Formatter input parser should read source JSON when metadata is empty")
    _expect(row == source_row, "Formatter input parser changed parsed JSON row")
    row, method = formatter_input.row_from_inputs(source_json, "", "bad_hint")
    _expect(method == "source_json" and row == source_row, "Formatter input parser should treat invalid hints as auto")
    row, method = formatter_input.row_from_inputs(source_json, "", "prompt")
    _expect(row is None and method == "text", "Formatter input parser should not parse source JSON in explicit prompt mode")
    pair_metadata = {
        "trigger": Trigger,
        "softcore_row": {
            "prompt": f"{Trigger}, {Trigger}, embedded-only soft.",
            "caption": f"{Trigger}, {Trigger}, embedded-only soft caption.",
            "negative_prompt": "bad anatomy, bad anatomy",
            "softcore_partner_styling": {"outfits": ["row partner outfit"], "pose": "row partner pose"},
            "camera_config": {"camera_mode": "standard"},
            "camera_directive": "Camera: row soft front view.",
            "camera_scene_directive": "Row soft scene camera layout.",
        },
        "hardcore_row": {
            "prompt": f"{Trigger}, {Trigger}, embedded-only hard.",
            "caption": f"{Trigger}, {Trigger}, embedded-only hard caption.",
            "negative_prompt": "low quality, low quality",
            "hardcore_clothing_state": "row hard clothing state",
            "camera_config": {"camera_mode": "pov"},
            "camera_scene_directive": "Row hard scene camera layout.",
        },
    }
    _expect(formatter_input.is_pair_metadata(pair_metadata), "Formatter input policy should detect structural pair metadata")
    parsed_pair, pair_method = formatter_input.row_from_inputs("", _json(pair_metadata), "metadata_json")
    _expect(pair_method == "metadata_json", "Formatter input parser should read pair metadata JSON")
    _expect(formatter_input.is_pair_metadata(parsed_pair), "Formatter input parser should preserve structural pair metadata")
    _expect_trigger_once("formatter_input.pair.soft_prompt", parsed_pair.get("softcore_prompt"), Trigger)
    _expect(
        parsed_pair.get("softcore_partner_styling") == parsed_pair["softcore_row"].get("softcore_partner_styling"),
        "Formatter input parser did not normalize pair soft side metadata",
    )
    _expect(
        parsed_pair.get("hardcore_clothing_state") == parsed_pair["hardcore_row"].get("hardcore_clothing_state"),
        "Formatter input parser did not normalize pair hard side metadata",
    )
    _expect(
        parsed_pair.get("softcore_camera_config") == parsed_pair["softcore_row"].get("camera_config"),
        "Formatter input parser did not normalize pair camera metadata",
    )
    _expect_no_duplicate_comma_items("formatter_input.pair.hard_negative", parsed_pair.get("hardcore_negative_prompt"))
    _expect(formatter_input.split_avoid("Prompt body. Avoid: blur, watermark") == ("Prompt body", "blur, watermark"), "Avoid split changed")
    _expect(
        formatter_input.prompt_field(source_row["prompt"], "Setting") == "quiet studio",
        "Prompt field extraction changed",
    )
    _expect(
        formatter_input.row_value({"prompt": source_row["prompt"]}, "scene_text", ("Setting",)) == "quiet studio",
        "Row value prompt fallback changed",
    )
    _expect(
        krea_formatter.PROMPT_FIELD_LABELS is formatter_input.DEFAULT_PROMPT_FIELD_LABELS,
        "Krea formatter field-label inventory should delegate to formatter_input",
    )
    _expect(
        sdxl_formatter.PROMPT_FIELD_LABELS is formatter_input.DEFAULT_PROMPT_FIELD_LABELS,
        "SDXL formatter field-label inventory should delegate to formatter_input",
    )
    _expect(
        caption_naturalizer.PROMPT_FIELD_LABELS is formatter_input.DEFAULT_PROMPT_FIELD_LABELS,
        "Caption formatter field-label inventory should delegate to formatter_input",
    )
    labeled_prompt = "Sexual scene: close-contact action. Camera control: side view. Composition: centered frame."
    _expect(
        formatter_input.prompt_field(labeled_prompt, "Sexual scene") == "close-contact action",
        "Shared formatter field-label inventory lost Sexual scene parsing",
    )
    _expect(
        formatter_input.prompt_field(labeled_prompt, "Camera control") == "side view",
        "Shared formatter field-label inventory lost Camera control parsing",
    )
    stripped_labels = formatter_input.strip_prompt_field_labels(
        "Characters: woman. Erotic outfit: sheer dress. Camera: side view."
    )
    _expect("Characters:" not in stripped_labels, "Shared label stripper did not remove Characters label")
    _expect("Erotic outfit:" not in stripped_labels, "Shared label stripper did not remove Erotic outfit label")
    _expect("Camera:" not in stripped_labels, "Shared label stripper did not remove Camera label")

    _expect(krea_formatter._clean("a  b , c") == formatter_input.clean_text("a  b , c"), "Krea clean helper is not delegated")
    _expect(sdxl_formatter._clean("a  b , c") == formatter_input.clean_text("a  b , c"), "SDXL clean helper is not delegated")
    _expect(
        sdxl_formatter._strip_prompt_field_labels("Characters: woman. Camera: side view.") == "woman. side view.",
        "SDXL label stripper should delegate to formatter_input",
    )
    _expect(caption_naturalizer._clean_text("a  b , c") == formatter_input.clean_text("a  b , c"), "Caption clean helper is not delegated")
    _expect(krea_formatter._strip_trigger(f"{Trigger}, prompt text", False) == "prompt text", "Krea trigger stripping changed")
    _expect(sdxl_formatter._strip_trigger(f"{SdxlTrigger}, prompt text", False) == "prompt text", "SDXL trigger stripping changed")
    _expect(caption_naturalizer._remove_trigger(Trigger, Trigger) == "", "Caption exact-trigger removal changed")

    krea = krea_formatter.format_krea2_prompt(source_json, input_hint="auto")
    sdxl = sdxl_formatter.format_sdxl_prompt(source_json, input_hint="auto", trigger=SdxlTrigger, prepend_trigger=True)
    caption, caption_method = caption_naturalizer.naturalize_caption(source_json, input_hint="auto", trigger=Trigger)
    _expect(krea.get("method", "").startswith("source_json:krea2("), "Krea formatter did not use shared source JSON parsing")
    _expect(sdxl.get("method", "").startswith("source_json:sdxl("), "SDXL formatter did not use shared source JSON parsing")
    _expect(caption_method.startswith("source_json:metadata("), "Caption naturalizer did not use shared source JSON parsing")
    _expect_text("formatter_input.krea_prompt", krea.get("krea_prompt"), 20)
    _expect_text("formatter_input.sdxl_prompt", sdxl.get("sdxl_prompt"), 20)
    _expect_text("formatter_input.caption", caption, 20)

    bad_hint_krea = krea_formatter.format_krea2_prompt(source_json, input_hint="bad_hint")
    bad_hint_sdxl = sdxl_formatter.format_sdxl_prompt(
        source_json,
        input_hint="bad_hint",
        trigger=SdxlTrigger,
        prepend_trigger=True,
    )
    bad_hint_caption, bad_hint_caption_method = caption_naturalizer.naturalize_caption(
        source_json,
        input_hint="bad_hint",
        trigger=Trigger,
    )
    _expect(
        bad_hint_krea.get("method", "").startswith("source_json:krea2("),
        "Krea formatter did not normalize bad input hint to auto",
    )
    _expect(
        bad_hint_sdxl.get("method", "").startswith("source_json:sdxl("),
        "SDXL formatter did not normalize bad input hint to auto",
    )
    _expect(
        bad_hint_caption_method.startswith("source_json:metadata("),
        "Caption formatter did not normalize bad input hint to auto",
    )

    fallback_sdxl = sdxl_formatter.format_sdxl_prompt(
        "Characters: woman. Erotic outfit: sheer dress. Camera: side view. Avoid: blur",
        input_hint="prompt",
        style_preset="none",
        quality_preset="none",
        trigger=SdxlTrigger,
        prepend_trigger=False,
    )
    fallback_prompt = fallback_sdxl.get("sdxl_prompt", "")
    _expect("Characters:" not in fallback_prompt, "SDXL fallback leaked Characters label")
    _expect("Erotic outfit:" not in fallback_prompt, "SDXL fallback leaked Erotic outfit label")
    _expect("Camera:" not in fallback_prompt, "SDXL fallback leaked Camera label")
    _expect("blur" in fallback_sdxl.get("negative_prompt", ""), "SDXL fallback lost Avoid negative text")


def smoke_formatter_target_policy() -> None:
    _expect(
        formatter_target.target_choices() == ["auto", "single", "softcore", "hardcore"],
        "Formatter target choices changed",
    )
    _expect(formatter_target.normalize_target("single") == "single", "Formatter target lost single")
    _expect(formatter_target.normalize_target("Hard-Core") == "hardcore", "Formatter target alias lost hardcore")
    _expect(formatter_target.normalize_target("soft") == "softcore", "Formatter target alias lost softcore")
    _expect(formatter_target.normalize_target("bad target") == "auto", "Formatter target should normalize invalid values")

    auto_pair = formatter_target.pair_policy("auto")
    _expect(auto_pair.target == "auto", "Pair target policy lost normalized auto target")
    _expect(auto_pair.pair_target == "auto", "Pair target policy lost auto pair target")
    _expect(auto_pair.selected_side == "softcore", "Pair auto should select softcore side for single-output formatters")
    _expect(auto_pair.include_softcore and auto_pair.include_hardcore, "Pair auto should include both sides for combined captions")

    single_pair = formatter_target.pair_policy("single")
    _expect(single_pair.target == "single", "Pair target policy lost normalized single target")
    _expect(single_pair.pair_target == "auto", "Pair single should map to auto for pair inclusion")
    _expect(single_pair.selected_side == "softcore", "Pair single should select softcore side by default")
    _expect(single_pair.include_softcore and single_pair.include_hardcore, "Pair single should include both sides when treated as auto")

    hard_pair = formatter_target.pair_policy("hard")
    _expect(hard_pair.target == "hardcore", "Pair target policy lost hard alias")
    _expect(hard_pair.pair_target == "hardcore", "Pair hard alias should become hardcore pair target")
    _expect(hard_pair.selected_side == "hardcore", "Pair hardcore should select hardcore side")
    _expect(not hard_pair.include_softcore and hard_pair.include_hardcore, "Pair hardcore should include only hard side")


def smoke_formatter_detail_policy() -> None:
    _expect(
        formatter_detail.detail_level_choices() == ["balanced", "concise", "dense"],
        "Formatter detail choices changed",
    )
    _expect(formatter_detail.normalize_detail_level("dense") == "dense", "Formatter detail lost dense")
    _expect(formatter_detail.normalize_detail_level("BAD") == "balanced", "Formatter detail should normalize invalid values")
    _expect(formatter_detail.detail_allows("concise") is False, "Formatter detail concise gate changed")
    _expect(formatter_detail.detail_allows("dense", dense_only=True) is True, "Formatter detail dense-only gate changed")
    _expect(caption_policy.DETAIL_LEVELS is formatter_detail.DETAIL_LEVELS, "Caption detail levels should delegate")
    _expect(
        caption_policy.normalize_detail_level("bad") == formatter_detail.normalize_detail_level("bad"),
        "Caption detail normalization should delegate",
    )
    _expect(
        caption_policy.detail_allows("dense", dense_only=True) == formatter_detail.detail_allows("dense", dense_only=True),
        "Caption detail gate should delegate",
    )


def smoke_krea_format_route_policy() -> None:
    row = _prompt_row(
        name="krea_format_route_single",
        category="woman",
        subcategory="random",
        seed=3601,
        men_count=0,
        camera_config=_orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5),
    )
    single_request = krea_format_route.KreaFormatRequest(
        source_text="",
        metadata_json=_json(row),
        target="single",
        detail_level="dense",
        style_mode="photographic",
        extra_positive="krea route marker",
        extra_negative="krea route negative",
    )
    typed_single = krea_format_route.format_krea2_prompt_result(
        single_request,
        krea_formatter._krea_format_dependencies(),
    )
    public_single = krea_formatter.format_krea2_prompt(
        "",
        metadata_json=single_request.metadata_json,
        target=single_request.target,
        detail_level=single_request.detail_level,
        style_mode=single_request.style_mode,
        extra_positive=single_request.extra_positive,
        extra_negative=single_request.extra_negative,
    )
    _expect(typed_single.output == public_single, "Typed Krea format route should match public single formatter output")
    _expect(typed_single.branch == "metadata(single)", "Typed Krea format route changed single branch")
    _expect(typed_single.target == "single", "Typed Krea format route lost target normalization")
    single_trace = json.loads(_expect_text("krea_format_route_policy.single_trace", typed_single.output.get("route_trace_json"), 20))
    _expect(single_trace.get("formatter") == "krea2", "Typed Krea single trace lost formatter")
    _expect(single_trace.get("branch") == "metadata(single)", "Typed Krea single trace lost branch")
    _expect(single_trace.get("target") == "single", "Typed Krea single trace lost target")
    _expect("krea route marker" in typed_single.output.get("krea_prompt", ""), "Typed Krea route lost extra positive")
    _expect("krea route negative" in typed_single.output.get("negative_prompt", ""), "Typed Krea route lost extra negative")

    pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=3602,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(),
        character_cast=_character_cast(),
        hardcore_position_config=_action_filter("penetration_only"),
    )
    pair_request = krea_format_route.KreaFormatRequest(
        source_text="",
        metadata_json=_json(pair),
        target="hardcore",
        detail_level="balanced",
        style_mode="preserve",
        extra_positive="pair route marker",
        extra_negative="pair route negative",
    )
    typed_pair = krea_format_route.format_krea2_prompt_result(
        pair_request,
        krea_formatter._krea_format_dependencies(),
    )
    public_pair = krea_formatter.format_krea2_prompt(
        "",
        metadata_json=pair_request.metadata_json,
        target=pair_request.target,
        detail_level=pair_request.detail_level,
        style_mode=pair_request.style_mode,
        extra_positive=pair_request.extra_positive,
        extra_negative=pair_request.extra_negative,
    )
    _expect(typed_pair.output == public_pair, "Typed Krea format route should match public pair formatter output")
    _expect(typed_pair.branch == "insta_of_pair", "Typed Krea format route changed pair branch")
    pair_trace = json.loads(_expect_text("krea_format_route_policy.pair_trace", typed_pair.output.get("route_trace_json"), 20))
    _expect(pair_trace.get("branch") == "insta_of_pair", "Typed Krea pair trace lost branch")
    _expect(pair_trace.get("selected_side") == "hardcore", "Typed Krea pair trace lost selected side")
    _expect_text("krea_format_route_policy.hard_prompt", typed_pair.output.get("krea_hardcore_prompt"), 40)
    _expect("pair route marker" in typed_pair.output.get("krea_prompt", ""), "Typed Krea pair route lost extra positive")

    fallback_request = krea_format_route.KreaFormatRequest(
        source_text="Scene: quiet studio. Pose: seated portrait. Avoid: blur",
        input_hint="prompt",
        target="weird",
        detail_level="verbose",
        style_mode="invalid",
        preserve_trigger=False,
    )
    typed_fallback = krea_format_route.format_krea2_prompt_result(
        fallback_request,
        krea_formatter._krea_format_dependencies(),
    )
    public_fallback = krea_formatter.format_krea2_prompt(
        fallback_request.source_text,
        input_hint=fallback_request.input_hint,
        target=fallback_request.target,
        detail_level=fallback_request.detail_level,
        style_mode=fallback_request.style_mode,
        preserve_trigger=fallback_request.preserve_trigger,
    )
    _expect(typed_fallback.output == public_fallback, "Typed Krea format route should match public fallback output")
    _expect(typed_fallback.branch == "fallback", "Typed Krea format route changed fallback branch")
    _expect(typed_fallback.target == "auto", "Typed Krea format route should normalize invalid target")
    fallback_trace = json.loads(_expect_text("krea_format_route_policy.fallback_trace", typed_fallback.output.get("route_trace_json"), 20))
    _expect(fallback_trace.get("branch") == "fallback", "Typed Krea fallback trace lost branch")
    _expect(fallback_trace.get("input_hint") == "prompt", "Typed Krea fallback trace lost input hint")
    _expect(typed_fallback.detail_level == "balanced", "Typed Krea format route should normalize invalid detail level")
    _expect(typed_fallback.style_mode == "preserve", "Typed Krea format route should normalize invalid style mode")
    _expect(krea_format_route.style_mode_choices() == ["preserve", "photographic", "minimal"], "Krea style mode choices changed")
    _expect(krea_format_route.normalize_style_mode("photographic") == "photographic", "Krea style mode lost photographic")
    _expect(krea_format_route.normalize_style_mode("bad") == "preserve", "Krea style mode invalid fallback changed")
    _expect("blur" in typed_fallback.output.get("negative_prompt", ""), "Typed Krea fallback route lost Avoid negative")


def smoke_formatter_cast_policy() -> None:
    descriptor = (
        "Woman A / primary creator: 25-year-old adult woman, average figure, warm skin, dark hair; "
        "Man A: 40-year-old adult man, average figure, tan skin, short dark hair"
    )
    entries = [
        ("Woman A", "25-year-old adult woman, average figure, warm skin, dark hair"),
        ("Man A", "40-year-old adult man, average figure, tan skin, short dark hair"),
    ]
    _expect(krea_cast.cast_entries(descriptor) == entries, "Shared cast entry parser changed")
    _expect(caption_naturalizer._cast_entries(descriptor) == entries, "Caption cast parser should delegate to shared cast policy")
    _expect(krea_cast.cast_labels(descriptor) == ["Woman A", "Man A"], "Shared cast label parser changed")
    _expect(
        caption_naturalizer._cast_labels(descriptor) == krea_cast.cast_labels(descriptor),
        "Caption cast labels should delegate to shared cast policy",
    )
    natural = krea_cast.natural_cast_descriptor_text(descriptor)
    _expect(natural.startswith("A 25-year-old adult woman"), "Shared natural cast descriptor text changed")
    _expect(caption_naturalizer._natural_cast_descriptor_text(descriptor) == natural, "Caption cast descriptor text should delegate")
    _expect(
        krea_cast.natural_label_text("Woman A faces Man A.", ["Woman A", "Man A"]) == "The woman faces the man.",
        "Krea natural label text should keep sentence capitalization",
    )
    _expect(
        caption_naturalizer._natural_label_text("Woman A faces Man A.", ["Woman A", "Man A"]) == "the woman faces the man.",
        "Caption natural label text should preserve previous lowercase inline behavior",
    )


def smoke_caption_policy() -> None:
    _expect(
        caption_naturalizer.STYLE_TAILS is caption_policy.STYLE_TAILS,
        "Caption naturalizer style tails should delegate to caption_policy",
    )
    _expect(
        caption_naturalizer.ITEM_LABELS is caption_policy.ITEM_LABELS,
        "Caption naturalizer item labels should delegate to caption_policy",
    )
    _expect(
        caption_naturalizer.ACTION_FAMILY_CAPTION_LABELS is caption_policy.ACTION_FAMILY_CAPTION_LABELS,
        "Caption naturalizer action labels should delegate to caption_policy",
    )
    _expect(caption_policy.normalize_detail_level("bad") == "balanced", "Caption invalid detail fallback changed")
    _expect(caption_policy.normalize_style_policy("bad") == "drop_style_tail", "Caption invalid style fallback changed")
    _expect(
        caption_policy.normalize_style_policy("Keep Style Terms") == "keep_style_terms",
        "Caption style policy should normalize spaces/case",
    )
    _expect(caption_policy.style_policy_choices() == ["drop_style_tail", "keep_style_terms"], "Caption style policy choices changed")
    _expect(caption_policy.keep_style_terms("keep_style_terms") is True, "Caption style policy keep flag changed")
    _expect(caption_policy.detail_allows("concise") is False, "Caption concise detail gate changed")
    _expect(caption_policy.detail_allows("dense", dense_only=True) is True, "Caption dense-only gate changed")
    _expect("training_concise" in caption_policy.caption_profile_choices(), "Caption profile choices lost training_concise")
    _expect(
        caption_policy.normalize_caption_profile("bad") == caption_policy.CAPTION_PROFILE_DEFAULT,
        "Caption invalid profile fallback changed",
    )
    _expect(
        caption_policy.normalize_caption_profile("training-dense") == "training_dense",
        "Caption profile should normalize hyphen spelling",
    )
    _expect(
        caption_policy.apply_caption_profile(
            "training_dense",
            detail_level="concise",
            style_policy="keep_style_terms",
            include_trigger=False,
        )
        == ("dense", "drop_style_tail", True),
        "Caption training_dense profile overrides changed",
    )
    _expect(
        caption_policy.apply_caption_profile(
            "manual_controls",
            detail_level="concise",
            style_policy="keep_style_terms",
            include_trigger=False,
        )
        == ("concise", "keep_style_terms", False),
        "Caption manual profile should preserve explicit controls",
    )

    style_tail = caption_policy.STYLE_TAILS[0]
    _expect(
        caption_policy.strip_style_tail(f"caption body{style_tail}") == "caption body",
        "Caption style-tail stripping changed",
    )
    _expect(
        caption_naturalizer._strip_style_tail(f"caption body{style_tail}") == "caption body",
        "Caption naturalizer style-tail wrapper should delegate",
    )
    _expect(
        caption_policy.normalize_composition("vertical centered body frame") == "centered body frame",
        "Caption composition normalization changed",
    )
    _expect(
        caption_policy.normalize_composition("vertical centered body frame composition") == "centered body frame",
        "Caption composition should drop trailing composition label",
    )
    _expect(
        caption_policy.clean_clothing("silk dress, fashion editorial styling") == "silk dress",
        "Caption clothing cleanup changed",
    )
    row = {"action_family": "oral", "position_family": ""}
    _expect(caption_policy.metadata_action_label(row) == "oral action", "Caption action-family label changed")
    row = {"action_family": "anal", "position_family": ""}
    _expect(caption_policy.metadata_action_label(row) == "anal action", "Caption anal action-family label changed")
    row = {"action_family": "manual", "position_family": ""}
    _expect(caption_policy.metadata_action_label(row) == "manual action", "Caption manual action-family label changed")
    row = {"action_family": "threesome", "position_family": ""}
    _expect(caption_policy.metadata_action_label(row) == "three-person action", "Caption threesome action-family label changed")
    row = {"action_family": "group", "position_family": ""}
    _expect(caption_policy.metadata_action_label(row) == "group action", "Caption group action-family label changed")
    row = {"action_family": "oral", "position_family": "Anal"}
    _expect(caption_naturalizer._metadata_action_label(row) == "anal action", "Caption position-family label priority changed")
    browsing_caption, browsing_method = caption_naturalizer.naturalize_caption(
        "woman, red dress, studio",
        caption_profile="browsing",
        include_trigger=True,
    )
    _expect(not browsing_caption.startswith(Trigger), "Caption browsing profile should disable trigger by default")
    _expect(browsing_method == "text(fallback)", "Caption browsing profile changed fallback method")


def smoke_caption_format_route_policy() -> None:
    row = _prompt_row(
        name="caption_format_route_single",
        category="woman",
        subcategory="random",
        seed=3801,
        men_count=0,
    )
    metadata_request = caption_format_route.CaptionFormatRequest(
        source_text="",
        metadata_json=_json(row),
        input_hint="metadata_json",
        target="single",
        trigger=Trigger,
        include_trigger=False,
        detail_level="concise",
        style_policy="keep_style_terms",
        caption_profile="training_dense",
    )
    typed_metadata = caption_format_route.naturalize_caption_result(
        metadata_request,
        caption_naturalizer._caption_format_dependencies(),
    )
    public_metadata = caption_naturalizer.naturalize_caption(
        "",
        metadata_json=metadata_request.metadata_json,
        input_hint=metadata_request.input_hint,
        target=metadata_request.target,
        trigger=metadata_request.trigger,
        include_trigger=metadata_request.include_trigger,
        detail_level=metadata_request.detail_level,
        style_policy=metadata_request.style_policy,
        caption_profile=metadata_request.caption_profile,
    )
    _expect(typed_metadata.as_tuple() == public_metadata, "Typed caption format route should match public metadata output")
    _expect(typed_metadata.branch == "metadata", "Typed caption format route changed metadata branch")
    _expect(typed_metadata.input_hint == "metadata_json", "Typed caption route lost input hint")
    _expect(typed_metadata.target == "single", "Typed caption route lost target normalization")
    _expect(typed_metadata.detail_level == "dense", "Typed caption route lost training_dense detail override")
    _expect(typed_metadata.style_policy == "drop_style_tail", "Typed caption route lost training_dense style override")
    _expect(typed_metadata.include_trigger is True, "Typed caption route lost training_dense trigger override")
    metadata_trace = json.loads(_expect_text("caption_format_route_policy.metadata_trace", typed_metadata.route_trace_json, 20))
    _expect(metadata_trace.get("formatter") == "caption", "Typed caption metadata trace lost formatter")
    _expect(metadata_trace.get("branch") == "metadata", "Typed caption metadata trace lost branch")
    _expect(metadata_trace.get("target") == "single", "Typed caption metadata trace lost target")
    traced_public_metadata = caption_naturalizer.naturalize_caption_with_trace(
        "",
        metadata_json=metadata_request.metadata_json,
        input_hint=metadata_request.input_hint,
        target=metadata_request.target,
        trigger=metadata_request.trigger,
        include_trigger=metadata_request.include_trigger,
        detail_level=metadata_request.detail_level,
        style_policy=metadata_request.style_policy,
        caption_profile=metadata_request.caption_profile,
    )
    _expect(traced_public_metadata == typed_metadata.as_trace_tuple(), "Caption trace wrapper drifted from typed route")
    _expect(typed_metadata.caption.startswith(Trigger), "Typed caption metadata route should prepend training trigger")

    fallback_request = caption_format_route.CaptionFormatRequest(
        source_text="woman, red dress, studio, coloured pencil comic illustration",
        input_hint="bad_hint",
        target="weird",
        trigger=Trigger,
        include_trigger=True,
        detail_level="dense",
        style_policy="drop_style_tail",
        caption_profile="browsing",
    )
    typed_fallback = caption_format_route.naturalize_caption_result(
        fallback_request,
        caption_naturalizer._caption_format_dependencies(),
    )
    public_fallback = caption_naturalizer.naturalize_caption(
        fallback_request.source_text,
        input_hint=fallback_request.input_hint,
        target=fallback_request.target,
        trigger=fallback_request.trigger,
        include_trigger=fallback_request.include_trigger,
        detail_level=fallback_request.detail_level,
        style_policy=fallback_request.style_policy,
        caption_profile=fallback_request.caption_profile,
    )
    _expect(typed_fallback.as_tuple() == public_fallback, "Typed caption format route should match public fallback output")
    _expect(typed_fallback.branch == "text", "Typed caption format route changed fallback branch")
    _expect(typed_fallback.input_hint == "auto", "Typed caption route should normalize invalid input hint")
    _expect(typed_fallback.target == "auto", "Typed caption route should normalize invalid target")
    _expect(typed_fallback.include_trigger is False, "Typed caption browsing profile should disable trigger")
    fallback_trace = json.loads(_expect_text("caption_format_route_policy.fallback_trace", typed_fallback.route_trace_json, 20))
    _expect(fallback_trace.get("branch") == "text", "Typed caption fallback trace lost branch")
    _expect(fallback_trace.get("input_hint") == "auto", "Typed caption fallback trace lost input hint")
    _expect(typed_fallback.keep_style is True, "Typed caption browsing profile should keep style terms")
    _expect(not typed_fallback.caption.startswith(Trigger), "Typed caption fallback route should not prepend browsing trigger")
    _expect(typed_fallback.method == "text(fallback)", "Typed caption fallback method changed")


def smoke_caption_text_policy() -> None:
    row = {
        "primary_subject": "woman",
        "age_band": "25-year-old adult",
        "body_phrase": "slim figure",
        "caption": f"{Trigger}, woman, 25-year-old adult, slim figure, fair skin, blonde hair, blue eyes, studio",
        "formatter_hints": {"caption": ["caption policy hint"]},
    }
    _expect(
        caption_naturalizer._body_phrase("slim", "balanced figure") == caption_text_policy.body_phrase("slim", "balanced figure"),
        "Caption body phrase wrapper should delegate to caption_text_policy",
    )
    _expect(
        caption_naturalizer._single_caption_front(row) == caption_text_policy.single_caption_front(row),
        "Caption front parser wrapper should delegate to caption_text_policy",
    )
    _expect(
        caption_naturalizer._formatter_hint_parts(row) == caption_text_policy.formatter_hint_parts(row),
        "Caption formatter hint wrapper should delegate to caption_text_policy",
    )
    _expect(
        caption_naturalizer._append_formatter_hints("Base sentence.", row)
        == caption_text_policy.append_formatter_hints("Base sentence.", row),
        "Caption formatter hint append wrapper should delegate to caption_text_policy",
    )
    _expect(
        caption_naturalizer._with_trigger("A caption body", Trigger, True)
        == caption_text_policy.with_trigger("A caption body", Trigger, True),
        "Caption trigger wrapper should delegate to caption_text_policy",
    )
    axis_detail_row = {
        "item_axis_values": {
            "position": "standing oral position",
            "contact_detail": "mouth contact at hip height",
            "duplicate": "standing oral position",
            "ignored": "random",
        }
    }
    _expect(
        caption_text_policy.item_axis_detail_text(axis_detail_row, "generic action")
        == "standing oral position and mouth contact at hip height",
        "Caption axis detail text should flatten selected item axes",
    )
    _expect(
        caption_text_policy.item_axis_detail_text(axis_detail_row, "standing oral position already appears")
        == "mouth contact at hip height",
        "Caption axis detail text should skip details already present in item prose",
    )
    deps = caption_naturalizer._caption_metadata_route_dependencies()
    _expect(deps.clean_text is caption_text_policy.clean_text, "Caption route deps lost clean text policy")
    _expect(deps.field_row_value is caption_text_policy.field_row_value, "Caption route deps lost field row-value policy")
    _expect(deps.expression_disabled is caption_text_policy.expression_disabled, "Caption route deps lost expression policy")
    _expect(deps.single_caption_front is caption_text_policy.single_caption_front, "Caption route deps lost front parser")
    _expect(deps.item_axis_detail_text is caption_text_policy.item_axis_detail_text, "Caption route deps lost item-axis detail policy")
    _expect(deps.metadata_to_prose is caption_naturalizer._metadata_to_prose, "Caption route deps lost metadata recursion callback")


def _expect_caption_route_parity(
    name: str,
    row: dict[str, Any],
    route_builder: Callable[
        [caption_metadata_routes.CaptionMetadataRouteRequest, caption_metadata_routes.CaptionMetadataRouteDependencies],
        caption_metadata_routes.CaptionMetadataRoute | None,
    ],
    wrapper: Callable[[dict[str, Any], str, bool], tuple[str, str] | None],
    expected_method: str,
) -> None:
    request = caption_naturalizer._caption_metadata_route_request(row, "balanced", False)
    deps = caption_naturalizer._caption_metadata_route_dependencies()
    typed_route = route_builder(request, deps)
    legacy_route = wrapper(row, "balanced", False)
    _expect(typed_route is not None, f"{name} typed caption metadata route did not match")
    assert typed_route is not None
    _expect(
        typed_route.as_tuple() == legacy_route,
        f"{name} typed caption metadata route should match legacy wrapper output",
    )
    _expect(typed_route.method == expected_method, f"{name} caption route method changed")
    _expect_text(f"{name}.caption_route", typed_route.prose, 20)


def smoke_caption_metadata_routes() -> None:
    single = {
        "primary_subject": "woman",
        "age_band": "25-year-old adult",
        "body_phrase": "slim figure",
        "skin": "fair skin",
        "hair": "long blonde hair",
        "eyes": "blue eyes",
        "item": "silk dress",
        "pose": "standing beside a window",
        "scene_text": "quiet studio with warm daylight",
        "expression": "soft smile",
        "composition": "vertical centered portrait",
    }
    _expect_caption_route_parity(
        "caption_route_single",
        single,
        caption_metadata_routes.single_from_row_result,
        caption_naturalizer._single_from_row,
        "metadata(single)",
    )

    couple = {
        "primary_subject": "a woman and a man",
        "subject_phrase": "woman and man",
        "age": "25-year-old adult and 40-year-old adult",
        "body": "slim and average builds",
        "item": "Partner A wears black dress; Partner B wears dark shirt",
        "pose": "standing close together",
        "scene_text": "private lounge with soft lamps",
        "expression": "shared confident gaze",
        "composition": "two-person editorial frame",
    }
    _expect_caption_route_parity(
        "caption_route_couple",
        couple,
        caption_metadata_routes.couple_from_row_result,
        caption_naturalizer._couple_from_row,
        "metadata(couple)",
    )

    configured = _fixture_hardcore_row()
    _expect_caption_route_parity(
        "caption_route_configured_cast",
        configured,
        caption_metadata_routes.configured_cast_from_row_result,
        caption_naturalizer._configured_cast_from_row,
        "metadata(configured_cast)",
    )
    configured_character_expression = _fixture_hardcore_row(
        character_expression_text="Woman A has flushed focus; Man A has concentrated stare",
    )
    character_expression_route = caption_metadata_routes.configured_cast_from_row_result(
        caption_naturalizer._caption_metadata_route_request(configured_character_expression, "balanced", False),
        caption_naturalizer._caption_metadata_route_dependencies(),
    )
    _expect(character_expression_route is not None, "Caption configured-cast character expression row did not match")
    assert character_expression_route is not None
    _expect(
        "with Woman A has" not in character_expression_route.prose,
        "Caption configured-cast prose kept old character-expression grammar",
    )
    _expect(
        "Woman A with flushed focus" in character_expression_route.prose,
        "Caption configured-cast prose did not naturalize Woman A expression",
    )
    _expect(
        "Man A with concentrated stare" in character_expression_route.prose,
        "Caption configured-cast prose did not naturalize Man A expression",
    )
    configured_axis_only = _fixture_hardcore_row(
        item="generic configured adult action",
        role_graph="",
        source_role_graph="",
        item_axis_values={
            "position": "standing oral position",
            "contact_detail": "mouth contact at hip height, hands on hips",
        },
        action_family="oral",
        position_family="oral",
        position_key="standing",
        position_keys=["standing"],
    )
    axis_route = caption_metadata_routes.configured_cast_from_row_result(
        caption_naturalizer._caption_metadata_route_request(configured_axis_only, "balanced", False),
        caption_naturalizer._caption_metadata_route_dependencies(),
    )
    _expect(axis_route is not None, "Caption configured-cast axis-only row did not match")
    assert axis_route is not None
    _expect("Selected action details include" in axis_route.prose, "Caption route did not emit selected axis details")
    _expect("standing oral position" in axis_route.prose, "Caption route lost item-axis position detail")
    _expect("mouth contact at hip height" in axis_route.prose, "Caption route lost item-axis contact detail")
    _expect("hands on hips" in axis_route.prose, "Caption route lost item-axis split detail")

    group = {
        "primary_subject": "group scene",
        "subject_phrase": "three adult friends",
        "age": "late 20s adults",
        "item": "coordinated evening outfits",
        "scene_text": "rooftop lounge with city lights",
        "expression": "relaxed shared smiles",
        "composition": "wide group frame",
    }
    _expect_caption_route_parity(
        "caption_route_group",
        group,
        caption_metadata_routes.group_or_layout_from_row_result,
        caption_naturalizer._group_or_layout_from_row,
        "metadata(group_layout)",
    )

    pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=3511,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(hardcore_clothing_continuity="partially_removed"),
        character_cast=_character_cast(),
        hardcore_position_config=_action_filter("penetration_only"),
    )
    _expect_pair(pair, "caption_route_pair")
    _expect_caption_route_parity(
        "caption_route_insta_pair",
        pair,
        caption_metadata_routes.insta_of_pair_from_row_result,
        caption_naturalizer._insta_of_pair_from_row,
        "metadata(insta_of_pair)",
    )
    deps = caption_naturalizer._caption_metadata_route_dependencies()
    soft_route = caption_metadata_routes.insta_of_pair_from_row_result(
        caption_naturalizer._caption_metadata_route_request(pair, "balanced", False, target="softcore"),
        deps,
    )
    hard_route = caption_metadata_routes.insta_of_pair_from_row_result(
        caption_naturalizer._caption_metadata_route_request(pair, "balanced", False, target="hardcore"),
        deps,
    )
    _expect(soft_route is not None, "Caption pair softcore target did not match")
    _expect(hard_route is not None, "Caption pair hardcore target did not match")
    assert soft_route is not None
    assert hard_route is not None
    _expect("Softcore side:" not in soft_route.prose, "Caption softcore target should not keep combined pair labels")
    _expect("Hardcore side:" not in soft_route.prose, "Caption softcore target should not include hard label")
    _expect("Softcore side:" not in hard_route.prose, "Caption hardcore target should not include soft label")
    _expect("Hardcore side:" not in hard_route.prose, "Caption hardcore target should not keep combined pair labels")
    _expect(soft_route.prose != hard_route.prose, "Caption pair soft/hard targets should produce distinct prose")
    shared_cast = pair.get("shared_cast_descriptors")
    if isinstance(shared_cast, list):
        shared_cast_text = "; ".join(str(item or "").strip() for item in shared_cast if str(item or "").strip())
    else:
        shared_cast_text = str(shared_cast or "").strip()
    shared_cast_caption = caption_naturalizer._natural_cast_descriptor_text(shared_cast_text)
    if shared_cast_caption:
        _expect(
            hard_route.prose.count(shared_cast_caption) <= 1,
            "Caption hardcore target repeated shared cast descriptors",
        )
    public_hard, public_hard_method = caption_naturalizer.naturalize_caption(
        "",
        metadata_json=_json(pair),
        input_hint="metadata_json",
        target="hardcore",
        trigger=Trigger,
        include_trigger=False,
    )
    _expect(public_hard == hard_route.prose, "Public caption hardcore target drifted from typed route")
    _expect("metadata(insta_of_pair)" in public_hard_method, "Public caption hardcore target lost pair method")


def smoke_sdxl_presets_policy() -> None:
    _expect(
        sdxl_formatter.SDXL_STYLE_PRESETS is sdxl_presets.SDXL_STYLE_PRESETS,
        "SDXL formatter style presets should delegate to sdxl_presets",
    )
    _expect(
        sdxl_formatter.SDXL_QUALITY_PRESETS is sdxl_presets.SDXL_QUALITY_PRESETS,
        "SDXL formatter quality presets should delegate to sdxl_presets",
    )
    _expect(
        sdxl_formatter.SDXL_FORMATTER_PROFILES is sdxl_presets.SDXL_FORMATTER_PROFILES,
        "SDXL formatter profiles should delegate to sdxl_presets",
    )
    _expect(
        sdxl_formatter.SDXL_ACTION_FAMILY_TAGS is sdxl_presets.SDXL_ACTION_FAMILY_TAGS,
        "SDXL formatter action-family tags should delegate to sdxl_presets",
    )
    _expect("sdxl_photo" in sdxl_presets.sdxl_formatter_profile_choices(), "SDXL profile choices lost sdxl_photo")
    _expect("flat_vector_pony" in sdxl_presets.sdxl_style_preset_choices(), "SDXL style preset choices lost default")
    _expect("pony_high" in sdxl_presets.sdxl_quality_preset_choices(), "SDXL quality preset choices lost default")
    _expect(
        sdxl_presets.normalize_formatter_profile("bad") == sdxl_presets.DEFAULT_FORMATTER_PROFILE,
        "SDXL invalid profile fallback changed",
    )
    _expect(sdxl_presets.normalize_formatter_profile("SDXL Photo") == "sdxl_photo", "SDXL profile should normalize spaces/case")
    _expect(sdxl_presets.normalize_style_preset("bad") == sdxl_presets.DEFAULT_STYLE_PRESET, "SDXL invalid style fallback changed")
    _expect(sdxl_presets.normalize_style_preset("flat-vector-pony") == "flat_vector_pony", "SDXL style should normalize hyphens")
    _expect(sdxl_presets.normalize_quality_preset("bad") == sdxl_presets.DEFAULT_QUALITY_PRESET, "SDXL invalid quality fallback changed")
    _expect(sdxl_presets.normalize_quality_preset("Pony High") == "pony_high", "SDXL quality should normalize spaces/case")
    _expect(
        sdxl_presets.apply_formatter_profile(
            "sdxl_photo",
            style_preset="flat_vector_pony",
            quality_preset="pony_high",
        )
        == ("photographic", "sdxl_high"),
        "SDXL photo profile overrides changed",
    )
    _expect(
        sdxl_presets.apply_formatter_profile(
            "manual_controls",
            style_preset="flat_vector",
            quality_preset="none",
        )
        == ("flat_vector", "none"),
        "SDXL manual profile should preserve explicit controls",
    )

    row = _fixture_hardcore_row(
        action_family="oral",
        position_family="oral",
        position_key="kneeling_oral",
        position_keys=["kneeling_oral"],
    )
    tags = sdxl_formatter._metadata_family_tags(row)
    _expect("oral sex" in tags, "SDXL metadata family tags lost oral family tag")
    _expect("kneeling oral" in tags, "SDXL metadata family tags lost position key tag")
    formatted = sdxl_formatter.format_sdxl_prompt(
        _json(row),
        input_hint="auto",
        style_preset="bad",
        quality_preset="bad",
        trigger=SdxlTrigger,
        prepend_trigger=True,
    )
    _expect_trigger_once("sdxl_presets.formatted_prompt", formatted.get("sdxl_prompt"), SdxlTrigger)
    _expect("Flat vector" in formatted.get("sdxl_prompt", ""), "SDXL invalid style did not fall back to default preset")
    _expect("score_9" in formatted.get("sdxl_prompt", ""), "SDXL invalid quality did not fall back to default preset")
    profiled = sdxl_formatter.format_sdxl_prompt(
        _json(row),
        input_hint="auto",
        formatter_profile="sdxl_photo",
        style_preset="flat_vector_pony",
        quality_preset="pony_high",
        trigger=SdxlTrigger,
        prepend_trigger=True,
    )
    profiled_prompt = profiled.get("sdxl_prompt", "")
    _expect("realistic photo" in profiled_prompt, "SDXL photo profile did not apply photographic style")
    _expect("score_9" not in profiled_prompt, "SDXL photo profile should switch away from Pony score quality tail")


def smoke_sdxl_format_route_policy() -> None:
    row = _prompt_row(
        name="sdxl_format_route_single",
        category="woman",
        subcategory="random",
        seed=3701,
        men_count=0,
        camera_config=_orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5),
    )
    single_request = sdxl_format_route.SDXLFormatRequest(
        source_text="",
        metadata_json=_json(row),
        target="single",
        style_preset="flat_vector_pony",
        quality_preset="pony_high",
        trigger=SdxlTrigger,
        prepend_trigger=True,
        nude_weight=9.0,
        extra_positive="sdxl route marker",
        extra_negative="sdxl route negative",
        formatter_profile="sdxl_photo",
    )
    typed_single = sdxl_format_route.format_sdxl_prompt_result(
        single_request,
        sdxl_formatter._sdxl_format_dependencies(),
    )
    public_single = sdxl_formatter.format_sdxl_prompt(
        "",
        metadata_json=single_request.metadata_json,
        target=single_request.target,
        style_preset=single_request.style_preset,
        quality_preset=single_request.quality_preset,
        trigger=single_request.trigger,
        prepend_trigger=single_request.prepend_trigger,
        nude_weight=single_request.nude_weight,
        extra_positive=single_request.extra_positive,
        extra_negative=single_request.extra_negative,
        formatter_profile=single_request.formatter_profile,
    )
    _expect(typed_single.output == public_single, "Typed SDXL format route should match public single formatter output")
    _expect(typed_single.branch == "metadata", "Typed SDXL format route changed single branch")
    _expect(typed_single.target == "single", "Typed SDXL format route lost target normalization")
    _expect(typed_single.nude_weight == 3.0, "Typed SDXL format route should clamp high nude weight")
    _expect(typed_single.style_preset == "photographic", "Typed SDXL format route lost profile style override")
    single_trace = json.loads(_expect_text("sdxl_format_route_policy.single_trace", typed_single.output.get("route_trace_json"), 20))
    _expect(single_trace.get("formatter") == "sdxl", "Typed SDXL single trace lost formatter")
    _expect(single_trace.get("branch") == "metadata", "Typed SDXL single trace lost branch")
    _expect(single_trace.get("target") == "single", "Typed SDXL single trace lost target")
    _expect(single_trace.get("nude_weight") == 3.0, "Typed SDXL single trace lost clamped nude weight")
    _expect("sdxl route marker" in typed_single.output.get("sdxl_prompt", ""), "Typed SDXL route lost extra positive")
    _expect("sdxl route negative" in typed_single.output.get("negative_prompt", ""), "Typed SDXL route lost extra negative")

    pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=3702,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(),
        character_cast=_character_cast(),
        hardcore_position_config=_action_filter("penetration_only"),
    )
    pair_request = sdxl_format_route.SDXLFormatRequest(
        source_text="",
        metadata_json=_json(pair),
        target="hardcore",
        trigger=SdxlTrigger,
        prepend_trigger=True,
        extra_positive="pair sdxl route marker",
        extra_negative="pair sdxl route negative",
    )
    typed_pair = sdxl_format_route.format_sdxl_prompt_result(
        pair_request,
        sdxl_formatter._sdxl_format_dependencies(),
    )
    public_pair = sdxl_formatter.format_sdxl_prompt(
        "",
        metadata_json=pair_request.metadata_json,
        target=pair_request.target,
        trigger=pair_request.trigger,
        prepend_trigger=pair_request.prepend_trigger,
        extra_positive=pair_request.extra_positive,
        extra_negative=pair_request.extra_negative,
    )
    _expect(typed_pair.output == public_pair, "Typed SDXL format route should match public pair formatter output")
    _expect(typed_pair.branch == "insta_of_pair", "Typed SDXL format route changed pair branch")
    pair_trace = json.loads(_expect_text("sdxl_format_route_policy.pair_trace", typed_pair.output.get("route_trace_json"), 20))
    _expect(pair_trace.get("branch") == "insta_of_pair", "Typed SDXL pair trace lost branch")
    _expect(pair_trace.get("selected_side") == "hardcore", "Typed SDXL pair trace lost selected side")
    _expect_text("sdxl_format_route_policy.hard_prompt", typed_pair.output.get("sdxl_hardcore_prompt"), 40)
    _expect("pair sdxl route marker" in typed_pair.output.get("sdxl_prompt", ""), "Typed SDXL pair route lost extra positive")

    fallback_request = sdxl_format_route.SDXLFormatRequest(
        source_text="Characters: woman. Erotic outfit: sheer dress. Camera: side view. Avoid: blur",
        input_hint="prompt",
        target="weird",
        trigger=SdxlTrigger,
        prepend_trigger=False,
        nude_weight=0.01,
        style_preset="none",
        quality_preset="none",
    )
    typed_fallback = sdxl_format_route.format_sdxl_prompt_result(
        fallback_request,
        sdxl_formatter._sdxl_format_dependencies(),
    )
    public_fallback = sdxl_formatter.format_sdxl_prompt(
        fallback_request.source_text,
        input_hint=fallback_request.input_hint,
        target=fallback_request.target,
        trigger=fallback_request.trigger,
        prepend_trigger=fallback_request.prepend_trigger,
        nude_weight=fallback_request.nude_weight,
        style_preset=fallback_request.style_preset,
        quality_preset=fallback_request.quality_preset,
    )
    _expect(typed_fallback.output == public_fallback, "Typed SDXL format route should match public fallback output")
    _expect(typed_fallback.branch == "fallback", "Typed SDXL format route changed fallback branch")
    _expect(typed_fallback.target == "auto", "Typed SDXL format route should normalize invalid target")
    _expect(typed_fallback.nude_weight == 0.1, "Typed SDXL format route should clamp low nude weight")
    fallback_trace = json.loads(_expect_text("sdxl_format_route_policy.fallback_trace", typed_fallback.output.get("route_trace_json"), 20))
    _expect(fallback_trace.get("branch") == "fallback", "Typed SDXL fallback trace lost branch")
    _expect(fallback_trace.get("input_hint") == "prompt", "Typed SDXL fallback trace lost input hint")
    _expect("Characters:" not in typed_fallback.output.get("sdxl_prompt", ""), "Typed SDXL fallback leaked Characters label")
    _expect("blur" in typed_fallback.output.get("negative_prompt", ""), "Typed SDXL fallback route lost Avoid negative")


def smoke_sdxl_tag_policy() -> None:
    row = _fixture_hardcore_row(
        action_family="oral",
        position_family="oral",
        position_key="kneeling_oral",
        position_keys=["kneeling_oral"],
        formatter_hints={"sdxl": ["policy route tag"]},
    )
    _expect(
        sdxl_formatter._split_tag_text("Woman A with camera, Man A")
        == sdxl_tag_policy.split_tag_text("Woman A with camera, Man A"),
        "SDXL formatter split helper should delegate to sdxl_tag_policy",
    )
    _expect(
        sdxl_formatter._metadata_family_tags(row) == sdxl_tag_policy.metadata_family_tags(row),
        "SDXL formatter metadata-family helper should delegate to sdxl_tag_policy",
    )
    axis_row = {
        "item_axis_values": {
            "position": "edge-supported kneeling pose",
            "contact_detail": "hands braced on thighs, close body alignment",
            "ignored": "random",
        }
    }
    axis_tags = sdxl_tag_policy.axis_value_tags(axis_row)
    _expect("edge-supported kneeling pose" in axis_tags, "SDXL axis tags lost selected position axis")
    _expect("hands braced on thighs" in axis_tags, "SDXL axis tags lost selected detail axis")
    _expect("close body alignment" in axis_tags, "SDXL axis tags lost split detail axis")
    _expect("random" not in axis_tags, "SDXL axis tags should ignore random placeholders")
    hyphenated_tags = sdxl_tag_policy.split_tag_text("front-and-back penetration with hands on hips")
    _expect("front-and-back penetration" in hyphenated_tags, "SDXL tag splitter broke hyphenated and compound")
    _expect("front-" not in hyphenated_tags and "-back penetration" not in hyphenated_tags, "SDXL tag splitter emitted broken hyphen fragments")
    _expect("hands on hips" in hyphenated_tags, "SDXL tag splitter stopped splitting non-hyphenated with connector")
    subject_pair_tags = sdxl_tag_policy.split_tag_text("Woman A, Man A are mid-transition with hands on hips")
    _expect("woman and man are mid-transition" in subject_pair_tags, "SDXL tag splitter broke paired character clause")
    _expect("woman" not in subject_pair_tags and "man are mid-transition" not in subject_pair_tags, "SDXL tag splitter emitted broken paired character fragments")
    sentence_boundary_tags = sdxl_tag_policy.split_tag_text("keep hands on hips, breasts, thighs. Man watches close")
    sentence_boundary_tags_lower = [tag.lower() for tag in sentence_boundary_tags]
    _expect("hands on hips" in sentence_boundary_tags_lower, "SDXL tag splitter did not clean leading keep imperative")
    _expect("keep hands on hips" not in sentence_boundary_tags_lower, "SDXL tag splitter kept leading keep imperative")
    _expect("thighs" in sentence_boundary_tags, "SDXL tag splitter lost pre-period tag")
    _expect("man watches close" in sentence_boundary_tags_lower, "SDXL tag splitter did not split sentence-boundary tag")
    _expect(
        "thighs. man watches close" not in sentence_boundary_tags_lower,
        "SDXL tag splitter kept sentence-boundary tag fragment",
    )
    _expect(
        sdxl_formatter._camera_tags(row) == sdxl_tag_policy.camera_tags(row),
        "SDXL formatter camera helper should delegate to sdxl_tag_policy",
    )
    _expect(
        sdxl_formatter._combine_tags("a, b", "a", "c")
        == sdxl_tag_policy.combine_tags("a, b", "a", "c")
        == "a, b, c",
        "SDXL tag combining changed",
    )
    deps = sdxl_formatter._sdxl_tag_route_dependencies()
    _expect(deps.tag_key is sdxl_tag_policy.tag_key, "SDXL route deps lost policy tag_key")
    _expect(deps.normal_character_tags is sdxl_tag_policy.normal_character_tags, "SDXL route deps lost character tag policy")
    _expect(deps.metadata_family_tags is sdxl_tag_policy.metadata_family_tags, "SDXL route deps lost metadata family policy")
    _expect(deps.axis_value_tags is sdxl_tag_policy.axis_value_tags, "SDXL route deps lost axis-value tag policy")
    _expect(deps.camera_tags is sdxl_tag_policy.camera_tags, "SDXL route deps lost camera tag policy")
    _expect(deps.explicit_tags is sdxl_tag_policy.explicit_tags, "SDXL route deps lost explicit tag policy")
    _expect(
        deps.filter_incompatible_route_tags is sdxl_tag_policy.filter_incompatible_route_tags,
        "SDXL route deps lost route-family tag filter",
    )
    _expect(deps.softcore_pair_tags is sdxl_tag_policy.softcore_pair_tags, "SDXL route deps lost softcore pair tag policy")
    mouth_nearby_tags = sdxl_tag_policy.explicit_tags(
        "missionary penetration with mouth close to the ear",
        1.29,
    )
    _expect("penetration" in mouth_nearby_tags, "SDXL explicit tags lost penetration signal")
    _expect("oral sex" not in mouth_nearby_tags, "SDXL explicit tags should not treat nearby mouth wording as oral")
    outercourse_filtered_tags = sdxl_tag_policy.filter_incompatible_route_tags(
        ["outercourse", "penis licking", "oral sex", "penetration"],
        _fixture_hardcore_row(
            action_family="outercourse",
            position_family="outercourse",
            position_key="penis_licking",
            position_keys=["penis_licking"],
        ),
    )
    _expect("outercourse" in outercourse_filtered_tags, "SDXL route filter removed matching outercourse tag")
    _expect("penis licking" in outercourse_filtered_tags, "SDXL route filter removed specific outercourse key")
    _expect("oral sex" not in outercourse_filtered_tags, "SDXL route filter kept incompatible oral tag")
    _expect("penetration" not in outercourse_filtered_tags, "SDXL route filter kept incompatible penetration tag")

    stale_character_row = {
        "prompt": "Characters: 99-year-old adult man, stale body, stale skin, stale hair, stale eyes.",
        "age_band": "27-year-old adult",
        "subject_phrase": "woman",
        "body_phrase": "athletic figure",
        "skin": "warm olive skin",
        "hair": "short black hair",
        "eyes": "green eyes",
    }
    stale_character_tags = sdxl_tag_policy.normal_character_tags(stale_character_row)
    _expect("27-year-old adult" in stale_character_tags, "SDXL character tags lost structured age")
    _expect("warm olive skin" in stale_character_tags, "SDXL character tags lost structured skin")
    _expect(
        all("stale" not in tag for tag in stale_character_tags),
        "SDXL character tags should not parse stale raw prompt character labels",
    )
    descriptor_tags = sdxl_tag_policy.normal_character_tags(
        {
            "prompt": "Characters: stale prompt descriptor.",
            "cast_descriptor_text": "Woman A: 30-year-old adult woman, toned figure, fair skin, red hair, gray eyes",
        }
    )
    _expect("30-year-old adult woman" in descriptor_tags, "SDXL character tags lost cast descriptor metadata")
    _expect("toned build" in descriptor_tags, "SDXL character tags did not normalize descriptor figure tag")
    _expect(all("stale" not in tag for tag in descriptor_tags), "SDXL cast descriptor metadata should beat stale prompt labels")


def smoke_sdxl_tag_routes() -> None:
    row = _fixture_hardcore_row(
        formatter_hints={
            "all": ["shared route anchor"],
            "sdxl": ["sdxl route tag"],
        }
    )
    deps = sdxl_formatter._sdxl_tag_route_dependencies()
    typed_row = sdxl_tag_routes.row_core_tags_result(
        sdxl_tag_routes.SDXLRowTagRequest(row, 1.29),
        deps,
    )
    _expect(
        typed_row.tags == sdxl_formatter._row_core_tags(row, 1.29),
        "Typed SDXL row tag route should match legacy wrapper output",
    )
    _expect("sdxl route tag" in typed_row.as_text(), "Typed SDXL row tag route lost route-specific formatter hint")
    axis_only_row = _fixture_hardcore_row(
        item="generic configured adult action",
        pose="configured explicit pose",
        role_graph="",
        source_role_graph="",
        item_axis_values={
            "position": "standing oral position",
            "contact_detail": "mouth contact at hip height, hands on hips",
        },
        action_family="oral",
        position_family="oral",
        position_key="standing",
        position_keys=["standing"],
    )
    axis_only_tags = sdxl_tag_routes.row_core_tags_result(
        sdxl_tag_routes.SDXLRowTagRequest(axis_only_row, 1.29),
        deps,
    ).as_text()
    _expect("standing oral position" in axis_only_tags, "SDXL row route lost item axis position tag")
    _expect("mouth contact at hip height" in axis_only_tags, "SDXL row route lost item axis contact tag")
    _expect("hands on hips" in axis_only_tags, "SDXL row route lost split item axis detail tag")
    stale_character_route_row = _fixture_hardcore_row(
        prompt="Characters: stale prompt subject, stale body, stale skin, stale hair, stale eyes.",
        cast_descriptor_text="",
        cast_descriptors=[],
        subject_type="woman",
        subject_phrase="woman",
        primary_subject="woman",
        age_band="27-year-old adult",
        body_phrase="athletic figure",
        skin="warm olive skin",
        hair="short black hair",
        eyes="green eyes",
        women_count=1,
        men_count=0,
        person_count=1,
    )
    stale_character_route_tags = sdxl_tag_routes.row_core_tags_result(
        sdxl_tag_routes.SDXLRowTagRequest(stale_character_route_row, 1.29),
        deps,
    ).as_text()
    _expect("27-year-old adult" in stale_character_route_tags, "SDXL route lost structured character age")
    _expect("warm olive skin" in stale_character_route_tags, "SDXL route lost structured character skin")
    _expect("stale" not in stale_character_route_tags, "SDXL route should not parse stale prompt character labels")
    composition_label_row = _fixture_hardcore_row(
        composition="coworking lounge frame with tall-window depth behind them composition",
    )
    composition_label_tags = sdxl_tag_routes.row_core_tags_result(
        sdxl_tag_routes.SDXLRowTagRequest(composition_label_row, 1.29),
        deps,
    ).as_text()
    _expect("tall-window depth behind them composition" not in composition_label_tags, "SDXL route kept raw composition label tag")
    _expect("tall-window depth behind them" in composition_label_tags, "SDXL route lost composition detail while removing label")
    expression_label_row = _fixture_hardcore_row(
        character_expression_text="Woman A has focused gaze; Man A has steady expression",
        expression="stale expression",
    )
    expression_label_tags = sdxl_tag_routes.row_core_tags_result(
        sdxl_tag_routes.SDXLRowTagRequest(expression_label_row, 1.29),
        deps,
    ).as_text()
    _expect("woman has" not in expression_label_tags.lower(), "SDXL route kept woman-has expression label")
    _expect("man has" not in expression_label_tags.lower(), "SDXL route kept man-has expression label")
    _expect("focused gaze" in expression_label_tags, "SDXL route lost cleaned woman expression")
    _expect("steady expression" in expression_label_tags, "SDXL route lost cleaned man expression")
    stale_prompt_row = _fixture_hardcore_row(
        prompt="stale raw prompt mentions fully nude naked pussy penis oral anal semen penetration",
        item="standing portrait setup",
        pose="standing pose",
        role_graph="",
        source_role_graph="",
        expression="neutral expression",
        action_family="",
        position_family="",
        position_key="",
        position_keys=[],
    )
    stale_tags = sdxl_tag_routes.row_core_tags_result(
        sdxl_tag_routes.SDXLRowTagRequest(stale_prompt_row, 1.29),
        deps,
    ).as_text()
    for forbidden in ("(naked:", "pussy", "penis", "oral sex", "anal sex", "semen", "penetration"):
        _expect(forbidden not in stale_tags, f"SDXL row tags should not infer explicit tag from stale prompt: {forbidden}")

    metadata_explicit_row = _fixture_hardcore_row(
        prompt="stale raw prompt without explicit tag anchors",
        item="kneeling pose with visible penis and pussy contact",
        pose="penetration pose",
        role_graph="penis thrusts into pussy",
        source_role_graph="penis thrusts into pussy",
        hardcore_clothing_state="fully nude body, bare skin unobstructed",
        action_family="penetration",
        position_family="penetrative",
        position_key="kneeling",
        position_keys=["kneeling"],
    )
    metadata_tags = sdxl_tag_routes.row_core_tags_result(
        sdxl_tag_routes.SDXLRowTagRequest(metadata_explicit_row, 1.29),
        deps,
    ).as_text()
    for required in ("(naked:1.29)", "pussy", "penis", "penetration"):
        _expect(required in metadata_tags, f"SDXL row tags lost structured explicit metadata tag: {required}")
    outercourse_noise_row = _fixture_hardcore_row(
        item="penis-licking outercourse position with tongue along the penis shaft",
        pose="configured outercourse pose",
        role_graph="Woman A bends low while her tongue runs along Man A's penis shaft.",
        source_role_graph="Woman A bends low while her tongue runs along Man A's penis shaft.",
        item_axis_values={
            "position": "penis-licking outercourse position",
            "outer_act": "tongue along the penis shaft",
        },
        action_family="outercourse",
        position_family="outercourse",
        position_key="penis_licking",
        position_keys=["penis_licking"],
    )
    outercourse_noise_tags = sdxl_tag_routes.row_core_tags_result(
        sdxl_tag_routes.SDXLRowTagRequest(outercourse_noise_row, 1.29),
        deps,
    ).as_text()
    _expect("outercourse" in outercourse_noise_tags, "SDXL outercourse row lost matching family tag")
    _expect("penis licking" in outercourse_noise_tags, "SDXL outercourse row lost specific position key")
    _expect("oral sex" not in outercourse_noise_tags, "SDXL outercourse row kept incompatible oral tag")
    _expect("penetration" not in outercourse_noise_tags, "SDXL outercourse row kept incompatible penetration tag")
    stale_hardcore_pose_row = _fixture_hardcore_row(
        item="oral contact with mouth on the visible genitals in side-lying oral position",
        pose="kneeling and balancing a cucumber upright on an open palm held overhead",
        role_graph="Woman A lies on her side while Man A's mouth is pressed to her pussy.",
        source_role_graph="Woman A lies on her side while Man A's mouth is pressed to her pussy.",
        item_axis_values={
            "position": "side-lying oral position",
            "oral_act": "oral contact with mouth on the visible genitals",
        },
        action_family="oral",
        position_family="oral",
        position_key="side_lying",
        position_keys=["side_lying"],
    )
    stale_hardcore_pose_tags = sdxl_tag_routes.row_core_tags_result(
        sdxl_tag_routes.SDXLRowTagRequest(stale_hardcore_pose_row, 1.29),
        deps,
    ).as_text()
    _expect("oral sex" in stale_hardcore_pose_tags, "SDXL hardcore route lost oral family tag")
    _expect("side lying" in stale_hardcore_pose_tags, "SDXL hardcore route lost structured position key")
    _expect("cucumber" not in stale_hardcore_pose_tags, "SDXL hardcore route leaked generic stale pose text")

    pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=3511,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(hardcore_clothing_continuity="partially_removed"),
        character_cast=_character_cast(),
        hardcore_position_config=_action_filter("penetration_only"),
    )
    _expect_pair(pair, "sdxl_tag_routes_pair")
    soft_row = pair.get("softcore_row") if isinstance(pair.get("softcore_row"), dict) else {}
    hard_row = pair.get("hardcore_row") if isinstance(pair.get("hardcore_row"), dict) else {}
    typed_soft = sdxl_tag_routes.soft_tags_result(
        sdxl_tag_routes.SDXLPairTagRequest(soft_row, pair, 1.29),
        deps,
    )
    typed_hard = sdxl_tag_routes.hard_tags_result(
        sdxl_tag_routes.SDXLPairTagRequest(hard_row, pair, 1.29),
        deps,
    )
    _expect(
        typed_soft.as_text() == sdxl_formatter._soft_tags(soft_row, pair, 1.29),
        "Typed SDXL pair soft tag route should match legacy wrapper output",
    )
    _expect(
        typed_hard.as_text() == sdxl_formatter._hard_tags(hard_row, pair, 1.29),
        "Typed SDXL pair hard tag route should match legacy wrapper output",
    )
    pair_stale_hard_row = _fixture_hardcore_row(
        prompt="",
        item="standing portrait setup",
        pose="standing pose",
        role_graph="",
        source_role_graph="",
        expression="neutral expression",
        composition="standing portrait frame",
        action_family="",
        position_family="",
        position_key="",
        position_keys=[],
    )
    pair_stale_root = {
        "hardcore_prompt": "stale pair prompt says fully nude naked pussy penis oral anal semen penetration",
        "hardcore_women_count": 1,
        "hardcore_men_count": 1,
    }
    pair_stale_tags = sdxl_tag_routes.hard_tags_result(
        sdxl_tag_routes.SDXLPairTagRequest(pair_stale_hard_row, pair_stale_root, 1.29),
        deps,
    ).as_text()
    for forbidden in ("(naked:", "pussy", "penis", "oral sex", "anal sex", "semen", "penetration"):
        _expect(forbidden not in pair_stale_tags, f"SDXL pair tags should not infer explicit tag from stale prompt: {forbidden}")

    pair_metadata_root = {
        "hardcore_prompt": "stale pair prompt without explicit tag anchors",
        "hardcore_women_count": 1,
        "hardcore_men_count": 1,
        "hardcore_clothing_state": "fully nude body, bare skin unobstructed",
    }
    pair_metadata_hard_row = _fixture_hardcore_row(
        prompt="",
        item="kneeling pose with visible penis and pussy contact",
        role_graph="penis thrusts into pussy",
        source_role_graph="penis thrusts into pussy",
        action_family="penetration",
        position_family="penetrative",
        position_key="kneeling",
        position_keys=["kneeling"],
    )
    pair_metadata_tags = sdxl_tag_routes.hard_tags_result(
        sdxl_tag_routes.SDXLPairTagRequest(pair_metadata_hard_row, pair_metadata_root, 1.29),
        deps,
    ).as_text()
    for required in ("(naked:1.29)", "pussy", "penis", "penetration"):
        _expect(required in pair_metadata_tags, f"SDXL pair tags lost structured explicit metadata tag: {required}")
    pair_axis_tags = sdxl_tag_routes.hard_tags_result(
        sdxl_tag_routes.SDXLPairTagRequest(axis_only_row, pair_metadata_root, 1.29),
        deps,
    ).as_text()
    _expect("standing oral position" in pair_axis_tags, "SDXL pair hard route lost item axis position tag")
    _expect("mouth contact at hip height" in pair_axis_tags, "SDXL pair hard route lost item axis contact tag")
    formatted = sdxl_formatter.format_sdxl_prompt(
        "",
        metadata_json=_json(pair),
        target="hardcore",
        trigger=SdxlTrigger,
        prepend_trigger=True,
    )
    _expect("sdxl(insta_of_pair)" in formatted.get("method", ""), "SDXL pair formatter route changed method")


def smoke_hardcore_position_config_policy() -> None:
    _expect(
        pb.HARDCORE_POSITION_FAMILY_CHOICES is hardcore_position_config.HARDCORE_POSITION_FAMILY_CHOICES,
        "Prompt builder hardcore position family choices are not delegated",
    )
    _expect("outercourse_only" in hardcore_position_config.hardcore_position_focus_choices(), "Hardcore focus choices lost outercourse_only")
    _expect("boobjob" in hardcore_position_config.hardcore_position_key_choices(), "Hardcore position keys lost boobjob")
    _expect(
        category_template_metadata.template_action_family({"action_family": "toy double"}) == "toy_double",
        "Template action-family normalizer should accept spaced aliases",
    )
    _expect(
        category_template_metadata.template_action_family({"action_family": "manual stimulation"}) == "manual",
        "Template action-family normalizer should accept subcategory-style aliases",
    )
    _expect(
        category_template_metadata.template_action_family({"action_family": "anal sex"}) == "anal",
        "Template action-family normalizer should accept anal aliases",
    )
    _expect(
        category_template_metadata.template_action_family({"action_family": "three way"}) == "threesome",
        "Template action-family normalizer should accept threesome aliases",
    )
    _expect(
        category_template_metadata.template_action_family({"action_family": "group sex"}) == "group",
        "Template action-family normalizer should accept group aliases",
    )
    _expect(
        category_template_metadata.template_position_family({"position_family": "penetration"}) == "penetrative",
        "Template position-family normalizer should accept action-style aliases",
    )
    _expect(
        category_template_metadata.template_position_family({"position_family": "outer-course"}) == "outercourse",
        "Template position-family normalizer should accept hyphenated aliases",
    )

    base = json.loads(
        pb.build_hardcore_position_pool_json(
            combine_mode="replace",
            family="oral",
            selected_positions=["standing", "bad value", "standing"],
        )
    )
    _expect(base.get("enabled") is True, "Hardcore position pool should enable config")
    _expect(base.get("family") == "oral", "Hardcore position pool lost family")
    _expect(base.get("positions") == ["standing"], "Hardcore position normalization changed")
    _expect(base.get("require_position") is True, "Hardcore position pool should require selected position")

    added = json.loads(
        hardcore_position_config.build_hardcore_position_pool_json(
            hardcore_position_config=base,
            combine_mode="add",
            family="any",
            selected_positions=["kneeling", "standing"],
        )
    )
    _expect(added.get("positions") == ["standing", "kneeling"], "Hardcore position add merge changed")

    filtered = json.loads(
        pb.build_hardcore_action_filter_json(
            hardcore_position_config=added,
            focus="outercourse_only",
            allow_toys=False,
            allow_double=False,
            allow_penetration=True,
            allow_foreplay=True,
            allow_interaction=True,
            allow_manual=True,
            allow_oral=True,
            allow_outercourse=True,
            allow_anal=True,
            allow_climax=True,
        )
    )
    _expect(filtered.get("family") == "outercourse", "Hardcore action focus did not set outercourse family")
    _expect(filtered.get("allow_oral") is False, "Hardcore outercourse focus should disable oral")
    _expect(filtered.get("allow_penetration") is False, "Hardcore outercourse focus should disable penetration")
    _expect("outercourse_sex" in hardcore_position_config.hardcore_allowed_subcategory_slugs(filtered), "Allowed subcategories lost outercourse")
    _expect("oral_sex" not in hardcore_position_config.hardcore_allowed_subcategory_slugs(filtered), "Allowed subcategories should exclude oral")
    strict_threesome = json.loads(
        pb.build_hardcore_action_filter_json(
            hardcore_position_config=pb.build_hardcore_position_pool_json(family="threesome"),
            focus="threesome_only",
            allow_toys=False,
            allow_double=False,
            allow_penetration=False,
            allow_foreplay=False,
            allow_interaction=False,
            allow_manual=False,
            allow_oral=False,
            allow_outercourse=False,
            allow_anal=False,
            allow_climax=False,
        )
    )
    _expect(
        hardcore_position_config.hardcore_allowed_subcategory_slugs(strict_threesome) == {"threesomes"},
        "Specific hardcore family filter should not widen to the full pool when boolean filters empty it",
    )
    action_only = json.loads(
        hardcore_position_config.build_hardcore_action_filter_json(
            focus="outercourse_only",
            allow_toys=False,
            allow_double=False,
            allow_penetration=True,
            allow_foreplay=True,
            allow_interaction=True,
            allow_manual=True,
            allow_oral=True,
            allow_outercourse=True,
            allow_anal=True,
            allow_climax=True,
        )
    )
    action_axis = hardcore_position_config.filter_hardcore_axis(
        "outer_act",
        ["boobjob body contact", "blowjob oral sex", "vaginal penetration"],
        action_only,
    )
    _expect(action_axis == ["boobjob body contact"], "Hardcore action filter policy did not block disabled oral/penetration text")
    action_axis_metadata = hardcore_position_config.filter_hardcore_axis(
        "outer_act",
        [
            {"text": "generic contact route", "action_family": "outercourse", "position_family": "outercourse"},
            {"text": "generic contact route", "action_family": "oral", "position_family": "oral"},
            {"text": "generic contact route", "action_family": "penetration", "position_family": "penetrative"},
        ],
        action_only,
    )
    _expect(
        action_axis_metadata == [{"text": "generic contact route", "action_family": "outercourse", "position_family": "outercourse"}],
        "Hardcore action filter policy did not honor structured action metadata",
    )
    position_filtered = hardcore_position_config.apply_hardcore_position_config_to_subcategory(
        {
            "slug": "oral_sex",
            "item_templates": [
                {"template": "oral contact in {position}"},
                {"template": "metadata-specific oral contact", "position_key": "standing", "action_family": "oral"},
                {"template": "oral sex without a position axis"},
                {"template": "unsupported static template"},
            ],
            "item_axes": {
                "position": [
                    "standing oral position",
                    "kneeling oral position",
                    {"text": "generic standing pose", "position_key": "standing"},
                    {"text": "generic kneeling pose", "position_key": "kneeling"},
                ],
                "oral_act": ["blowjob", "cunnilingus"],
            },
        },
        base,
    )
    _expect(
        position_filtered["item_templates"]
        == [
            {"template": "oral contact in {position}"},
            {"template": "metadata-specific oral contact", "position_key": "standing", "action_family": "oral"},
        ],
        "Hardcore position policy did not filter templates by selected position requirements or metadata",
    )
    _expect(
        position_filtered["item_axes"]["position"] == ["standing oral position", {"text": "generic standing pose", "position_key": "standing"}],
        "Hardcore position policy did not filter position axes by selected keys or metadata",
    )
    filtered_categories = hardcore_position_config.filter_hardcore_categories_for_position(
        [
            {
                "name": "Hardcore sexual poses",
                "slug": "hardcore_sexual_poses",
                "subcategories": [{"slug": "oral_sex"}, {"slug": "outercourse_sex"}],
            },
            {"name": "Casual clothes", "slug": "casual_clothes", "subcategories": [{"slug": "tops"}]},
        ],
        filtered,
        1,
        1,
        lambda _entry, _women, _men: True,
    )
    _expect(
        [entry["slug"] for entry in filtered_categories[0]["subcategories"]] == ["outercourse_sex"],
        "Hardcore category filter policy did not remove disallowed subcategories",
    )
    _expect(filtered_categories[1]["slug"] == "casual_clothes", "Hardcore category filter should preserve non-hardcore categories")

    keys = pb._hardcore_position_keys("woman on all fours from behind", axis_values={"position": "doggy"})
    _expect(keys == ["doggy"], "Hardcore position key detection changed")
    source_family = hardcore_position_config.hardcore_source_position_family({"slug": "manual_stimulation"}, filtered)
    _expect(source_family == "manual", "Hardcore source family lookup changed")
    source_action_family = hardcore_action_metadata.source_hardcore_action_family(
        "outer-course",
        "",
        "generic contact",
    )
    _expect(source_action_family == "outercourse", "Source action-family fallback should accept hyphenated source aliases")
    _expect(
        hardcore_action_metadata.source_hardcore_action_family("threesome", "", "three-body contact") == "threesome",
        "Source action-family fallback should accept threesome source family",
    )
    _expect(
        hardcore_action_metadata.source_hardcore_action_family("group", "", "group sex contact") == "group",
        "Source action-family fallback should accept group source family",
    )
    default_action_route = row_route_metadata.resolve_action_position_route(
        is_pose_category=True,
        subcategory={"slug": "anal_double_penetration"},
        hardcore_position_config=None,
        item_template_metadata={"action_family": "default", "position_family": "anal"},
        item_text="toy-assisted double penetration with front-and-back contact",
        source_role_graph="one partner between two bodies",
        source_composition="",
        pose="",
        item_axis_values={"double_act": "toy-assisted double penetration"},
    )
    _expect(default_action_route.get("position_family") == "anal", "Default-action metadata should preserve position family")
    _expect(default_action_route.get("action_family") == "toy_double", "Default-action metadata should still allow action inference")
    item_text, item_name, axis_values, template_metadata = pb._compose_item(
        random.Random(42),
        {},
        {
            "name": "Template metadata route",
            "item_templates": [
                {
                    "template": "{act} in {position}",
                    "action_family": "oral",
                    "position_family": "oral",
                    "position_keys": ["kneeling", "open_thighs"],
                    "formatter_hint": {
                        "krea2": "keep mouth contact readable",
                        "sdxl": ["oral contact", "kneeling oral"],
                        "training_caption": "oral contact caption detail",
                    },
                }
            ],
            "item_axes": {
                "act": ["mouth contact"],
                "position": ["kneeling oral position"],
            },
        },
        "Template metadata route",
        women_count=1,
        men_count=1,
    )
    _expect(item_text == "mouth contact in kneeling oral position", "Template metadata route changed composed item text")
    _expect(item_name == "Template metadata route", "Template metadata route changed item name")
    _expect(axis_values == {"act": "mouth contact", "position": "kneeling oral position"}, "Template metadata route lost axis values")
    _expect(template_metadata.get("action_family") == "oral", "Template metadata route lost action family")
    _expect(pb._template_position_family(template_metadata) == "oral", "Template metadata route lost position family")
    _expect(pb._template_position_keys(template_metadata) == ["kneeling", "open_thighs"], "Template metadata route lost position keys")
    _expect(pb._template_action_family(template_metadata) == "oral", "Template metadata route lost normalized action family")
    formatter_hints = pb._template_formatter_hints(template_metadata)
    _expect(formatter_hints.get("krea") == ["keep mouth contact readable"], "Template metadata route lost Krea formatter hint")
    _expect(formatter_hints.get("sdxl") == ["oral contact", "kneeling oral"], "Template metadata route lost SDXL formatter hints")
    _expect(formatter_hints.get("caption") == ["oral contact caption detail"], "Template metadata route lost caption formatter hint")
    inherited_text, _inherited_name, inherited_axis_values, inherited_metadata = pb._compose_item(
        random.Random(42),
        {},
        {
            "name": "Inherited metadata route",
            "item_template_metadata": {
                "action_family": "manual",
                "position_family": "manual",
                "position_keys": ["kneeling"],
                "formatter_hint": {"caption": "inherited caption cue"},
            },
            "item_templates": ["{act} in {position}"],
            "item_axes": {
                "act": ["hand stimulation"],
                "position": ["kneeling manual position"],
            },
        },
        "Inherited metadata route",
        women_count=1,
        men_count=1,
    )
    _expect(inherited_text == "hand stimulation in kneeling manual position", "Inherited template metadata changed item text")
    _expect(inherited_axis_values == {"act": "hand stimulation", "position": "kneeling manual position"}, "Inherited template metadata lost axis values")
    _expect(inherited_metadata.get("action_family") == "manual", "String template did not inherit action family")
    _expect(inherited_metadata.get("position_family") == "manual", "String template did not inherit position family")
    _expect(pb._template_position_keys(inherited_metadata) == ["kneeling"], "String template did not inherit position keys")
    _expect(
        route_metadata.row_formatter_hints({"item_template_metadata": inherited_metadata}, "caption") == ["inherited caption cue"],
        "String template did not inherit formatter hints",
    )
    override_text, _override_name, _override_axis_values, override_metadata = pb._compose_item(
        random.Random(42),
        {},
        {
            "name": "Override metadata route",
            "item_template_metadata": {
                "action_family": "manual",
                "position_family": "manual",
                "formatter_hint": {"all": "inherited shared cue"},
            },
            "item_templates": [
                {
                    "template": "{act} in {position}",
                    "action_family": "oral",
                    "formatter_hint": {"krea2": "override krea cue"},
                }
            ],
            "item_axes": {
                "act": ["mouth contact"],
                "position": ["kneeling oral position"],
            },
        },
        "Override metadata route",
        women_count=1,
        men_count=1,
    )
    _expect(override_text == "mouth contact in kneeling oral position", "Override template metadata changed item text")
    _expect(override_metadata.get("action_family") == "oral", "Template object did not override inherited action family")
    _expect(override_metadata.get("position_family") == "manual", "Template object should keep inherited position family when absent")
    _expect(
        route_metadata.row_formatter_hints({"item_template_metadata": override_metadata}, "krea")
        == ["inherited shared cue", "override krea cue"],
        "Template metadata did not merge inherited and template formatter hints",
    )
    route_row = {
        "action_family": "penetrative",
        "position_family": "Oral",
        "position_keys": ["spread leg oral", "bad key"],
        "position_key": "open thighs",
        "formatter_hints": {"all": ["shared formatter cue"], "training_caption": ["caption formatter cue"]},
    }
    _expect(route_metadata.row_action_family(route_row) == "penetration", "Route metadata action normalization changed")
    _expect(route_metadata.row_position_family(route_row) == "oral", "Route metadata position-family normalization changed")
    _expect(
        route_metadata.row_position_keys(route_row) == ["spread_leg_oral", "open_thighs"],
        "Route metadata position-key normalization changed",
    )
    _expect(
        route_metadata.row_position_keys({"position_keys": ["kneeling_oral"]}, include_unknown=True) == ["kneeling_oral"],
        "Route metadata legacy position-key passthrough changed",
    )
    _expect(
        route_metadata.row_formatter_hints(route_row, "caption") == ["shared formatter cue", "caption formatter cue"],
        "Route metadata formatter hint routing changed",
    )
    nested_route_row = {
        "item_template_metadata": {
            "action_family": "oral",
            "position_family": "oral",
            "position_keys": ["kneeling", "open_thighs"],
            "formatter_hint": {"krea2": "nested krea cue", "sdxl": "nested sdxl cue", "training_caption": "nested caption cue"},
        }
    }
    _expect(
        route_metadata.row_action_family(nested_route_row) == "oral",
        "Route metadata should fall back to nested template action family",
    )
    _expect(
        route_metadata.row_position_family(nested_route_row) == "oral",
        "Route metadata should fall back to nested template position family",
    )
    _expect(
        route_metadata.row_position_keys(nested_route_row) == ["kneeling", "open_thighs"],
        "Route metadata should fall back to nested template position keys",
    )
    _expect(
        route_metadata.row_formatter_hints(nested_route_row, "krea") == ["nested krea cue"],
        "Route metadata should fall back to nested Krea formatter hints",
    )
    merged_route_row = {
        "position_key": "standing",
        "formatter_hints": {"all": ["shared cue"]},
        "item_template_metadata": {
            "position_keys": ["kneeling"],
            "formatter_hint": {"caption": "nested caption cue"},
        },
    }
    _expect(
        route_metadata.row_position_keys(merged_route_row) == ["standing", "kneeling"],
        "Route metadata should merge top-level and nested position keys",
    )
    _expect(
        route_metadata.row_formatter_hints(merged_route_row, "caption") == ["shared cue", "nested caption cue"],
        "Route metadata should merge top-level and nested formatter hints",
    )
    route_hints = category_template_metadata.formatter_hints_for_route(
        {"formatter_hints": {"all": ["shared formatter cue"], "krea2": ["krea formatter cue"]}},
        "krea2",
    )
    _expect(route_hints == ["shared formatter cue", "krea formatter cue"], "Formatter hint route resolver changed")
    raw_route_hints = category_template_metadata.formatter_hints_for_route(
        {"all": ["raw shared cue"], "caption": ["raw caption cue"]},
        "caption",
    )
    _expect(raw_route_hints == ["raw shared cue", "raw caption cue"], "Raw formatter route-map resolver changed")
    _expect(
        category_template_metadata.formatter_hints_for_route({"caption": "row caption should not be a hint", "prompt": "row prompt"}, "caption")
        == [],
        "Formatter hint route resolver treated an arbitrary metadata row as route hints",
    )
    _expect(
        pb._template_action_family(template_metadata) == category_template_metadata.template_action_family(template_metadata),
        "Prompt builder template action policy should delegate",
    )
    _expect(
        category_template_metadata.template_metadata_errors(template_metadata) == [],
        "Valid template metadata should not report audit errors",
    )
    invalid_metadata = {
        "action_family": "bad_action",
        "position_family": "bad_family",
        "position_keys": ["kneeling", "bad_position"],
        "formatter_hint": {"bad_route": 9, "sdxl": ["ok", ""]},
    }
    invalid_errors = category_template_metadata.template_metadata_errors(invalid_metadata)
    _expect(any("bad_action" in error for error in invalid_errors), "Template metadata validation missed bad action")
    _expect(any("bad_family" in error for error in invalid_errors), "Template metadata validation missed bad family")
    _expect(any("bad_position" in error for error in invalid_errors), "Template metadata validation missed bad position key")
    _expect(any("bad_route" in error for error in invalid_errors), "Template metadata validation missed bad formatter route")
    _expect(any("invalid formatter_hint" in error for error in invalid_errors), "Template metadata validation missed bad formatter hint value")


def smoke_row_route_metadata_policy() -> None:
    template_metadata = {
        "action_family": "oral",
        "position_family": "oral",
        "position_keys": ["kneeling", "open_thighs"],
    }
    route = row_route_metadata.resolve_action_position_route(
        is_pose_category=True,
        subcategory={"slug": "oral_sex"},
        hardcore_position_config={},
        item_template_metadata=template_metadata,
        item_text="mouth contact in kneeling oral position",
        source_role_graph="the woman kneels in front of the man",
        source_composition="close kneeling oral composition",
        pose="kneeling pose",
        item_axis_values={"position": "kneeling oral position"},
    )
    _expect(route["action_family"] == "oral", "Route policy lost template action family")
    _expect(route["position_family"] == "oral", "Route policy lost template position family")
    _expect(route["position_key"] == "kneeling", "Route policy did not preserve first template position key")
    _expect(route["position_keys"] == ["kneeling", "open_thighs"], "Route policy changed template position-key precedence")
    route_result = row_route_metadata.resolve_action_position_route_result(
        is_pose_category=True,
        subcategory={"slug": "oral_sex"},
        hardcore_position_config={},
        item_template_metadata=template_metadata,
        item_text="mouth contact in kneeling oral position",
        source_role_graph="the woman kneels in front of the man",
        source_composition="close kneeling oral composition",
        pose="kneeling pose",
        item_axis_values={"position": "kneeling oral position"},
    )
    _expect(route_result.as_dict() == route, "Typed action/position route should match legacy dict route")
    _expect(route_result.position_key == "kneeling", "Typed action/position route lost first position key")

    delegated = pb._action_position_route_metadata(
        is_pose_category=True,
        subcategory={"slug": "oral_sex"},
        hardcore_position_config={},
        item_template_metadata=template_metadata,
        item_text="mouth contact in kneeling oral position",
        source_role_graph="the woman kneels in front of the man",
        source_composition="close kneeling oral composition",
        pose="kneeling pose",
        item_axis_values={"position": "kneeling oral position"},
    )
    _expect(delegated == route, "Prompt builder route wrapper should delegate to row_route_metadata")
    typed_delegated = pb._action_position_route(
        is_pose_category=True,
        subcategory={"slug": "oral_sex"},
        hardcore_position_config={},
        item_template_metadata=template_metadata,
        item_text="mouth contact in kneeling oral position",
        source_role_graph="the woman kneels in front of the man",
        source_composition="close kneeling oral composition",
        pose="kneeling pose",
        item_axis_values={"position": "kneeling oral position"},
    )
    _expect(typed_delegated == route_result, "Prompt builder typed route wrapper should delegate to row_route_metadata")

    fallback = row_route_metadata.resolve_action_position_route(
        is_pose_category=True,
        subcategory={"slug": "manual_stimulation"},
        hardcore_position_config={},
        item_template_metadata={},
        item_text="manual stimulation while kneeling",
        source_role_graph="the woman kneels close and uses her hand",
        source_composition="kneeling manual composition",
        pose="kneeling pose",
        item_axis_values={"position": "kneeling manual position"},
    )
    _expect(fallback["position_family"] == "manual", "Route policy lost source position-family fallback")
    _expect(fallback["action_family"] == "manual", "Route policy lost source action-family fallback")
    _expect("kneeling" in fallback["position_keys"], "Route policy lost inferred position key")

    empty = row_route_metadata.resolve_action_position_route(
        is_pose_category=False,
        subcategory={"slug": "casual_clothes"},
        hardcore_position_config={},
        item_template_metadata=template_metadata,
        item_text="casual outfit",
        source_role_graph="",
        source_composition="",
        pose="standing pose",
    )
    _expect(empty == row_route_metadata.empty_action_position_route(), "Non-pose route should return empty route metadata")
    _expect(
        row_route_metadata.empty_action_position_route_result().as_dict() == empty,
        "Typed empty action/position route should match legacy dict route",
    )


def smoke_category_library_route() -> None:
    categories = category_library.load_category_library()
    _expect(len(categories) >= 3, "category library should load JSON categories")
    category, subcategory, women_count, men_count = category_library.find_subcategory(
        categories,
        "custom_random",
        "Hardcore sexual poses / Oral sex",
        random.Random(101),
        random.Random(102),
        women_count=1,
        men_count=1,
    )
    _expect(category.get("slug") == "hardcore_sexual_poses", "exact category lookup selected wrong category")
    _expect(subcategory.get("slug") == "oral_sex", "exact subcategory lookup selected wrong subcategory")
    _expect((women_count, men_count) == (1, 1), "exact subcategory lookup changed compatible cast counts")

    slash_categories = [
        {"name": "Dev", "slug": "dev", "subcategories": [{"name": "Wrong Route", "slug": "wrong_route", "items": ["wrong item"]}]},
        {
            "name": "Dev / Test Wear",
            "slug": "dev_test_wear",
            "subcategories": [{"name": "Layered / Office", "slug": "layered_office", "items": ["structured test item"]}],
        },
    ]
    slash_selector = category_library.exact_subcategory_selector(
        slash_categories[1],
        slash_categories[1]["subcategories"][0],
    )
    slash_choice = category_library.split_exact_subcategory_choice(slash_categories, slash_selector)
    _expect(slash_choice is not None, "Exact selector parser did not accept slash-bearing category/subcategory names")
    if slash_choice is not None:
        _expect(slash_choice[0].get("slug") == "dev_test_wear", "Exact selector parser did not prefer longest category prefix")
        _expect(slash_choice[1] == "Layered / Office", "Exact selector parser trimmed slash-bearing subcategory incorrectly")
    slash_category, slash_subcategory, _slash_women, _slash_men = category_library.find_subcategory(
        slash_categories,
        "custom_random",
        slash_selector,
        random.Random(201),
        random.Random(202),
        women_count=1,
        men_count=0,
    )
    _expect(slash_category.get("slug") == "dev_test_wear", "Exact subcategory lookup failed slash-bearing category")
    _expect(slash_subcategory.get("slug") == "layered_office", "Exact subcategory lookup failed slash-bearing subcategory")

    item = category_library.compatible_entries(list(subcategory.get("items") or []), women_count, men_count)[0]
    scenes = category_library.configured_pool(
        category,
        subcategory,
        item,
        "scenes",
        "scene_pools",
        category_library.load_scene_pool_library(),
        "inherit_scenes",
    )
    expressions = category_library.configured_pool(
        category,
        subcategory,
        item,
        "expressions",
        "expression_pools",
        category_library.load_expression_pool_library(),
        "inherit_expressions",
    )
    compositions = category_library.configured_pool(
        category,
        subcategory,
        item,
        "compositions",
        "composition_pools",
        category_library.load_composition_pool_library(),
        "inherit_compositions",
    )
    _expect(scenes, "category inheritance did not resolve scenes")
    _expect(expressions, "category inheritance did not resolve expressions")
    _expect(compositions, "category inheritance did not resolve compositions")
    _expect(any("oral" in _clean_key(entry.get("prompt") if isinstance(entry, dict) else entry) for entry in scenes), "oral scene pool did not contribute")
    location_override = {"enabled": True, "apply_mode": "replace", "scene_entries": ["custom scene"]}
    composition_override = {"enabled": True, "apply_mode": "replace", "composition_entries": ["custom composition"]}
    _expect(
        pb._scene_pool(category, subcategory, item, "configured_cast", location_override)
        == row_pools.scene_pool(category, subcategory, item, "configured_cast", location_override),
        "Prompt builder scene pool should delegate to row_pools",
    )
    _expect(
        pb._expression_pool(category, subcategory, item) == row_pools.expression_pool(category, subcategory, item),
        "Prompt builder expression pool should delegate to row_pools",
    )
    _expect(
        pb._pose_pool(category, subcategory, item, "couple", "standard") == row_pools.pose_pool(category, subcategory, item, "couple", "standard"),
        "Prompt builder pose pool should delegate to row_pools",
    )
    _expect(
        pb._composition_pool(category, subcategory, item, "configured_cast", composition_override)
        == row_pools.composition_pool(category, subcategory, item, "configured_cast", composition_override),
        "Prompt builder composition pool should delegate to row_pools",
    )


def smoke_category_subcategory_matrix() -> None:
    categories = category_library.load_category_library()
    cases: list[tuple[dict[str, Any], dict[str, Any]]] = []
    for category in categories:
        for subcategory in category.get("subcategories") or []:
            if subcategory.get("item_templates") or subcategory.get("items"):
                cases.append((category, subcategory))
    _expect(len(cases) >= 30, "category matrix should cover all configured JSON subcategories")

    hardcore_filter = _broad_hardcore_filter()
    for index, (category, subcategory) in enumerate(cases, start=6101):
        category_slug = str(category.get("slug") or "")
        subcategory_slug = str(subcategory.get("slug") or "")
        name = f"category_matrix.{category_slug}.{subcategory_slug}"
        women_count, men_count, cast = _matrix_cast_for_route(category, subcategory)
        row = _prompt_row(
            name=name,
            category=str(category.get("name") or ""),
            subcategory=_exact_subcategory_selector(category, subcategory),
            seed=index,
            character_cast=cast,
            women_count=women_count,
            men_count=men_count,
            hardcore_position_config=hardcore_filter if category_slug == "hardcore_sexual_poses" else "",
        )

        _expect(row.get("source") == "json_category", f"{name}.source should be json_category")
        _expect(row.get("category_slug") == category_slug, f"{name}.category_slug drifted to {row.get('category_slug')}")
        _expect(
            row.get("subcategory_slug") == subcategory_slug,
            f"{name}.subcategory_slug drifted to {row.get('subcategory_slug')}",
        )
        _expect_text(f"{name}.item", row.get("item"), 8)
        _expect_text(f"{name}.scene_text", row.get("scene_text"), 8)
        _expect_text(f"{name}.composition", row.get("composition"), 8)
        _expect(isinstance(row.get("item_axis_values"), dict), f"{name}.item_axis_values missing")
        _expect(isinstance(row.get("formatter_hints"), dict), f"{name}.formatter_hints missing")

        if category_slug == "hardcore_sexual_poses":
            _expect(row.get("content_seed_axis") == "pose", f"{name}.content_seed_axis should be pose")
            _expect_text(f"{name}.source_role_graph", row.get("source_role_graph") or row.get("role_graph"), 20)
            _expect_text(f"{name}.action_family", row.get("action_family"), 3)
            _expect_text(f"{name}.position_family", row.get("position_family"), 3)
            _expect(isinstance(row.get("position_keys"), list), f"{name}.position_keys missing")
            _expect(isinstance(row.get("item_template_metadata"), dict), f"{name}.item_template_metadata missing")
            _expect(row.get("item_template_metadata"), f"{name}.item_template_metadata should not be empty")
        else:
            _expect(row.get("content_seed_axis") == "content", f"{name}.content_seed_axis should be content")

        _expect_formatter_outputs(row, name, target="single")


def smoke_hardcore_category_routes() -> None:
    cast = _character_cast()
    cases = [
        ("hardcore_penetration", "Penetrative sex", "penetration_only", "penetrative", {"penetration", "default"}, "penetrative sex", "penetrative action"),
        ("hardcore_oral", "Oral sex", "oral_only", "oral", {"oral"}, "oral sex", "oral action"),
        ("hardcore_manual", "Manual stimulation", "manual_only", "manual", {"manual"}, "manual stimulation", "manual action"),
        ("hardcore_anal", "Anal and double penetration", "anal_only", "anal", {"anal", "toy_double"}, "anal sex", "anal action"),
        ("hardcore_outercourse", "Outercourse and genital teasing", "outercourse_only", "outercourse", {"outercourse"}, "outercourse", "non-penetrative action"),
        ("hardcore_foreplay", "Foreplay and teasing", "foreplay_only", "foreplay", {"foreplay"}, "foreplay", "foreplay action"),
        ("hardcore_aftercare", "Aftercare and cleanup", "interaction_only", "interaction", {"foreplay"}, "interaction", "interaction beat"),
    ]
    for index, (name, subcategory, focus, position_family, action_families, sdxl_tag, caption_label) in enumerate(cases, start=1101):
        row = _prompt_row(
            name=name,
            category="Hardcore sexual poses",
            subcategory=subcategory,
            seed=index,
            character_cast=cast,
            women_count=1,
            men_count=1,
            hardcore_position_config=_action_filter(focus),
        )
        _expect_custom_row(row, name)
        _expect(row.get("subject_type") == "configured_cast", f"{name} should use configured cast")
        _expect(row.get("position_family") == position_family, f"{name} position_family mismatch: {row.get('position_family')}")
        _expect(row.get("action_family") in action_families, f"{name} action_family mismatch: {row.get('action_family')}")
        _expect(isinstance(row.get("position_keys"), list), f"{name} position_keys missing")
        _expect_formatter_outputs(row, name, target="single")
        sdxl = sdxl_formatter.format_sdxl_prompt("", metadata_json=_json(row), target="single", trigger=SdxlTrigger, prepend_trigger=True)
        _expect(sdxl_tag in (sdxl.get("sdxl_prompt") or "").lower(), f"{name} SDXL prompt did not include family tag {sdxl_tag!r}")
        caption, _method = caption_naturalizer.naturalize_caption("", metadata_json=_json(row), trigger=Trigger, include_trigger=True)
        _expect(caption_label in caption.lower(), f"{name} caption did not include family label {caption_label!r}")
    styled_row = _prompt_row(
        name="hardcore_style_pool_override",
        category="Hardcore sexual poses",
        subcategory="Penetrative sex",
        seed=1181,
        character_cast=cast,
        women_count=1,
        men_count=1,
        hardcore_position_config=_action_filter("penetration_only"),
        style_config=pb.build_style_config_json(preset="comic_pinup_colored_pencil"),
    )
    _expect("comic pin-up" in styled_row.get("style", ""), "Style Pool did not reach generated hardcore row style")
    _expect("comic linework" in styled_row.get("positive_suffix", ""), "Style Pool did not reach generated hardcore row suffix")
    _expect("comic pin-up" in styled_row.get("prompt", ""), "Style Pool style was not rendered into hardcore prompt")
    multi_cases = [
        ("hardcore_threesome", "Threesomes", "threesome_only", "threesome", {"threesome", "toy_double"}, "threesome", "three-person action", 1, 2),
        ("hardcore_group", "Group sex and orgy", "group_only", "group", {"group", "toy_double"}, "group sex", "group action", 2, 2),
    ]
    for index, (name, subcategory, focus, position_family, action_families, sdxl_tag, caption_label, women_count, men_count) in enumerate(multi_cases, start=1151):
        subjects = ["woman"] * women_count + ["man"] * men_count
        row = _prompt_row(
            name=name,
            category="Hardcore sexual poses",
            subcategory=subcategory,
            seed=index,
            character_cast=_character_cast_subjects(subjects),
            women_count=women_count,
            men_count=men_count,
            hardcore_position_config=_action_filter(focus),
        )
        _expect_custom_row(row, name)
        _expect(row.get("subject_type") == "configured_cast", f"{name} should use configured cast")
        _expect(row.get("position_family") == position_family, f"{name} position_family mismatch: {row.get('position_family')}")
        _expect(row.get("action_family") in action_families, f"{name} action_family mismatch: {row.get('action_family')}")
        _expect_formatter_outputs(row, name, target="single")
        sdxl = sdxl_formatter.format_sdxl_prompt("", metadata_json=_json(row), target="single", trigger=SdxlTrigger, prepend_trigger=True)
        _expect(sdxl_tag in (sdxl.get("sdxl_prompt") or "").lower(), f"{name} SDXL prompt did not include family tag {sdxl_tag!r}")
        caption, _method = caption_naturalizer.naturalize_caption("", metadata_json=_json(row), trigger=Trigger, include_trigger=True)
        _expect(caption_label in caption.lower(), f"{name} caption did not include family label {caption_label!r}")
    annotated_row = None
    for seed in range(1801, 1841):
        row = _prompt_row(
            name="hardcore_annotated_template",
            category="Hardcore sexual poses",
            subcategory="Oral sex",
            seed=seed,
            character_cast=cast,
            women_count=1,
            men_count=1,
            hardcore_position_config=_action_filter("oral_only"),
        )
        if row.get("item_template_metadata"):
            annotated_row = row
            break
    _expect(annotated_row is not None, "No annotated item template reached generated row in deterministic seed window")
    if annotated_row is not None:
        _expect(annotated_row.get("action_family") == "oral", "Annotated item template action_family did not reach row")
        _expect(annotated_row.get("position_family") == "oral", "Annotated item template position_family did not reach row")
        _expect(annotated_row.get("item_template_metadata", {}).get("action_family") == "oral", "Annotated item metadata missing in row")


def smoke_krea_close_foreplay_route() -> None:
    row = _prompt_row(
        name="krea_close_foreplay_route",
        category="Hardcore sexual poses",
        subcategory="Foreplay and teasing",
        seed=3401,
        character_cast=_character_cast(),
        women_count=1,
        men_count=1,
        hardcore_position_config=_action_filter("foreplay_only"),
    )
    _expect_custom_row(row, "krea_close_foreplay_route")
    krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(row), target="single")
    prompt = _expect_text("krea_close_foreplay_route.krea_prompt", krea.get("krea_prompt"), 40)
    lower = prompt.lower()
    _expect("metadata" in krea.get("method", ""), "close foreplay route did not use metadata")
    _expect("role graph:" not in lower, "close foreplay leaked raw role label")
    _expect("foreplay action:" not in lower, "close foreplay leaked raw item label")
    _expect("on against" not in lower, "close foreplay kept invalid surface grammar")
    _expect(
        any(term in lower for term in ("clothing", "hands", "kiss", "bodies press", "body contact")),
        "close foreplay lost close-contact action wording",
    )
    _expect_formatter_outputs(row, "krea_close_foreplay_route", target="single")


def _insta_options(**overrides: Any) -> str:
    options = pb.build_insta_of_options_json(
        softcore_cast="same_as_hardcore",
        hardcore_cast="couple",
        hardcore_women_count=1,
        hardcore_men_count=1,
        softcore_level="lingerie_tease",
        hardcore_level="hardcore",
        platform_style="hybrid",
        continuity="same_creator_same_room",
        hardcore_clothing_continuity="explicit_nude",
        softcore_camera_mode="standard",
        hardcore_camera_mode="standard",
        camera_detail="compact",
        hardcore_detail_density="balanced",
    )
    data = json.loads(options)
    data.update(overrides)
    return _json(data)


def smoke_pair_options_policy() -> None:
    _expect(
        pb.INSTA_OF_SOFTCORE_OUTFITS is pb.pair_options.INSTA_OF_SOFTCORE_OUTFITS,
        "prompt_builder should delegate Insta/OF softcore outfit policy to pair_options",
    )
    _expect(
        pb.HARDCORE_DETAIL_DENSITY_CHOICES is pb.pair_options.HARDCORE_DETAIL_DENSITY_CHOICES,
        "prompt_builder should delegate hardcore detail density choices to pair_options",
    )
    _expect(
        pb.pair_options.hardcore_detail_directive("compact").startswith("Use one compact"),
        "compact hardcore detail density should have a compact directive",
    )
    _expect(
        pb.pair_options.hardcore_detail_directive("dense").startswith("Use dense"),
        "dense hardcore detail density should have a dense directive",
    )
    _expect(
        pb.pair_options.hardcore_detail_directive("balanced") == "",
        "balanced hardcore detail density should not add a directive",
    )
    _expect(
        pb.pair_options.hardcore_detail_directive("bad") == "",
        "invalid hardcore detail density directive should be empty",
    )
    _expect(
        "scattered clothes" not in pair_clothing.body_exposure_scene_text(
            "mirror corner, scattered clothes, outfit-check framing"
        ),
        "Pair clothing body exposure scene cleanup should remove clothing clutter",
    )
    _expect(
        "creator-shot" in pair_clothing.body_exposure_scene_text("outfit-check framing"),
        "Pair clothing body exposure scene cleanup should replace outfit-check wording",
    )
    sanitized_closet_scene = pair_clothing.body_exposure_scene_text(
        "full-length closet mirror with outfit racks, shoe shelves, and soft boutique lighting"
    ).lower()
    _expect("outfit racks" not in sanitized_closet_scene, "Pair clothing body exposure scene cleanup should remove outfit racks")
    _expect("shoe shelves" not in sanitized_closet_scene, "Pair clothing body exposure scene cleanup should remove shoe shelves")
    _expect("mirror shelves" in sanitized_closet_scene, "Pair clothing body exposure scene cleanup should keep neutral mirror detail")
    _expect(
        pair_clothing.softcore_outfit_sentence("Man A", "wears hoodie and joggers")
        == "Man A wears hoodie and joggers",
        "Pair clothing softcore outfit sentence formatting changed",
    )
    _expect(
        pair_clothing.hardcore_clothing_sentence("Woman A", "fully nude")
        == "Woman A's body is fully exposed, bare skin unobstructed",
        "Pair clothing hardcore fully nude sentence formatting changed",
    )
    _expect(
        pair_clothing.character_hardcore_clothing_entries(
            {
                "Woman A": {"hardcore_clothing": "fully nude"},
                "Man A": {"hardcore_clothing": "wears jeans"},
            },
            1,
            1,
            ["Man A"],
            random.Random(1),
            lambda slot, _rng: str((slot or {}).get("hardcore_clothing") or ""),
        )
        == ["Woman A's body is fully exposed, bare skin unobstructed"],
        "Pair clothing character entries should skip POV labels",
    )
    _expect(
        pair_cast.cast_summary_phrase(2, 1) == "2 women, 1 man, 3 total adults",
        "Pair cast summary phrase should live in pair_cast",
    )
    descriptor_row = {
        "age": "25-year-old adult",
        "body_phrase": "curvy build",
        "skin": "warm skin",
        "hair": "dark hair",
        "eyes": "brown eyes",
    }
    _expect(
        pb._insta_of_descriptor(descriptor_row) == pair_cast.insta_descriptor_from_row(descriptor_row),
        "Prompt builder Insta descriptor should delegate to pair_cast",
    )
    _expect(
        pair_cast.insta_descriptor_from_context(
            {"subject_type": "man", "age": "40-year-old adult", "body_phrase": "stocky figure"}
        )
        == "40-year-old adult man, stocky figure",
        "Pair cast context descriptor formatting changed",
    )
    _expect(
        pair_cast.prompt_cast_descriptors("Woman A / primary creator: descriptor") == "Woman A: descriptor",
        "Pair cast prompt descriptor label cleanup changed",
    )

    def _fake_character_context(
        label: str,
        label_map: dict[str, dict[str, Any]],
        _rng: random.Random,
        _ethnicity: str,
        _figure: str,
        _no_plus_women: bool,
        _no_black: bool,
    ) -> tuple[dict[str, Any], dict[str, Any] | None]:
        subject = "man" if label.startswith("Man ") else "woman"
        age = "40-year-old adult" if subject == "man" else "30-year-old adult"
        return {"subject_type": subject, "age": age, "body_phrase": f"{label} body"}, label_map.get(label)

    descriptor_entries, descriptor_slots = pair_cast.cast_descriptor_entries_from_slots(
        seed_config={},
        seed=1,
        row_number=1,
        ethnicity="any",
        figure="any",
        no_plus_women=False,
        no_black=False,
        women_count=2,
        men_count=1,
        character_slots=[{"subject_type": "man", "presence_mode": "pov"}],
        character_slot_map={"Man A": {"subject_type": "man", "presence_mode": "pov"}},
        primary_descriptor="primary descriptor",
        axis_rng=lambda _config, _axis, seed_value, row_value: random.Random(seed_value + row_value),
        character_context_for_label=_fake_character_context,
        slot_is_pov=lambda slot: bool(slot and slot.get("presence_mode") == "pov"),
    )
    _expect(
        descriptor_entries
        == [
            "Woman A / primary creator: primary descriptor",
            "Woman B: 30-year-old adult woman, Woman B body",
        ],
        "Pair cast descriptor entries should keep primary label and skip POV men",
    )
    _expect(
        descriptor_slots == [{"subject_type": "man", "presence_mode": "pov"}],
        "Pair cast descriptor entries should return the source slots",
    )
    partner_styling = pair_cast.softcore_partner_styling(
        seed_config={},
        seed=1,
        row_number=1,
        women_count=2,
        men_count=1,
        pov_labels=["Man A"],
        label_map={"Woman B": {"softcore_outfit": "custom satin dress"}, "Man A": {"softcore_outfit": "hidden"}},
        axis_rng=lambda _config, _axis, seed_value, row_value: random.Random(seed_value + row_value),
        choose=lambda _rng, pool: pool[0],
        slot_softcore_outfit=lambda slot, _rng: str((slot or {}).get("softcore_outfit") or ""),
    )
    _expect(
        partner_styling["outfits"] == ["Woman B wears custom satin dress"],
        "Pair cast partner styling should use configured partner outfit and skip POV men",
    )
    _expect_text("pair_cast.partner_pose", partner_styling.get("pose"), 12)
    options = json.loads(
        pb.build_insta_of_options_json(
            softcore_expression_enabled="false",
            hardcore_expression_enabled="0",
            softcore_expression_intensity=1.4,
            hardcore_expression_intensity=-0.4,
            hardcore_detail_density="invalid",
        )
    )
    _expect(options["softcore_expression_enabled"] is False, "softcore expression enabled should normalize false strings")
    _expect(options["hardcore_expression_enabled"] is False, "hardcore expression enabled should normalize false strings")
    _expect(options["softcore_expression_intensity"] == 1.0, "softcore expression intensity should clamp high values")
    _expect(options["hardcore_expression_intensity"] == 0.0, "hardcore expression intensity should clamp low values")
    _expect(options["hardcore_detail_density"] == "balanced", "invalid hardcore detail density should fallback")

    parsed = pb._parse_insta_of_options(
        {
            "softcore_cast": "bad",
            "hardcore_cast": "bad",
            "softcore_camera_mode": "bad",
            "hardcore_camera_mode": "bad",
            "camera_detail": "bad",
            "hardcore_detail_density": "bad",
            "hardcore_women_count": "20",
            "hardcore_men_count": "-3",
        }
    )
    _expect(parsed["softcore_cast"] == "solo", "invalid softcore cast should fallback")
    _expect(parsed["hardcore_cast"] == "use_counts", "invalid hardcore cast should fallback")
    _expect(parsed["softcore_camera_mode"] == "handheld_selfie", "invalid softcore camera should fallback")
    _expect(parsed["hardcore_camera_mode"] == "from_camera_config", "invalid hardcore camera should fallback")
    _expect(parsed["camera_detail"] == "from_camera_config", "invalid camera detail should fallback")
    _expect(parsed["hardcore_detail_density"] == "balanced", "invalid hardcore density should fallback on parse")
    _expect(parsed["hardcore_women_count"] == 12, "women count should clamp to max")
    _expect(parsed["hardcore_men_count"] == 0, "men count should clamp to min")

    _expect(pb.character_softcore_outfit_values("partner_man"), "partner man softcore outfit pool should not be empty")
    _expect(
        pb.character_softcore_outfit_values("custom", "one; two\nthree") == ["one", "two", "three"],
        "custom softcore outfits should split stable free-text lists",
    )
    _expect("fully nude" in pb.character_hardcore_clothing_values("fully_nude"), "fully nude clothing state should be exposed")
    _expect(
        pb.character_hardcore_clothing_values("custom", "bare; outfit pushed aside") == ["bare", "outfit pushed aside"],
        "custom hardcore clothing should split stable free-text lists",
    )
    _expect(pb._insta_of_hardcore_counts({"hardcore_cast": "threesome"}) == (2, 1), "threesome count policy changed")
    _expect(pb._insta_of_softcore_category("social_tease") == ("Casual clothes", "Casual clothes / Smart casual"), "softcore category mapping changed")


def smoke_pair_route_policy() -> None:
    def _fake_build_prompt(**kwargs: Any) -> dict[str, Any]:
        is_hard = kwargs.get("category") == "Hardcore sexual poses"
        return {
            "category": kwargs.get("category"),
            "subcategory": kwargs.get("subcategory"),
            "scene_text": "shared test room",
            "source_scene_text": "",
            "composition": "centered test composition",
            "source_composition": "",
            "expression": "steady look",
            "role_graph": "test role graph",
            "item": "test item",
            "positive_suffix": "test positive suffix",
            "negative_prompt": "test negative",
            "prompt": "test hard prompt" if is_hard else "test soft prompt",
            "caption": "test hard caption" if is_hard else "test soft caption",
        }

    pair_options_data = {
        "softcore_cast": "solo",
        "softcore_expression_enabled": True,
        "softcore_expression_intensity": 0.45,
        "hardcore_expression_enabled": True,
        "hardcore_expression_intensity": 0.85,
        "hardcore_detail_density": "balanced",
    }
    pair_rows_kwargs: dict[str, Any] = {
        "row_number": 1,
        "start_index": 1,
        "seed": 10,
        "active_trigger": Trigger,
        "parsed_seed_config": {},
        "options": pair_options_data,
        "ethnicity": "any",
        "figure": "random",
        "no_plus_women": False,
        "no_black": False,
        "character_profile": "",
        "character_cast": "",
        "character_slot_map": {},
        "pov_character_labels": [],
        "hard_women_count": 1,
        "hard_men_count": 1,
        "soft_category": "Casual clothes",
        "soft_subcategory": "Casual clothes / Smart casual",
        "softcore_level_key": "social_tease",
        "hardcore_random_subcategory": pb.RANDOM_SUBCATEGORY,
        "hardcore_position_config": "",
        "location_config": "",
        "composition_config": "",
        "build_prompt": _fake_build_prompt,
        "axis_rng": lambda _config, _axis, seed_value, row_value: random.Random(seed_value + row_value),
        "cast_expression_intensity_override": lambda value, _slots, _women, _men, _phase: (value, "input"),
        "context_from_character_slot": lambda *_args, **_kwargs: {},
        "apply_character_context_to_row": lambda row, context: {**row, **context},
        "disable_row_expression": lambda row, source: {**row, "expression_disabled": True, "expression_intensity_source": source},
        "slot_softcore_outfit": lambda _slot, _rng: "",
        "softcore_outfit": lambda _rng, _level: "test soft outfit",
        "softcore_pose": lambda _rng, _level: "test soft pose",
        "softcore_item_prompt_label": lambda _level: "Softcore test outfit",
        "pov_prompt_directive": lambda labels: "POV directive" if labels else "",
        "pov_composition_prompt": lambda composition, labels: f"{composition} for {','.join(labels)}" if labels else str(composition),
    }
    rows_route = pair_rows.build_insta_pair_rows_result(**pair_rows_kwargs)
    rows_legacy = pair_rows.build_insta_pair_rows(**pair_rows_kwargs)
    _expect(rows_route.soft_row == rows_legacy["soft_row"], "Typed pair row route should match legacy soft row")
    _expect(rows_route.hard_row == rows_legacy["hard_row"], "Typed pair row route should match legacy hard row")
    _expect(
        rows_route.hard_content_rng.getstate() == rows_legacy["hard_content_rng"].getstate(),
        "Typed pair row route should match legacy hard content RNG state",
    )
    _expect(rows_route.soft_row["item"] == "test soft outfit", "Typed pair row route lost soft outfit override")
    _expect(rows_route.hard_row["hardcore_detail_density"] == "balanced", "Typed pair row route lost hard density")

    camera_options = {
        "hardcore_camera_mode": "same_as_softcore",
        "softcore_camera_mode": "standard",
        "camera_detail": "compact",
        "softcore_cast": "same_as_hardcore",
        "continuity": "same_creator_same_room",
    }

    def _camera_config_with_mode(source: Any, mode: str) -> dict[str, Any]:
        parsed = dict(source or {})
        parsed["camera_mode"] = mode
        return parsed

    def _camera_directive(config: dict[str, Any]) -> tuple[str, dict[str, Any]]:
        return f"{config.get('camera_mode')} camera", config

    def _camera_rows() -> tuple[dict[str, Any], dict[str, Any]]:
        return (
            {"scene_text": "soft room", "composition": "soft composition"},
            {"scene_text": "hard room", "composition": "hard composition"},
        )

    camera_common = {
        "options": camera_options,
        "camera_config": {"base": "camera"},
        "softcore_camera_config": None,
        "hardcore_camera_config": None,
        "hard_women_count": 1,
        "hard_men_count": 1,
        "pov_character_labels": [],
        "camera_detail_choices": ("compact",),
        "camera_config_with_mode": _camera_config_with_mode,
        "camera_directive": _camera_directive,
        "apply_contextual_composition": lambda row, subject_kind: {**row, "subject_kind": subject_kind},
        "contextual_composition_prompt": lambda scene, composition, subject_kind: f"{subject_kind}: {scene}: {composition}",
        "composition_prompt": lambda composition: f"Framed as {composition}",
        "camera_scene_directive_for_context": lambda _scene, _composition, config, _pov, subject: (f"{subject} scene directive", config),
    }
    soft_row, hard_row = _camera_rows()
    camera_route = pair_camera.resolve_insta_pair_camera_result(soft_row=soft_row, hard_row=hard_row, **camera_common)
    soft_row, hard_row = _camera_rows()
    camera_legacy = pair_camera.resolve_insta_pair_camera(soft_row=soft_row, hard_row=hard_row, **camera_common)
    _expect(camera_route.as_dict() == camera_legacy, "Typed pair camera route should match legacy dict route")
    _expect(camera_route.hard_scene == "soft room", "Typed pair camera route lost same-room continuity")
    _expect(camera_route.hard_camera_sentence.startswith("Camera control:"), "Typed pair camera route lost hard camera sentence")
    _expect(camera_route.soft_row.get("subject_kind") == "couple", "Same-cast softcore camera route should use couple subject kind")
    _expect("couple scene directive" in camera_route.soft_camera_scene_directive, "Same-cast softcore camera directive should use couple wording")

    clothing_common = {
        "hard_row": {
            "role_graph": "the man thrusts his penis into the woman",
            "item": "penetrative test item",
        },
        "mode": "explicit_nude",
        "softcore_outfit": "test lingerie",
        "character_hardcore_clothing_entries": [],
        "men_count": 0,
        "pov_labels": [],
        "rng": random.Random(1),
        "continuity_map": pb.INSTA_OF_HARDCORE_CLOTHING_CONTINUITY,
        "choose": lambda _rng, pool: pool[0],
    }
    clothing_route = pair_clothing.resolve_hardcore_pair_clothing_result(**clothing_common)
    clothing_legacy = pair_clothing.resolve_hardcore_pair_clothing(**clothing_common)
    _expect(clothing_route.as_dict() == clothing_legacy, "Typed pair clothing route should match legacy dict route")
    _expect(clothing_route.woman_access == "lower", "Typed pair clothing route lost lower-access detection")
    _expect(clothing_route.requires_body_exposure_scene is True, "Typed pair clothing route lost exposure-scene flag")
    continuity_outfit = "button-down shirt tied at the waist over a fitted bralette and denim shorts"
    partial_common = {**clothing_common, "mode": "partially_removed", "softcore_outfit": continuity_outfit}
    partial_route = pair_clothing.resolve_hardcore_pair_clothing_result(**partial_common)
    _expect(partial_route.requires_body_exposure_scene is True, "Partial lower-access clothing should request scene cleanup")
    partial_lower = partial_route.hardcore_clothing_state.lower()
    _expect("denim shorts" in partial_lower, "Partial lower-access clothing should name removed lower softcore garment")
    _expect("below the hips" in partial_lower, "Partial lower-access clothing should describe lower-garment removal")
    _expect("button-down shirt" in partial_lower and "fitted bralette" in partial_lower, "Partial lower-access clothing lost remaining softcore outfit styling")
    implied_route = pair_clothing.resolve_hardcore_pair_clothing_result(
        **{**clothing_common, "mode": "implied_nude", "softcore_outfit": continuity_outfit}
    )
    implied_lower = implied_route.hardcore_clothing_state.lower()
    _expect("fabric slipping off" not in implied_lower, "Implied nude clothing should not fall back to generic fabric slipping")
    _expect("denim shorts" in implied_lower and "fitted bralette" in implied_lower, "Implied nude clothing should mirror softcore outfit pieces")
    pov_clothing_route = pair_clothing.resolve_hardcore_pair_clothing_result(
        **{
            **clothing_common,
            "men_count": 1,
            "pov_labels": ["Man A"],
            "label_map": {"Man A": {"hardcore_clothing": "open shirt with jeans lowered below the hips"}},
            "slot_hardcore_clothing": lambda slot, _rng: str((slot or {}).get("hardcore_clothing") or ""),
        }
    )
    pov_clothing_lower = pov_clothing_route.hardcore_clothing_state.lower()
    _expect("pov foreground clothing cue" in pov_clothing_lower, "POV man clothing should become a foreground cue")
    _expect("jeans lowered below the hips" in pov_clothing_lower, "POV lower-access clothing lost configured lower garment state")
    _expect("man a wears" not in pov_clothing_lower, "POV clothing should not describe the POV man as a visible partner")
    _expect(not pov_clothing_route.default_man_hardcore_clothing, "POV man should not also receive default visible-man clothing")
    structured_axis_clothing = pair_clothing.resolve_hardcore_pair_clothing_result(
        **{
            **clothing_common,
            "softcore_outfit": continuity_outfit,
            "hard_row": {
                "role_graph": "generic adult action",
                "item": "generic configured action",
                "item_axis_values": {
                    "position": {"text": "edge-supported penetration"},
                    "leg_detail": ["thighs open", "auto"],
                },
            },
            "mode": "partially_removed",
        }
    )
    _expect(
        structured_axis_clothing.woman_access == "lower",
        "Pair clothing should read structured/list axis values for lower-access detection",
    )
    oral_common = {
        **clothing_common,
        "hard_row": {
            "role_graph": "the woman takes the man's penis in her mouth",
            "item": "standing oral test item",
        },
        "mode": "partially_removed",
    }
    oral_route = pair_clothing.resolve_hardcore_pair_clothing_result(**oral_common)
    _expect(oral_route.requires_body_exposure_scene is True, "Man lower-access clothing should request scene cleanup")


def smoke_pair_builder_policy() -> None:
    request = pair_builder.InstaPairBuildRequest(
        row_number=1,
        start_index=1,
        seed=2101,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(),
        character_cast=_character_cast(),
        hardcore_position_config=_action_filter("penetration_only"),
    )
    built = pair_builder.build_insta_of_pair(request, pb._insta_pair_build_dependencies())
    delegated = pb.build_insta_of_pair(
        row_number=request.row_number,
        start_index=request.start_index,
        seed=request.seed,
        ethnicity=request.ethnicity,
        figure=request.figure,
        no_plus_women=request.no_plus_women,
        no_black=request.no_black,
        trigger=request.trigger,
        prepend_trigger_to_prompt=request.prepend_trigger_to_prompt,
        seed_config=request.seed_config,
        options_json=request.options_json,
        filter_config=request.filter_config,
        camera_config=request.camera_config,
        softcore_camera_config=request.softcore_camera_config,
        hardcore_camera_config=request.hardcore_camera_config,
        character_profile=request.character_profile,
        character_cast=request.character_cast,
        hardcore_position_config=request.hardcore_position_config,
        location_config=request.location_config,
        composition_config=request.composition_config,
        extra_positive=request.extra_positive,
        extra_negative=request.extra_negative,
    )
    _expect(built == delegated, "Prompt builder Insta/OF wrapper should delegate to pair_builder without output drift")
    _expect_pair(built, "pair_builder_policy")


def _expect_pair(pair: dict[str, Any], name: str) -> None:
    _expect(pair.get("mode") == "Insta/OF", f"{name}.mode should be Insta/OF")
    _expect_row_base(pair.get("softcore_row") or {}, f"{name}.softcore_row")
    _expect_custom_row(pair.get("hardcore_row") or {}, f"{name}.hardcore_row")
    _expect_text(f"{name}.shared_descriptor", pair.get("shared_descriptor"), 12)
    _expect(pair.get("shared_cast_descriptors"), f"{name}.shared_cast_descriptors should not be empty")
    shared_cast_text = "; ".join(str(item).strip() for item in pair.get("shared_cast_descriptors") or [] if str(item).strip())
    _expect(
        pair["hardcore_row"].get("cast_descriptor_text") == shared_cast_text,
        f"{name}.hardcore_row cast descriptors drifted from pair root",
    )
    if pair.get("options", {}).get("softcore_cast") == "same_as_hardcore":
        _expect(
            pair["softcore_row"].get("cast_descriptor_text") == shared_cast_text,
            f"{name}.softcore_row cast descriptors drifted from same-cast pair root",
        )
    _expect_text(f"{name}.softcore_prompt", pair.get("softcore_prompt"), 20)
    _expect_text(f"{name}.hardcore_prompt", pair.get("hardcore_prompt"), 20)
    _expect_trigger_once(f"{name}.softcore_prompt", pair.get("softcore_prompt"), Trigger)
    _expect_trigger_once(f"{name}.hardcore_prompt", pair.get("hardcore_prompt"), Trigger)
    _expect_trigger_once(f"{name}.softcore_caption", pair.get("softcore_caption"), Trigger)
    _expect_trigger_once(f"{name}.hardcore_caption", pair.get("hardcore_caption"), Trigger)
    _expect(pair["softcore_row"].get("prompt") == pair.get("softcore_prompt"), f"{name}.softcore_row prompt drifted from pair prompt")
    _expect(pair["hardcore_row"].get("prompt") == pair.get("hardcore_prompt"), f"{name}.hardcore_row prompt drifted from pair prompt")
    _expect(pair["softcore_row"].get("caption") == pair.get("softcore_caption"), f"{name}.softcore_row caption drifted from pair caption")
    _expect(pair["hardcore_row"].get("caption") == pair.get("hardcore_caption"), f"{name}.hardcore_row caption drifted from pair caption")
    _expect(
        pair["softcore_row"].get("negative_prompt") == pair.get("softcore_negative_prompt"),
        f"{name}.softcore_row negative drifted from pair negative",
    )
    _expect(
        pair["hardcore_row"].get("negative_prompt") == pair.get("hardcore_negative_prompt"),
        f"{name}.hardcore_row negative drifted from pair negative",
    )
    if "softcore_partner_styling" in pair:
        _expect(
            pair["softcore_row"].get("softcore_partner_styling") == pair.get("softcore_partner_styling"),
            f"{name}.softcore_row partner styling drifted from pair root",
        )
    for key in (
        "hardcore_clothing_state",
        "character_hardcore_clothing",
        "default_man_hardcore_clothing",
        "hardcore_detail_density",
        "hardcore_position_config",
    ):
        if key in pair:
            _expect(pair["hardcore_row"].get(key) == pair.get(key), f"{name}.hardcore_row {key} drifted from pair root")
    _expect_no_duplicate_comma_items(f"{name}.softcore_negative", pair.get("softcore_negative_prompt"))
    _expect_no_duplicate_comma_items(f"{name}.hardcore_negative", pair.get("hardcore_negative_prompt"))
    _expect_formatter_outputs(pair, name, target="softcore")
    _expect_formatter_outputs(pair, f"{name}.hardcore", target="hardcore")


def smoke_insta_pair() -> None:
    pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=2101,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(),
        character_cast=_character_cast(),
        hardcore_position_config=_action_filter("penetration_only"),
    )
    _expect_pair(pair, "insta_pair_same_cast")
    _expect(pair["softcore_row"].get("scene_text") == pair["hardcore_row"].get("scene_text"), "pair scene continuity broke")
    _expect_no_softcore_noise("insta_pair_same_cast.softcore_prompt", pair.get("softcore_prompt"))
    _expect("styled creator-teaser frame" in str(pair.get("softcore_prompt", "")).lower(), "pair softcore prompt lost clean cast-presence wording")
    krea_soft = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="softcore")
    krea_soft_prompt = _expect_text("insta_pair_same_cast.krea_soft_prompt", krea_soft.get("krea_prompt"), 40)
    _expect_no_softcore_noise("insta_pair_same_cast.krea_soft_prompt", krea_soft_prompt)
    _expect("styled creator-teaser frame" in krea_soft_prompt.lower(), "Krea softcore prompt lost clean same-cast wording")
    clothing_state = _clean_key(pair.get("hardcore_clothing_state"))
    _expect("body is fully exposed" in clothing_state, "explicit nude pair should keep body exposure state")
    _expect("teaser outfit detail" not in clothing_state, "explicit nude pair should not repeat softcore outfit detail")
    partner_styling = pair.get("softcore_partner_styling") or {}
    _expect(partner_styling.get("outfits"), "same-cast pair should keep partner softcore outfit styling")
    _expect_text("insta_pair_same_cast.partner_pose", partner_styling.get("pose"), 12)


def smoke_krea_pair_clothing_state() -> None:
    forced_location = pb.build_location_pool_json(
        enabled=True,
        combine_mode="replace",
        preset="custom_only",
        custom_locations="closet_scene: full-length closet mirror with outfit racks, shoe shelves, and soft boutique lighting",
    )
    pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=3511,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(hardcore_clothing_continuity="partially_removed"),
        character_cast=_character_cast(),
        hardcore_position_config=_action_filter("penetration_only"),
        location_config=forced_location,
    )
    _expect_pair(pair, "krea_pair_clothing_state")
    typed_route = krea_pair_formatter.format_insta_pair_result(
        krea_pair_formatter.KreaPairFormatRequest(pair, "balanced", "preserve"),
        krea_formatter._krea_pair_format_dependencies(),
    )
    legacy_route = krea_formatter._insta_pair_to_krea(pair, "balanced", "preserve")
    _expect(
        typed_route.as_tuple() == legacy_route,
        "Typed Krea pair formatter route should match legacy wrapper output",
    )
    krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
    prompt = _expect_text("krea_pair_clothing_state.krea_prompt", krea.get("krea_prompt"), 60)
    lower = prompt.lower()
    root_clothing = _clean_key(pair.get("hardcore_clothing_state"))
    _expect(
        "below the hips" in root_clothing,
        "pair root clothing state lost lower-body removal wording",
    )
    _expect(pair.get("default_man_hardcore_clothing"), "pair root default man hardcore clothing is missing")
    _expect("metadata" in krea.get("method", ""), "pair clothing route did not use metadata")
    _expect("clothing state:" not in lower, "Krea clothing route leaked raw clothing label")
    _expect("visual clothing state" not in lower, "Krea clothing route fell back to visual clothing state label")
    _expect("softcore outfit" not in lower and "teaser outfit" not in lower, "Krea clothing route leaked softcore outfit label")
    _expect("below the hips" in lower, "Krea clothing route lost generated lower-body clothing continuity")
    _expect("the man keeps" in lower, "Krea clothing route lost partner clothing continuity")
    _expect("outfit racks" not in lower and "shoe shelves" not in lower, "Krea pair formatter leaked unsanitized hard scene")
    hard_scene = _clean_key(pair["hardcore_row"].get("scene_text"))
    _expect("outfit racks" not in hard_scene and "shoe shelves" not in hard_scene, "Pair builder leaked outfit-check scene clutter into hardcore row")
    _expect("mirror shelves" in hard_scene, "Pair builder lost neutralized scene anchor")
    caption_text, _caption_method = caption_naturalizer.naturalize_caption("", metadata_json=_json(pair), target="hardcore")
    caption_lower = caption_text.lower()
    _expect("outfit racks" not in caption_lower and "shoe shelves" not in caption_lower, "Caption pair formatter leaked unsanitized hard scene")
    sdxl = sdxl_formatter.format_sdxl_prompt("", metadata_json=_json(pair), target="hardcore")
    sdxl_lower = sdxl.get("sdxl_prompt", "").lower()
    _expect("outfit racks" not in sdxl_lower and "shoe shelves" not in sdxl_lower, "SDXL pair formatter leaked unsanitized hard scene")


def smoke_krea_anal_axis_compatibility() -> None:
    pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=6252,
        ethnicity="french_european",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(hardcore_clothing_continuity="partially_removed", camera_detail="off"),
        character_cast=_character_cast(),
        hardcore_position_config=_action_filter(
            "anal_only",
            pb.build_hardcore_position_pool_json(family="anal"),
        ),
        seed_config=pb.build_seed_lock_config_json(base_seed=6252, reroll_axis="pose", reroll_seed=6352),
    )
    hard_row = pair["hardcore_row"]
    axis_values = hard_row.get("item_axis_values") or {}
    position = str(axis_values.get("position") or "").lower()
    leg_detail = str(axis_values.get("leg_detail") or "").lower()
    if "side-lying" in position:
        _expect("standing" not in leg_detail, "Generated side-lying anal row leaked standing leg detail")
    krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
    prompt = _expect_text("krea_anal_axis_compatibility.krea_prompt", krea.get("krea_prompt"), 60)
    lower = prompt.lower()
    _expect(
        "side-lying rear-entry anal pose" not in lower or "stands braced" not in lower,
        "Krea anal formatter mixed side-lying anchor with standing role graph",
    )
    _expect("side-lying anal position, standing with legs braced" not in lower, "Krea anal formatter leaked contradictory axis detail")


def smoke_insta_pair_pov() -> None:
    pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=2201,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(),
        character_cast=_character_cast(pov_man=True),
        hardcore_position_config=_action_filter("oral_only"),
    )
    _expect_pair(pair, "insta_pair_pov_man")
    pov_labels = pair.get("pov_character_labels") or []
    _expect("Man A" in pov_labels, "pair POV labels should include Man A")
    hard_row = pair.get("hardcore_row") or {}
    _expect("Man A" in (hard_row.get("pov_character_labels") or []), "hard row POV labels should include Man A")
    pov_clothing = " ".join(pair.get("pov_hardcore_clothing") or []).lower()
    _expect("pov foreground clothing cue" in pov_clothing, "pair POV man should get foreground clothing metadata")
    _expect("viewer" in pov_clothing, "POV clothing metadata should be phrased through the viewer")
    _expect("man a wears" not in (pair.get("hardcore_clothing_state") or "").lower(), "POV clothing state should not describe Man A as visible")
    krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
    prompt = krea.get("krea_prompt") or ""
    prompt_lower = prompt.lower()
    _expect("viewer" in prompt_lower, "POV Krea prompt should mention viewer perspective")
    _expect("pov foreground clothing cue" in prompt_lower, "POV Krea prompt lost foreground clothing cue")


def smoke_insta_pair_camera_split() -> None:
    soft_camera = _orbit_camera(
        horizontal_angle=45,
        vertical_angle=-30,
        zoom=5.0,
        subject_focus="environment",
    )
    hard_camera = _orbit_camera(
        horizontal_angle=135,
        vertical_angle=30,
        zoom=8.0,
        subject_focus="action",
    )
    pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=2251,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(
            softcore_camera_mode="from_camera_config",
            hardcore_camera_mode="from_camera_config",
            camera_detail="compact",
        ),
        character_cast=_character_cast(),
        hardcore_position_config=_action_filter("penetration_only"),
        location_config=_coworking_location_config(),
        softcore_camera_config=soft_camera,
        hardcore_camera_config=hard_camera,
    )
    _expect_pair(pair, "insta_pair_camera_split")
    soft_scene = _expect_text("insta_pair_camera_split.soft_camera_scene", pair.get("softcore_camera_scene_directive"), 40)
    hard_scene = _expect_text("insta_pair_camera_split.hard_camera_scene", pair.get("hardcore_camera_scene_directive"), 40)
    _expect("front-right quarter view" in soft_scene, "soft camera scene missed soft orbit direction")
    _expect("back-right quarter view" in hard_scene, "hard camera scene missed hard orbit direction")
    _expect("low-angle shot" in soft_scene, "soft camera scene missed soft elevation")
    _expect("elevated shot" in hard_scene, "hard camera scene missed hard elevation")
    _expect("near desk edge" in soft_scene, "non-POV camera scene lost coworking foreground anchor")
    _expect("laptop corner" in soft_scene, "non-POV camera scene lost coworking foreground detail")
    _expect("front-right quarter view" in str(pair.get("softcore_camera_directive")), "soft pair camera directive was not preserved")
    _expect("back-right quarter view" in str(pair.get("hardcore_camera_directive")), "hard pair camera directive was not preserved")
    soft_row = pair.get("softcore_row") or {}
    hard_row = pair.get("hardcore_row") or {}
    _expect(pair.get("softcore_camera_config") == soft_row.get("camera_config"), "soft pair camera config drifted from soft row")
    _expect(pair.get("hardcore_camera_config") == hard_row.get("camera_config"), "hard pair camera config drifted from hard row")
    _expect(pair.get("softcore_camera_directive") == soft_row.get("camera_directive"), "soft pair camera directive drifted from soft row")
    _expect(pair.get("hardcore_camera_directive") == hard_row.get("camera_directive"), "hard pair camera directive drifted from hard row")
    _expect(pair.get("softcore_camera_scene_directive") == soft_row.get("camera_scene_directive"), "soft pair camera scene drifted from soft row")
    _expect(pair.get("hardcore_camera_scene_directive") == hard_row.get("camera_scene_directive"), "hard pair camera scene drifted from hard row")
    krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="auto")
    _expect("front-right quarter view" in (krea.get("krea_softcore_prompt") or ""), "Krea soft pair lost soft camera geometry")
    _expect("back-right quarter view" in (krea.get("krea_hardcore_prompt") or ""), "Krea hard pair lost hard camera geometry")


def smoke_pov_camera_scene() -> None:
    pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=2261,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(
            softcore_camera_mode="from_camera_config",
            hardcore_camera_mode="from_camera_config",
            camera_detail="compact",
        ),
        character_cast=_character_cast(pov_man=True),
        hardcore_position_config=_action_filter("oral_only"),
        location_config=_coworking_location_config(),
        hardcore_camera_config=_orbit_camera(
            horizontal_angle=135,
            vertical_angle=30,
            zoom=8.0,
            subject_focus="action",
        ),
    )
    _expect_pair(pair, "pov_camera_scene")
    hard_row = pair.get("hardcore_row") or {}
    _expect(not hard_row.get("camera_directive"), "POV hard row should suppress normal camera directive")
    scene_directive = _expect_text("pov_camera_scene.hard_camera_scene", hard_row.get("camera_scene_directive"), 40)
    _expect("from POV" in scene_directive, "POV camera scene should be marked as first-person")
    _expect(
        "context stays beside or behind the bodies and along the side/background edges" in scene_directive,
        "POV camera scene should place location anchors with positive wording",
    )
    _expect("not in the lower foreground" not in scene_directive, "POV camera scene should avoid negative foreground wording")
    _expect(
        "near desk edge" not in scene_directive and "laptop corner" not in scene_directive and "chair back" not in scene_directive,
        "POV camera scene should not reuse coworking foreground anchors as viewer-side objects",
    )
    _expect(
        "POV body or hand cues stay in the lower foreground" in scene_directive,
        "POV camera scene should keep first-person body cues in the lower foreground",
    )
    _expect("use the multiangle camera" not in scene_directive, "POV camera scene leaked internal multiangle instruction")
    krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
    prompt = krea.get("krea_prompt") or ""
    _expect("from POV" in prompt, "Krea POV prompt lost camera-scene directive")
    _expect("laptop corner" not in prompt, "Krea POV prompt leaked foreground desk anchor into camera scene")
    _expect("POV body or hand cues stay in the lower foreground" in prompt, "Krea POV prompt lost POV foreground cue")
    _expect("use the multiangle camera" not in prompt, "Krea POV prompt leaked internal multiangle instruction")
    _expect("Camera:" not in prompt, "Krea POV prompt should not emit normal third-person camera directive")


def smoke_krea2_pov_pose_variant_catalog() -> None:
    catalog_path = ROOT / "categories" / "krea2_pov_pose_variants.json"
    _expect(catalog_path.is_file(), "Krea2 POV pose-variant catalog is missing")
    catalog = json.loads(catalog_path.read_text(encoding="utf-8"))
    _expect(catalog.get("version") == 1, "Krea2 POV pose-variant catalog version changed unexpectedly")
    atlas_root = Path(_expect_text("krea2_pov_pose_variant_catalog.atlas_root", catalog.get("atlas_root"), 20))
    variants = catalog.get("variants")
    _expect(isinstance(variants, list) and variants, "Krea2 POV pose-variant catalog has no variants")
    statuses = set((catalog.get("status_values") or {}).keys())
    _expect({"proven", "candidate", "unstable"}.issubset(statuses), "Krea2 POV pose-variant statuses are incomplete")
    seen_keys: set[str] = set()
    required_keys = {
        "pov_doggy_top_down_rear_entry",
        "pov_boobjob_upright_cleavage",
        "pov_handjob_upright_centered",
    }
    for variant in variants:
        key = _expect_text("krea2_pov_pose_variant_catalog.key", variant.get("key"), 8)
        _expect(key not in seen_keys, f"Krea2 POV pose-variant catalog has duplicate key {key!r}")
        seen_keys.add(key)
        _expect(variant.get("status") in statuses, f"{key} has unknown status {variant.get('status')!r}")
        _expect_text(f"{key}.family", variant.get("family"), 3)
        _expect_text(f"{key}.canonical_geometry", variant.get("canonical_geometry"), 80)
        prompt_cues = variant.get("prompt_cues")
        avoid_cues = variant.get("avoid_cues")
        refs = variant.get("reference_images")
        _expect(isinstance(prompt_cues, list) and prompt_cues, f"{key} has no prompt cues")
        _expect(isinstance(avoid_cues, list) and avoid_cues, f"{key} has no avoid cues")
        prompt_variant_cues = variant.get("prompt_variant_cues", [])
        _expect(isinstance(prompt_variant_cues, list), f"{key} prompt variant cue sets should be a list when present")
        all_cue_sets = [prompt_cues, *prompt_variant_cues]
        for cue_set_index, cue_set in enumerate(all_cue_sets):
            _expect(isinstance(cue_set, list) and cue_set, f"{key} prompt cue set {cue_set_index} is empty")
            for cue in cue_set:
                cue_text = _expect_text(f"{key}.prompt_cue[{cue_set_index}]", cue, 8)
                _expect(
                    not re.search(r"\b(?:may|optionally|either|or)\b", cue_text, flags=re.IGNORECASE),
                    f"{key} prompt cue should be a direct model instruction, not an option list: {cue_text!r}",
                )
        _expect(isinstance(refs, list) and refs, f"{key} has no reference images")
        hook = variant.get("generator_hook") or {}
        _expect_text(f"{key}.generator_hook.module", hook.get("module"), 6)
        _expect(isinstance(hook.get("route_terms"), list) and hook["route_terms"], f"{key} has no route terms")
        for folder in variant.get("atlas_folders") or []:
            _expect("bg" not in str(folder).lower(), f"{key} should not use background-only folder {folder!r}")
        for ref in refs:
            ref_text = _expect_text(f"{key}.reference_image", ref, 8)
            _expect(".." not in Path(ref_text).parts, f"{key} reference escapes atlas root: {ref_text!r}")
            _expect("bg" not in ref_text.lower(), f"{key} should not use background-only reference {ref_text!r}")
            if atlas_root.exists():
                _expect((atlas_root / ref_text).is_file(), f"{key} reference image is missing: {atlas_root / ref_text}")
        if variant.get("status") == "proven":
            evidence = variant.get("evidence") or {}
            _expect(evidence.get("fixed_seed_tests"), f"{key} is proven but has no fixed-seed evidence")
            _expect_text(f"{key}.guide_section", evidence.get("guide_section"), 10)
    _expect(required_keys.issubset(seen_keys), "Krea2 POV pose-variant catalog lost a proven starter variant")


def _atlas_variant_include_key(variant_key: str) -> str:
    key = "".join(char if char.isalnum() else "_" for char in str(variant_key).lower().removeprefix("pov_")).strip("_")
    while "__" in key:
        key = key.replace("__", "_")
    return f"include_{key}"


def smoke_krea2_pov_atlas_variant_prompt_routes() -> None:
    filter_by_action_family = {
        "penetration": "SxCPKrea2POVPenetrationFilter",
        "oral": "SxCPKrea2POVOralFilter",
        "outercourse": "SxCPKrea2POVOutercourseFilter",
        "manual": "SxCPKrea2POVManualFilter",
        "toy": "SxCPKrea2POVToyFilter",
        "climax": "SxCPKrea2POVClimaxFilter",
        "interaction": "SxCPKrea2POVInteractionFilter",
    }
    variants = krea2_pose_variant_catalog.variants()
    _expect(variants, "Krea2 POV atlas prompt route smoke found no variants")
    for offset, variant in enumerate(variants, start=4510):
        key = _expect_text("krea2_pov_atlas_variant_prompt_routes.key", variant.get("key"), 8)
        action_family = _expect_text(f"{key}.action_family", variant.get("action_family"), 3)
        node_name = filter_by_action_family.get(action_family)
        _expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{key} has no Krea2 POV filter node for action family {action_family!r}")
        include_key = _atlas_variant_include_key(key)
        node_cls = sxcp_nodes.NODE_CLASS_MAPPINGS[node_name]
        _expect(include_key in (node_cls.INPUT_TYPES().get("required") or {}), f"{node_name} does not expose {include_key}")
        variant_config = node_cls().build("replace", "", **{include_key: True})[0]
        pair = pb.build_insta_of_pair(
            row_number=1,
            start_index=1,
            seed=offset,
            ethnicity="any",
            figure="random",
            no_plus_women=False,
            no_black=False,
            trigger=Trigger,
            prepend_trigger_to_prompt=True,
            options_json=_insta_options(
                softcore_camera_mode="from_camera_config",
                hardcore_camera_mode="from_camera_config",
                camera_detail="compact",
            ),
            character_cast=_character_cast(pov_man=True),
            hardcore_position_config=variant_config,
            location_config=_coworking_location_config(),
            hardcore_camera_config=_orbit_camera(
                horizontal_angle=45,
                vertical_angle=0,
                zoom=7.5,
                subject_focus="action",
            ),
        )
        _expect_pair(pair, f"krea2_pov_atlas_variant_prompt_routes.{key}")
        hard_row = pair.get("hardcore_row") or {}
        variant_keys = (hard_row.get("hardcore_position_config") or {}).get("krea2_variant_keys") or []
        _expect(variant_keys == [key], f"{key} row lost exact variant metadata: {variant_keys}")
        krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
        prompt = _expect_text(f"{key}.krea_prompt", krea.get("krea_prompt"), 80).lower()
        hard_axis_values = hard_row.get("item_axis_values") if isinstance(hard_row.get("item_axis_values"), dict) else {}
        variant_indices = hard_axis_values.get("krea2_prompt_variant_indices") if isinstance(hard_axis_values, dict) else {}
        selected_index = 0
        if isinstance(variant_indices, dict):
            try:
                selected_index = int(variant_indices.get(key, 0))
            except (TypeError, ValueError):
                selected_index = 0
        cue_sets = krea2_pose_variant_catalog.prompt_cue_sets(variant)
        _expect(0 <= selected_index < len(cue_sets), f"{key} selected invalid prompt variant index {selected_index}")
        for cue in cue_sets[selected_index]:
            cue_text = _expect_text(f"{key}.prompt_cue", cue, 8).lower()
            _expect(cue_text in prompt, f"{key} final Krea prompt lost atlas cue {cue_text!r}: {prompt}")
        atlas_action_prompt = prompt.split(" camera is ", 1)[0]
        _expect(
            not re.search(r"\b(?:may|optionally|either|or)\b", atlas_action_prompt, flags=re.IGNORECASE),
            f"{key} final Krea prompt kept optional atlas action wording: {prompt}",
        )
        _expect(
            "; " not in atlas_action_prompt,
            f"{key} final Krea prompt kept semicolon-delimited atlas cue formatting: {prompt}",
        )
        _expect(
            "camera layout" not in prompt
            and "keep the visible partner" not in prompt
            and "context stays beside" not in prompt
            and "pov body or hand cues stay" not in prompt,
            f"{key} final Krea prompt kept generic camera-scene layout prose: {prompt}",
        )
        _expect(
            "hands or body cues" not in prompt and "perspective or foreground cues" not in prompt,
            f"{key} final Krea prompt kept option wording in POV camera phrase: {prompt}",
        )
        _expect(
            "framed as " not in prompt and "the image is framed as " not in prompt,
            f"{key} final Krea prompt kept generic composition text after atlas route: {prompt}",
        )
        for forbidden in (
            "pov foreground clothing cue",
            "body is fully exposed",
            "explicit consensual",
            "use clear adult anatomy",
        ):
            _expect(forbidden not in prompt, f"{key} final Krea prompt kept generic formatter layer {forbidden!r}: {prompt}")
        atlas_geometry = " ".join([str(variant.get("canonical_geometry") or ""), *[str(cue) for cue in variant.get("prompt_cues") or []]]).lower()
        if any(term in atlas_geometry for term in ("top-down", "top view", "top-view", "nadir", "overhead")):
            _expect("eye-level shot" not in prompt, f"{key} final Krea prompt kept contradictory eye-level camera text: {prompt}")
        if key == "pov_blowjob_top_down_vertical_shaft":
            _expect(
                prompt.count("nadir-angle standing male pov top-view oral") == 1,
                f"{key} final Krea prompt repeated top-view atlas header instead of staying compact: {prompt}",
            )
            for forbidden in (
                "the woman takes the viewer's penis",
                "visible saliva",
                "cum on",
                "hands holding hips",
                "eyes looking up",
            ):
                _expect(forbidden not in prompt, f"{key} final Krea prompt kept generic detail fragment {forbidden!r}: {prompt}")
            for required in (
                "the woman kneels directly below the viewer between his feet",
                "mouth seals around the centered shaft",
                "one hand wraps the base",
            ):
                _expect(required in prompt, f"{key} final Krea prompt lost compact contact cue {required!r}: {prompt}")
            _expect("one woman" not in prompt, f"{key} final Krea prompt split the subject with 'one woman': {prompt}")
        for avoid in variant.get("avoid_cues") or []:
            avoid_text = _expect_text(f"{key}.avoid_cue", avoid, 4).lower()
            _expect(avoid_text not in prompt, f"{key} final Krea prompt leaked avoid cue {avoid_text!r}: {prompt}")


def smoke_krea2_pose_variant_catalog_policy() -> None:
    catalog = krea2_pose_variant_catalog.load_catalog()
    _expect(catalog.get("version") == 1, "Krea2 pose-variant loader returned wrong catalog")
    synthetic_variant = {
        "key": "pov_synthetic_seeded_variant",
        "prompt_cues": ["synthetic baseline atlas cue", "synthetic baseline contact anchor"],
        "prompt_variant_cues": [
            ["synthetic alternate atlas cue", "synthetic alternate contact anchor"],
            {"prompt_cues": ["synthetic second alternate cue", "synthetic second contact anchor"]},
        ],
    }
    _expect(
        krea2_pose_variant_catalog.prompt_cue_sets(synthetic_variant) == [
            ["synthetic baseline atlas cue", "synthetic baseline contact anchor"],
            ["synthetic alternate atlas cue", "synthetic alternate contact anchor"],
            ["synthetic second alternate cue", "synthetic second contact anchor"],
        ],
        "Krea2 pose-variant catalog should expose baseline and optional prompt variant cue sets",
    )
    original_get_variant = krea2_pose_variant_catalog.get_variant
    try:
        def fake_get_variant(key: str, **kwargs):
            if key == "pov_synthetic_seeded_variant":
                return synthetic_variant
            return original_get_variant(key, **kwargs)

        krea2_pose_variant_catalog.get_variant = fake_get_variant
        baseline_sentence = krea_pov_actions._krea2_atlas_variant_sentence(
            {
                "krea2_variant_keys": ["pov_synthetic_seeded_variant"],
                "krea2_prompt_variant_indices": {"pov_synthetic_seeded_variant": 0},
            }
        )
        alternate_sentence = krea_pov_actions._krea2_atlas_variant_sentence(
            {
                "krea2_variant_keys": ["pov_synthetic_seeded_variant"],
                "krea2_prompt_variant_indices": {"pov_synthetic_seeded_variant": 1},
            }
        )
        _expect("synthetic baseline atlas cue" in baseline_sentence, "Krea2 atlas sentence lost baseline prompt cue set")
        _expect("synthetic alternate atlas cue" in alternate_sentence, "Krea2 atlas sentence lost selected prompt variant cue set")
        selected_indices: set[int] = set()
        for seed in range(4510, 4560):
            axis_values = pb._axis_values_with_krea2_prompt_variant_indices(
                {"krea2_variant_keys": ["pov_synthetic_seeded_variant"]},
                seed_config={},
                seed=seed,
                row_number=1,
            )
            selected_indices.add(int(axis_values.get("krea2_prompt_variant_indices", {}).get("pov_synthetic_seeded_variant", 0)))
        _expect(
            len(selected_indices) >= 2,
            f"Krea2 prompt variant selector should vary across pose seeds, got {sorted(selected_indices)}",
        )
        preselected_axis_values = pb._axis_values_with_krea2_variant_keys(
            {},
            {
                "krea2_variant_keys": ["pov_synthetic_seeded_variant"],
                "krea2_prompt_variant_indices": {"pov_synthetic_seeded_variant": 1},
            },
        )
        _expect(
            preselected_axis_values.get("krea2_prompt_variant_indices") == {"pov_synthetic_seeded_variant": 1},
            "Krea2 variant-key merge should preserve node-selected prompt variant indices",
        )
        generated_axis_values = pb._axis_values_with_krea2_prompt_variant_indices(
            preselected_axis_values,
            seed_config={},
            seed=4510,
            row_number=1,
        )
        _expect(
            generated_axis_values.get("krea2_prompt_variant_indices") == {"pov_synthetic_seeded_variant": 1},
            "Pose-seed prompt variant selector should not overwrite node-selected atlas cue indices",
        )
    finally:
        krea2_pose_variant_catalog.get_variant = original_get_variant

    proven = krea2_pose_variant_catalog.variant_keys(status="proven")
    _expect(
        proven == [
            "pov_doggy_top_down_rear_entry",
            "pov_boobjob_upright_cleavage",
            "pov_handjob_upright_centered",
            "pov_footjob_frontal_sole_stroke",
            "pov_wand_foreground_tool_contact",
            "pov_blowjob_side_profile_oral",
            "pov_cowgirl_frontal_straddle_penetration",
        ],
        f"Krea2 pose-variant proven keys changed unexpectedly: {proven}",
    )
    penetration = krea2_pose_variant_catalog.variant_keys(action_family="penetration")
    _expect(
        penetration == [
            "pov_doggy_top_down_rear_entry",
            "pov_missionary_open_leg_penetration",
            "pov_missionary_folded_high_leg_penetration",
            "pov_cowgirl_frontal_straddle_penetration",
            "pov_cowgirl_alt_low_squat_penetration",
            "pov_reverse_cowgirl_back_facing_penetration",
            "pov_reverse_cowgirl_alt_upright_back_facing_penetration",
        ],
        f"Krea2 pose-variant penetration filtering changed unexpectedly: {penetration}",
    )
    outercourse = krea2_pose_variant_catalog.variant_keys(action_family="outercourse")
    _expect(
        outercourse == [
            "pov_boobjob_upright_cleavage",
            "pov_handjob_upright_centered",
            "pov_ballsucking_low_head",
            "pov_footjob_frontal_sole_stroke",
        ],
        f"Krea2 pose-variant outercourse filtering changed unexpectedly: {outercourse}",
    )
    manual = krea2_pose_variant_catalog.variant_keys(action_family="manual")
    _expect(
        manual == ["pov_fingering_reclined_open_thighs"],
        f"Krea2 pose-variant manual filtering changed unexpectedly: {manual}",
    )
    toy = krea2_pose_variant_catalog.variant_keys(action_family="toy")
    _expect(
        toy == ["pov_wand_foreground_tool_contact"],
        f"Krea2 pose-variant toy filtering changed unexpectedly: {toy}",
    )
    climax = krea2_pose_variant_catalog.variant_keys(action_family="climax")
    _expect(
        climax == ["pov_ejaculation_aftermath_open_thigh_candidate"],
        f"Krea2 pose-variant climax filtering changed unexpectedly: {climax}",
    )
    interaction = krea2_pose_variant_catalog.variant_keys(action_family="interaction")
    _expect(
        interaction == ["pov_spread_open_thigh_presentation"],
        f"Krea2 pose-variant interaction filtering changed unexpectedly: {interaction}",
    )
    oral = krea2_pose_variant_catalog.variant_keys(action_family="oral")
    _expect(
        oral == [
            "pov_sixty_nine_close_reversed_oral",
            "pov_blowjob_top_down_vertical_shaft",
            "pov_blowjob_side_profile_oral",
            "pov_blowjob_laying_frontal_oral",
            "pov_blowjob_sitting_upright_oral",
        ],
        f"Krea2 pose-variant oral filtering changed unexpectedly: {oral}",
    )
    handjob = krea2_pose_variant_catalog.get_variant("pov_handjob_upright_centered")
    _expect(
        any("woman's right hand wraps" in str(cue) for cue in handjob.get("prompt_cues", [])),
        "Handjob variant lost hand ownership cue",
    )
    handjob["prompt_cues"].append("mutation should not leak")
    clean_handjob = krea2_pose_variant_catalog.get_variant("pov_handjob_upright_centered")
    _expect("mutation should not leak" not in clean_handjob.get("prompt_cues", []), "Catalog loader leaked caller mutation")
    ballsucking = krea2_pose_variant_catalog.get_variant("pov_ballsucking_low_head")
    _expect(
        any("scrotum is the mouth surface" in str(cue) for cue in ballsucking.get("prompt_cues", [])),
        "Ballsucking variant lost open-lips scrotum-surface cue",
    )
    _expect(
        any("scrotal skin is the nearest mouth surface" in str(cue) for cue in ballsucking.get("prompt_cues", [])),
        "Ballsucking variant lost scrotal-skin nearest-surface cue",
    )
    ballsucking_evidence = ballsucking.get("evidence") or {}
    _expect(
        ballsucking_evidence.get("fixed_seed_tests") == [
            "238365845574312",
            "1212121212",
            "5757575757",
            "6262626262",
            "9797979797",
            "9898989898",
            "5959595959",
            "6060606060",
            "6161616161",
            "7171717171",
            "7272727272",
        ],
        "Ballsucking variant lost fresh-seed target-object evidence",
    )
    footjob = krea2_pose_variant_catalog.get_variant("pov_footjob_frontal_sole_stroke")
    _expect(footjob.get("status") == "proven", "Footjob variant should be proven after fresh-seed generated-route repeat evidence")
    _expect(
        any("two large overlapping soles dominate the lower center foreground" in str(cue) for cue in footjob.get("prompt_cues", [])),
        "Footjob variant lost sole-contact cue",
    )
    _expect(
        any("narrow visible strip of shaft and glans rises between the compressed feet" in str(cue) for cue in footjob.get("prompt_cues", [])),
        "Footjob variant lost visible-glans contact cue",
    )
    fingering = krea2_pose_variant_catalog.get_variant("pov_fingering_reclined_open_thighs")
    _expect(fingering.get("status") == "candidate", "Fingering variant should remain a candidate until fixed-seed evidence exists")
    _expect(
        any("viewer hand enters from the foreground" in str(cue) for cue in fingering.get("prompt_cues", [])),
        "Fingering variant lost foreground-hand cue",
    )
    wand = krea2_pose_variant_catalog.get_variant("pov_wand_foreground_tool_contact")
    _expect(wand.get("status") == "proven", "Wand variant should be proven after repeated generated-route evidence")
    _expect(
        any("viewer hand holds a wand-style toy from the foreground" in str(cue) for cue in wand.get("prompt_cues", [])),
        "Wand variant lost foreground tool-hold cue",
    )
    ready = krea2_pose_variant_catalog.get_variant("pov_ejaculation_aftermath_open_thigh_candidate")
    _expect(ready.get("status") == "candidate", "Ready aftermath variant should remain a candidate until fixed-seed evidence exists")
    _expect(
        any("thick semen and clear fluid are visible around the exposed pussy and anal opening" in str(cue) for cue in ready.get("prompt_cues", [])),
        "Ready aftermath variant lost explicit post-ejaculation fluid/opening cue",
    )
    _expect(
        any("generic ready/setup pose before sex" in str(cue) for cue in ready.get("avoid_cues", [])),
        "Ready aftermath variant should reject generic ready/setup wording",
    )
    sixty_nine = krea2_pose_variant_catalog.get_variant("pov_sixty_nine_close_reversed_oral")
    _expect(sixty_nine.get("status") == "unstable", "Sixty-nine route should stay unstable until text-only evidence improves")
    _expect(sixty_nine.get("difficulty") == "hardest", "Sixty-nine route should be marked as the hardest atlas route")
    _expect(sixty_nine.get("priority") == "low", "Sixty-nine route should be marked low priority")
    _expect(
        sixty_nine.get("control_requirement") == "pose_or_image_guidance_first",
        "Sixty-nine route should require pose/image guidance before prompt-only tuning",
    )
    _expect(
        any("visible partner is reversed over the viewer with hips closest" in str(cue) for cue in sixty_nine.get("prompt_cues", [])),
        "Sixty-nine variant lost reversed-over-viewer cue",
    )
    spread = krea2_pose_variant_catalog.get_variant("pov_spread_open_thigh_presentation")
    _expect(spread.get("status") == "candidate", "Spread variant should remain a candidate until fixed-seed evidence exists")
    _expect(
        any("legs raised and knees held wide" in str(cue) for cue in spread.get("prompt_cues", [])),
        "Spread variant lost open-thigh cue",
    )
    oral_top = krea2_pose_variant_catalog.get_variant("pov_blowjob_top_down_vertical_shaft")
    _expect(oral_top.get("status") == "candidate", "Blowjob top-view variant should remain a candidate until fixed-seed evidence exists")
    _expect(
        any("nadir-angle standing male POV top-view oral position" in str(cue) for cue in oral_top.get("prompt_cues", [])),
        "Blowjob top-view variant lost nadir-angle cue",
    )
    _expect("kneeling" in (oral_top.get("position_keys") or []), "Blowjob top-view variant lost kneeling route key")
    oral_top_evidence = oral_top.get("evidence") or {}
    _expect(
        oral_top_evidence.get("fixed_seed_tests") == ["4242424242"],
        "Blowjob top-view variant lost fixed-seed evidence",
    )
    _expect(
        "blowjob-top-view--overhead-vertical-shaft" in str(oral_top_evidence.get("guide_section") or ""),
        "Blowjob top-view variant lost guide-section link",
    )
    _expect(
        "nadir-angle standing male POV" in str(oral_top_evidence.get("notes") or ""),
        "Blowjob top-view variant lost nadir-angle evidence note",
    )
    oral_side = krea2_pose_variant_catalog.get_variant("pov_blowjob_side_profile_oral")
    _expect(oral_side.get("status") == "proven", "Blowjob side variant should be proven after fresh-seed three-woman generated-route repeat evidence")
    _expect(
        any("woman enters laterally from the left edge beside his hip" in str(cue) for cue in oral_side.get("prompt_cues", [])),
        "Blowjob side variant lost lateral-edge body-line cue",
    )
    _expect(
        any("mouth-to-shaft contact is the nearest facial detail" in str(cue) for cue in oral_side.get("prompt_cues", [])),
        "Blowjob side variant lost generated-route contact-priority cue",
    )
    _expect(
        any("adult male viewer's own torso starts at the lower edge" in str(cue) for cue in oral_side.get("prompt_cues", [])),
        "Blowjob side variant lost lower-right self-torso anchor cue",
    )
    oral_side_evidence = oral_side.get("evidence") or {}
    _expect(
        oral_side_evidence.get("fixed_seed_tests") == ["5656565656", "9753197531", "9595959595", "9696969696", "5858585858"],
        "Blowjob side variant lost fixed-seed evidence",
    )
    _expect(
        "blowjob-side-profile--side-phone-weak-case" in str(oral_side_evidence.get("guide_section") or ""),
        "Blowjob side variant lost weak-case guide-section link",
    )
    _expect(
        "explicit adult-male foreground ownership" in str(oral_side_evidence.get("notes") or ""),
        "Blowjob side variant lost male-body ownership evidence note",
    )
    _expect(
        "transferring the central body surface to the woman" in str(oral_side_evidence.get("notes") or ""),
        "Blowjob side variant lost source-47 body-axis failure note",
    )
    _expect(
        "9753197531" in str(oral_side_evidence.get("notes") or "") and "lateral-edge body-line" in str(oral_side_evidence.get("notes") or ""),
        "Blowjob side variant lost repeated lateral-edge evidence note",
    )
    _expect(
        "turn 207" in str(oral_side_evidence.get("notes") or "") and "mouth-to-shaft-contact priority" in str(oral_side_evidence.get("notes") or ""),
        "Blowjob side variant lost generated-route contact-priority note",
    )
    _expect(
        "9595959595" in str(oral_side_evidence.get("notes") or "") and "lower-right torso anchor" in str(oral_side_evidence.get("notes") or ""),
        "Blowjob side variant lost lower-right torso repeat evidence note",
    )
    _expect(
        "9696969696" in str(oral_side_evidence.get("notes") or "") and "generated-route validation" in str(oral_side_evidence.get("notes") or ""),
        "Blowjob side variant lost patched generated-route validation note",
    )
    _expect(
        "5858585858" in str(oral_side_evidence.get("notes") or "") and "three-woman generated-route repeat" in str(oral_side_evidence.get("notes") or ""),
        "Blowjob side variant lost fresh-seed promotion note",
    )
    oral_laying = krea2_pose_variant_catalog.get_variant("pov_blowjob_laying_frontal_oral")
    _expect(oral_laying.get("status") == "candidate", "Blowjob laying variant should remain a candidate until repeated evidence exists")
    _expect(
        any("woman lies belly-down between the viewer's open thighs" in str(cue) for cue in oral_laying.get("prompt_cues", [])),
        "Blowjob laying variant lost prone frontal cue",
    )
    oral_laying_evidence = oral_laying.get("evidence") or {}
    _expect(
        oral_laying_evidence.get("fixed_seed_tests") == ["6767676767"],
        "Blowjob laying variant lost fixed-seed evidence",
    )
    _expect(
        "blowjob-laying-frontal--wide-v-frame" in str(oral_laying_evidence.get("guide_section") or ""),
        "Blowjob laying variant lost guide-section link",
    )
    _expect(
        "wide symmetrical V-frame" in str(oral_laying_evidence.get("notes") or ""),
        "Blowjob laying variant lost V-frame evidence note",
    )
    oral_sitting = krea2_pose_variant_catalog.get_variant("pov_blowjob_sitting_upright_oral")
    _expect(oral_sitting.get("status") == "candidate", "Blowjob sitting variant should remain a candidate until fixed-seed evidence exists")
    _expect(
        any("woman sits low between the viewer's open thighs" in str(cue) for cue in oral_sitting.get("prompt_cues", [])),
        "Blowjob sitting variant lost low seated cue",
    )
    _expect("blowjob_sitting" in (oral_sitting.get("position_keys") or []), "Blowjob sitting variant lost route key")
    oral_sitting_evidence = oral_sitting.get("evidence") or {}
    _expect(
        oral_sitting_evidence.get("fixed_seed_tests") == ["7878787878"],
        "Blowjob sitting variant lost fixed-seed evidence",
    )
    _expect(
        "blowjob-sitting-upright--low-mouth-contact" in str(oral_sitting_evidence.get("guide_section") or ""),
        "Blowjob sitting variant lost guide-section link",
    )
    _expect(
        "low-mouth seated hierarchy" in str(oral_sitting_evidence.get("notes") or ""),
        "Blowjob sitting variant lost low-mouth evidence note",
    )
    missionary = krea2_pose_variant_catalog.get_variant("pov_missionary_open_leg_penetration")
    _expect(missionary.get("status") == "candidate", "Missionary variant should remain a candidate until fixed-seed evidence exists")
    _expect(
        any("woman reclines on her back with knees open toward the viewer" in str(cue) for cue in missionary.get("prompt_cues", [])),
        "Missionary variant lost open-leg reclined cue",
    )
    _expect(
        any("viewer braced at the foot edge" in str(cue) for cue in missionary.get("prompt_cues", [])),
        "Missionary variant lost elevated-support edge cue",
    )
    missionary_evidence = missionary.get("evidence") or {}
    _expect(
        missionary_evidence.get("fixed_seed_tests") == ["8989898989"],
        "Missionary variant lost fixed-seed evidence",
    )
    _expect(
        "edge-supported route" in str(missionary_evidence.get("notes") or ""),
        "Missionary variant lost edge-supported patch boundary",
    )
    missionary_folded = krea2_pose_variant_catalog.get_variant("pov_missionary_folded_high_leg_penetration")
    _expect(missionary_folded.get("status") == "candidate", "Folded missionary variant should remain a candidate until repeated evidence exists")
    _expect(
        any("woman reclines on her back with knees folded high toward her chest" in str(cue) for cue in missionary_folded.get("prompt_cues", [])),
        "Folded missionary variant lost high-leg folded cue",
    )
    _expect(
        any("large centered shaft" in str(cue) for cue in missionary_folded.get("prompt_cues", [])),
        "Folded missionary variant lost contact-first shaft cue",
    )
    missionary_folded_evidence = missionary_folded.get("evidence") or {}
    _expect(
        missionary_folded_evidence.get("fixed_seed_tests") == ["8989898989"],
        "Folded missionary variant lost fixed-seed evidence",
    )
    _expect(
        "contact before the compact folded-knee block" in str(missionary_folded_evidence.get("notes") or ""),
        "Folded missionary variant lost contact-first evidence note",
    )
    cowgirl = krea2_pose_variant_catalog.get_variant("pov_cowgirl_frontal_straddle_penetration")
    _expect(cowgirl.get("status") == "proven", "Cowgirl variant should be proven after fresh-seed generated-route repeat evidence")
    _expect(
        any("woman straddles the viewer facing him" in str(cue) for cue in cowgirl.get("prompt_cues", [])),
        "Cowgirl variant lost frontal straddle cue",
    )
    _expect(
        any("wide horizontal thigh bridge" in str(cue) for cue in cowgirl.get("prompt_cues", [])),
        "Cowgirl variant lost wide thigh bridge cue",
    )
    cowgirl_evidence = cowgirl.get("evidence") or {}
    _expect(
        cowgirl_evidence.get("fixed_seed_tests") == ["8989898989", "2828282828", "9191919191"],
        "Cowgirl variant lost fixed-seed evidence",
    )
    _expect(
        "baseline already valid" in str(cowgirl_evidence.get("notes") or ""),
        "Cowgirl variant lost baseline-valid evidence note",
    )
    _expect(
        "9191919191" in str(cowgirl_evidence.get("notes") or "") and "turns 242" in str(cowgirl_evidence.get("notes") or ""),
        "Cowgirl variant lost fresh-seed proven evidence note",
    )
    cowgirl_alt = krea2_pose_variant_catalog.get_variant("pov_cowgirl_alt_low_squat_penetration")
    _expect(cowgirl_alt.get("status") == "candidate", "Cowgirl alt variant should remain a candidate until repeated evidence exists")
    _expect(
        any("low seated squat over the viewer's pelvis" in str(cue) for cue in cowgirl_alt.get("prompt_cues", [])),
        "Cowgirl alt variant lost low seated squat cue",
    )
    _expect(
        any("viewer lies flat on his back" in str(cue) for cue in cowgirl_alt.get("prompt_cues", [])),
        "Cowgirl alt variant lost flat-supine viewer cue",
    )
    cowgirl_alt_evidence = cowgirl_alt.get("evidence") or {}
    _expect(
        cowgirl_alt_evidence.get("fixed_seed_tests") == ["8989898989"],
        "Cowgirl alt variant lost fixed-seed evidence",
    )
    _expect(
        "ceiling and upper glass" in str(cowgirl_alt_evidence.get("notes") or ""),
        "Cowgirl alt variant lost spatial-orientation evidence note",
    )
    reverse_cowgirl = krea2_pose_variant_catalog.get_variant("pov_reverse_cowgirl_back_facing_penetration")
    _expect(reverse_cowgirl.get("status") == "candidate", "Reverse cowgirl variant should remain a candidate until fixed-seed evidence exists")
    _expect(
        any("woman faces away from the viewer in a back-facing straddle" in str(cue) for cue in reverse_cowgirl.get("prompt_cues", [])),
        "Reverse cowgirl variant lost back-facing straddle cue",
    )
    reverse_cowgirl_alt = krea2_pose_variant_catalog.get_variant("pov_reverse_cowgirl_alt_upright_back_facing_penetration")
    _expect(reverse_cowgirl_alt.get("status") == "candidate", "Reverse cowgirl alt variant should remain a candidate until fixed-seed evidence exists")
    _expect(
        any("woman sits upright facing away from the viewer in a back-facing straddle" in str(cue) for cue in reverse_cowgirl_alt.get("prompt_cues", [])),
        "Reverse cowgirl alt variant lost upright back-facing cue",
    )
    refs = krea2_pose_variant_catalog.reference_paths("pov_boobjob_upright_cleavage")
    _expect(refs and all(path.name.endswith(".png") for path in refs), "Boobjob reference paths are not image paths")
    _expect(all("bg" not in str(path).lower() for path in refs), "Reference paths should not include background-only atlas images")
    missing = krea2_pose_variant_catalog.get_variant("missing_pose_variant")
    _expect(missing == {}, "Missing pose variant should return an empty mapping")


def smoke_krea2_eval_log_policy() -> None:
    log = krea2_eval_log.load_eval_log()
    _expect(log.get("version") == 1, "Krea2 eval log version changed unexpectedly")
    _expect("external ComfyUI artifacts" in str(log.get("artifact_policy") or ""), "Krea2 eval log should document external artifact policy")
    entries = krea2_eval_log.entries()
    _expect(entries, "Krea2 eval log has no entries")
    catalog_keys = set(krea2_pose_variant_catalog.variant_keys())
    proven_keys = set(krea2_pose_variant_catalog.variant_keys(status="proven"))
    accepted_keys = set(krea2_eval_log.variant_keys(result="accepted"))
    _expect(proven_keys.issubset(accepted_keys), "Krea2 eval log does not cover every proven pose variant")
    seen_ids: set[str] = set()
    for entry in entries:
        entry_id = _expect_text("krea2_eval_log.entry.id", entry.get("id"), 6)
        _expect(entry_id not in seen_ids, f"Krea2 eval log has duplicate entry id {entry_id!r}")
        seen_ids.add(entry_id)
        variant_key = _expect_text(f"{entry_id}.variant_key", entry.get("variant_key"), 8)
        _expect(variant_key in catalog_keys, f"{entry_id} references unknown variant {variant_key!r}")
        _expect(isinstance(entry.get("seed"), int), f"{entry_id} has no integer fixed seed")
        _expect(entry.get("result") in {"accepted", "rejected", "inconclusive"}, f"{entry_id} has unknown result")
        _expect(
            entry.get("decision") in {
                "generator_patch",
                "provisional_generator_patch",
                "proven_with_evidence",
                "prompt_guide_rule",
                "prompt_only_retry",
                "needs_more_tests",
            },
            f"{entry_id} has unknown decision",
        )
        _expect_text(f"{entry_id}.baseline_prompt_summary", entry.get("baseline_prompt_summary"), 20)
        _expect_text(f"{entry_id}.candidate_prompt_summary", entry.get("candidate_prompt_summary"), 20)
        _expect_text(f"{entry_id}.observation", entry.get("observation"), 30)
        for image_key in ("baseline_image", "candidate_image"):
            image_path = str(entry.get(image_key) or "")
            if image_path:
                _expect(Path(image_path).is_absolute(), f"{entry_id}.{image_key} should be absolute when present")
                _expect(Path(image_path).suffix.lower() == ".png", f"{entry_id}.{image_key} should reference a PNG artifact")
    boobjob_entries = krea2_eval_log.entries_for_variant("pov_boobjob_upright_cleavage", result="accepted")
    _expect(boobjob_entries and boobjob_entries[0].get("seed") == 7302, "Boobjob accepted eval evidence changed")
    mutation = krea2_eval_log.entries_for_variant("pov_handjob_upright_centered")[0]
    mutation["observation"] = "mutation should not leak"
    clean = krea2_eval_log.entries_for_variant("pov_handjob_upright_centered")[0]
    _expect(clean.get("observation") != "mutation should not leak", "Krea2 eval log leaked caller mutation")
    with tempfile.TemporaryDirectory() as tmpdir:
        temp_log_path = Path(tmpdir) / "krea2-eval-log.json"
        temp_log_path.write_text(json.dumps(log, ensure_ascii=True, indent=2) + "\n", encoding="utf-8")
        smoke_entry = {
            "id": "ballsucking-9001-low-head-smoke",
            "date": "2026-06-29",
            "variant_key": "pov_ballsucking_low_head",
            "seed": 9001,
            "generator_seed": 4101,
            "source": "smoke",
            "result": "inconclusive",
            "decision": "needs_more_tests",
            "baseline_prompt_summary": "Baseline prompt kept the head too high for the atlas low-head target.",
            "candidate_prompt_summary": "Candidate prompt moved the head below the shaft at testicle height.",
            "observation": "Smoke entry validates the durable fixed-seed record path without changing the real eval log.",
            "baseline_image": "/tmp/krea2_baseline.png",
            "candidate_image": "",
            "commit": "smoke",
        }
        errors = krea2_eval_log.validate_entry(
            smoke_entry,
            existing_entries=log.get("entries") or [],
            catalog_keys=set(krea2_pose_variant_catalog.variant_keys()),
        )
        _expect(errors == [], f"Valid Krea2 eval entry failed validation: {errors}")
        provisional_entry = dict(smoke_entry)
        provisional_entry["id"] = "ballsucking-9002-provisional-generator-smoke"
        provisional_entry["seed"] = 9002
        provisional_entry["result"] = "accepted"
        provisional_entry["decision"] = "provisional_generator_patch"
        provisional_errors = krea2_eval_log.validate_entry(
            provisional_entry,
            existing_entries=log.get("entries") or [],
            catalog_keys=set(krea2_pose_variant_catalog.variant_keys()),
        )
        _expect(provisional_errors == [], f"Provisional generator eval entry failed validation: {provisional_errors}")
        appended_log = krea2_eval_log.append_entry(smoke_entry, path=temp_log_path)
        _expect(len(appended_log.get("entries") or []) == len(entries) + 1, "Krea2 eval append did not add one entry")
        appended_entries = krea2_eval_log.entries_for_variant("pov_ballsucking_low_head", path=temp_log_path)
        _expect(appended_entries and appended_entries[-1].get("seed") == 9001, "Krea2 eval append did not persist temp entry")
        duplicate_errors = krea2_eval_log.validate_entry(
            smoke_entry,
            existing_entries=appended_log.get("entries") or [],
            catalog_keys=set(krea2_pose_variant_catalog.variant_keys()),
        )
        _expect(any("duplicate id" in error for error in duplicate_errors), "Krea2 eval validation should reject duplicate ids")
        bad_variant = dict(smoke_entry)
        bad_variant["id"] = "missing-variant-9001"
        bad_variant["variant_key"] = "missing_variant"
        bad_variant_errors = krea2_eval_log.validate_entry(
            bad_variant,
            existing_entries=appended_log.get("entries") or [],
            catalog_keys=set(krea2_pose_variant_catalog.variant_keys()),
        )
        _expect(any("unknown variant" in error for error in bad_variant_errors), "Krea2 eval validation should reject unknown variants")
        bad_generator_seed = dict(smoke_entry)
        bad_generator_seed["id"] = "bad-generator-seed-9001"
        bad_generator_seed["generator_seed"] = "4101"
        bad_generator_seed_errors = krea2_eval_log.validate_entry(
            bad_generator_seed,
            existing_entries=appended_log.get("entries") or [],
            catalog_keys=set(krea2_pose_variant_catalog.variant_keys()),
        )
        _expect(
            any("generator_seed must be an integer" in error for error in bad_generator_seed_errors),
            "Krea2 eval validation should reject non-integer generator_seed",
        )
        template = krea2_eval_log.entry_template(
            "pov_footjob_frontal_sole_stroke",
            seed=9102,
            generator_seed=9104,
            source="smoke",
            date="2026-06-29",
        )
        _expect(template.get("variant_key") == "pov_footjob_frontal_sole_stroke", "Krea2 eval template lost variant key")
        _expect(template.get("seed") == 9102, "Krea2 eval template lost fixed seed")
        _expect(template.get("generator_seed") == 9104, "Krea2 eval template lost generator seed")
        _expect(template.get("result") == "inconclusive", "Krea2 eval template should default to inconclusive")
        _expect(template.get("decision") == "needs_more_tests", "Krea2 eval template should default to needs_more_tests")
        _expect("footjob" in str(template.get("id") or ""), "Krea2 eval template id should include variant family")
        template_errors = krea2_eval_log.validate_entry(
            template,
            existing_entries=appended_log.get("entries") or [],
            catalog_keys=set(krea2_pose_variant_catalog.variant_keys()),
        )
        _expect(template_errors == [], f"Krea2 eval template should validate immediately: {template_errors}")
        cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_record_eval.py"),
                "--print-template",
                "--variant-key",
                "pov_fingering_reclined_open_thighs",
                "--seed",
                "9103",
                "--generator-seed",
                "9105",
                "--source",
                "smoke",
                "--date",
                "2026-06-29",
            ],
            cwd=str(ROOT),
            capture_output=True,
            text=True,
            check=False,
        )
        _expect(cli_result.returncode == 0, f"Krea2 eval template CLI failed: {cli_result.stderr}")
        cli_template = json.loads(cli_result.stdout)
        _expect(cli_template.get("variant_key") == "pov_fingering_reclined_open_thighs", "Krea2 eval template CLI lost variant")
        _expect(cli_template.get("seed") == 9103, "Krea2 eval template CLI lost seed")
        _expect(cli_template.get("generator_seed") == 9105, "Krea2 eval template CLI lost generator seed")


def smoke_krea2_prompt_guide_policy() -> None:
    guide = (ROOT / "docs" / "krea2-prompt-guide.md").read_text(encoding="utf-8")
    _expect("## Climax / Ejaculation Wording" in guide, "Krea2 prompt guide lost climax wording section")
    _expect("ejaculates semen" in guide, "Krea2 prompt guide lost explicit semen wording rule")
    _expect("### Ballsucking / Testicle Sucking" in guide, "Krea2 prompt guide lost ballsucking section")
    _expect("chest low over the viewer's pelvis" in guide, "Krea2 prompt guide lost low-body ballsucking cue")
    _expect("balls fill the exact center" in guide, "Krea2 prompt guide lost balls-first scrotum framing rule")
    _expect("shaft is cropped to the side" in guide, "Krea2 prompt guide lost side-cropped shaft rule")
    _expect("Fresh-seed partial update: sampler seed `6262626262`" in guide, "Krea2 prompt guide lost ballsucking fresh-seed partial update")
    _expect("Generated-route validation: turns `262` and `263`" in guide, "Krea2 prompt guide lost ballsucking generated-route validation update")
    _expect("Fresh-seed target-object repeat: sampler seed `9797979797`" in guide, "Krea2 prompt guide lost ballsucking target-object repeat update")
    _expect("scrotal skin is the nearest mouth surface" in guide and "turns `288` and `293`" in guide, "Krea2 prompt guide lost ballsucking scrotal-skin target evidence")
    _expect("Generated-route validation: sampler seed `9898989898`" in guide, "Krea2 prompt guide lost ballsucking scrotal-skin generated-route validation")
    _expect("turns `296` and `297`" in guide and "side-low cheek/thigh geometry" in guide, "Krea2 prompt guide lost ballsucking validation turn evidence")
    _expect("Fresh-seed weak case: sampler seed `5959595959`" in guide, "Krea2 prompt guide lost ballsucking fresh-seed weak-case update")
    _expect("lip-oval" in guide and "sideways mouth pocket" in guide and "chin-pelvis upward seal" in guide, "Krea2 prompt guide lost ballsucking weak-case wording axes")
    _expect("Fresh-seed occlusion weak case: sampler seed `6060606060`" in guide, "Krea2 prompt guide lost ballsucking occlusion weak-case update")
    _expect("scrotum foreground occlusion" in guide and "under-scrotum tongue shelf" in guide and "hand-guided scrotum" in guide, "Krea2 prompt guide lost ballsucking occlusion weak-case wording axes")
    _expect("Fresh-seed mouth-axis mixed case: sampler seed `6161616161`" in guide, "Krea2 prompt guide lost ballsucking mouth-axis mixed-case update")
    _expect("exact mouth-sucking" in guide and "single-testicle" in guide and "hanging balls below shaft" in guide, "Krea2 prompt guide lost ballsucking mouth-axis wording axes")
    _expect("turns `331` and `337`" in guide and "turn `348`" in guide, "Krea2 prompt guide lost ballsucking mouth-axis turn evidence")
    _expect("Fresh-seed pelvis-valley weak case: sampler seed `7171717171`" in guide, "Krea2 prompt guide lost ballsucking pelvis-valley weak-case update")
    _expect("flat pelvis-valley" in guide and "thigh tunnel" in guide and "pelvis-edge target-first" in guide, "Krea2 prompt guide lost ballsucking pelvis-valley wording axes")
    _expect("turns `350`, `356`, and `362`" in guide and "body-plane correction" in guide, "Krea2 prompt guide lost ballsucking pelvis-valley turn evidence")
    _expect("Fresh-seed flat-target hybrid weak case: sampler seed `7272727272`" in guide, "Krea2 prompt guide lost ballsucking flat-target hybrid weak-case update")
    _expect("flat-valley scrotal-skin target" in guide and "upper-frame shaft lower-scrotum" in guide and "side-low flat-valley hybrid" in guide, "Krea2 prompt guide lost ballsucking flat-target hybrid wording axes")
    _expect("turns `368`, `374`, and `380`" in guide and "shaft-centered" in guide, "Krea2 prompt guide lost ballsucking flat-target hybrid turn evidence")
    _expect("### Footjob" in guide, "Krea2 prompt guide lost footjob section")
    _expect("large overlapping soles" in guide, "Krea2 prompt guide lost overlapping-sole footjob rule")
    _expect("shaft trapped between the two soles" in guide, "Krea2 prompt guide lost trapped-shaft footjob rule")
    _expect("glans rises between the compressed feet" in guide, "Krea2 prompt guide lost visible-glans footjob correction rule")
    _expect("Fresh-seed promotion: sampler seed `7373737373`" in guide, "Krea2 prompt guide lost footjob fresh-seed promotion note")
    _expect("### Fingering / Manual Stimulation" in guide, "Krea2 prompt guide lost fingering section")
    _expect("single foreground hand is the largest lower-frame object" in guide, "Krea2 prompt guide lost hand-first fingering rule")
    _expect("office-chair support and coworking depth fourth" in guide, "Krea2 prompt guide lost coworking-safe fingering hierarchy")
    _expect("provisional improvement over baseline" in guide, "Krea2 prompt guide lost provisional fingering generator note")
    _expect("### Wand Toy Contact" in guide, "Krea2 prompt guide lost wand section")
    _expect("single continuous teal wand-style massager" in guide, "Krea2 prompt guide lost wand single-device rule")
    _expect("Krea2 splitting the cue into a contact toy plus a second wand-like foreground object" in guide, "Krea2 prompt guide lost wand split-device failure mode")
    _expect("### Ready / Post-Ejaculation Open-Thigh Display" in guide, "Krea2 prompt guide lost ready aftermath section")
    _expect("the wet aftermath detail is the exact center" in guide, "Krea2 prompt guide lost ready wet-aftermath hierarchy")
    _expect("category-exit rule" in guide, "Krea2 prompt guide lost ready provisional generator note")
    _expect("### Spread / Open-Thigh Presentation" in guide, "Krea2 prompt guide lost spread section")
    _expect("thighs form a broad V-frame" in guide, "Krea2 prompt guide lost spread V-frame rule")
    _expect("hands hold the knees or thighs" in guide, "Krea2 prompt guide lost spread hand-anchor rule")
    _expect("### Blowjob Top View / Overhead Vertical Shaft" in guide, "Krea2 prompt guide lost blowjob top-view section")
    _expect("nadir-angle standing male POV top-view oral position" in guide, "Krea2 prompt guide lost top-view nadir-angle rule")
    _expect("nearby carpet/floor plane dominates the image" in guide, "Krea2 prompt guide lost top-view floor-plane rule")
    _expect("shaft appears as a short centered vertical column" in guide, "Krea2 prompt guide lost top-view vertical-column rule")
    _expect("hair crown, forehead, shoulders, hands, and knees are visible from above" in guide, "Krea2 prompt guide lost top-view from-above body-surface rule")
    _expect("plumb-line" in guide and "literal drawn artifacts" in guide, "Krea2 prompt guide lost top-view literalization warning")
    _expect("### Blowjob Side Profile / Side-Phone Weak Case" in guide, "Krea2 prompt guide lost blowjob side weak-case section")
    _expect("side-phone" in guide, "Krea2 prompt guide lost side-phone oral weak-case wording")
    _expect("not valid POV evidence" in guide, "Krea2 prompt guide lost side-profile non-POV boundary")
    _expect("visible adult male chest, navel, abdomen hair" in guide, "Krea2 prompt guide lost side-profile male-body ownership cue")
    _expect("accepted single-source evidence" in guide, "Krea2 prompt guide lost side-profile single-source evidence boundary")
    _expect("Krea2 transferred the body-axis cue" in guide, "Krea2 prompt guide lost side-profile body-axis transfer failure")
    _expect("lateral_edge_entry" in guide and "male viewer's abdomen" in guide and "near thigh create a broad horizontal foreground body" in guide, "Krea2 prompt guide lost side-profile lateral-edge matrix update")
    _expect("turn `206`" in guide and "mouth-to-shaft contact is the nearest facial detail" in guide, "Krea2 prompt guide lost side-profile generated-route contact update")
    _expect("Fresh-seed weak case: sampler seed `8484848484`" in guide, "Krea2 prompt guide lost side-profile fresh-seed weak-case update")
    _expect("Fresh-seed lower-right torso repeat: sampler seed `9595959595`" in guide, "Krea2 prompt guide lost side-profile lower-right torso repeat")
    _expect("turns `279` and `283`" in guide and "adult male viewer's own torso starts at the lower edge" in guide, "Krea2 prompt guide lost side-profile lower-right self-torso evidence")
    _expect("Generated-route validation: sampler seed `9696969696`" in guide, "Krea2 prompt guide lost side-profile generated-route validation")
    _expect("turns `284` and `285`" in guide and "lower-right own-body foreground" in guide, "Krea2 prompt guide lost side-profile generated-route validation turn evidence")
    _expect("Fresh-seed promotion: sampler seed `5858585858`" in guide, "Krea2 prompt guide lost side-profile fresh-seed promotion")
    _expect("turns `298`, `301`, and `304`" in guide and "side-camera-style self-body crop" in guide, "Krea2 prompt guide lost side-profile three-woman promotion evidence")
    _expect("### Blowjob Laying Frontal / Wide V-Frame" in guide, "Krea2 prompt guide lost blowjob laying frontal section")
    _expect("wide symmetrical V-frame" in guide, "Krea2 prompt guide lost blowjob laying V-frame rule")
    _expect("torso stretched low and horizontal" in guide, "Krea2 prompt guide lost blowjob laying low-horizontal rule")
    _expect("centered mouth-to-shaft contact" in guide, "Krea2 prompt guide lost blowjob laying centered-contact rule")
    _expect("### Blowjob Sitting Upright / Low Mouth Contact" in guide, "Krea2 prompt guide lost blowjob sitting section")
    _expect("face lowered close to the exact center contact point" in guide, "Krea2 prompt guide lost blowjob sitting low-face rule")
    _expect("open mouth covers the tip at the centerline" in guide, "Krea2 prompt guide lost blowjob sitting mouth-tip rule")
    _expect("both hands stay low at the base directly below her mouth" in guide, "Krea2 prompt guide lost blowjob sitting hand-base rule")
    _expect("### Missionary Open-Leg / Seated-Lounge Drift" in guide, "Krea2 prompt guide lost missionary weak-case section")
    _expect("Follow-up prompt-axis probes on the same sampler seed" in guide, "Krea2 prompt guide lost missionary prompt-axis follow-up")
    _expect("pose-first probes omitted the original subject/look" in guide and "block" in guide, "Krea2 prompt guide lost missionary prompt-order caveat")
    _expect("Patch only the raised-edge or" in guide and "edge-supported route" in guide, "Krea2 prompt guide lost missionary edge-supported patch boundary")
    _expect("keep generic" in guide and "valid angled missionary views" in guide, "Krea2 prompt guide lost generic missionary validity boundary")
    _expect("office-lounge support words" in guide, "Krea2 prompt guide lost missionary lounge-drift warning")
    _expect("flat elevated-support atlas" in guide, "Krea2 prompt guide lost flat elevated-support axis")
    _expect("### Cowgirl Frontal / Wide Thigh Bridge" in guide, "Krea2 prompt guide lost cowgirl wide-thigh section")
    _expect("Matrix update: sampler seed `2828282828`" in guide, "Krea2 prompt guide lost cowgirl repeat matrix update")
    _expect("Mirror the wide-thigh bridge hierarchy into the normal cowgirl" in guide, "Krea2 prompt guide lost cowgirl provisional generator note")
    _expect("Generated-route validation: turn `216`" in guide, "Krea2 prompt guide lost cowgirl generated-route validation note")
    _expect("Fresh-seed promotion: sampler seed `9191919191`" in guide, "Krea2 prompt guide lost cowgirl fresh-seed promotion note")
    _expect("## Evidence Promotion Threshold" in guide, "Krea2 prompt guide lost evidence promotion threshold")
    _expect("Do not promote a single-character, single-location prompt hack" in guide, "Krea2 prompt guide lost single-scene overfit guard")
    _expect("Scene repair words must match the selected location" in guide, "Krea2 prompt guide lost location-consistency guard")
    methodology = (ROOT / "docs" / "krea2-ab-methodology.md").read_text(encoding="utf-8")
    _expect("2026-06-30-generated-route-validation-positive-channel-cleanup" in methodology, "Krea2 A/B methodology memory lost current method version")
    _expect("Update it whenever the testing method improves" in methodology, "Krea2 A/B methodology memory lost update contract")
    _expect("Use location-compatible anchors only" in methodology, "Krea2 A/B methodology memory lost anchor rule")
    _expect("## Generator Mirroring" in methodology, "Krea2 A/B methodology memory lost generator mirroring section")
    _expect("final formatter output" in methodology, "Krea2 A/B methodology memory lost formatter-output regression rule")
    _expect("## Generator-Patch Evidence Matrix" in methodology, "Krea2 A/B methodology memory lost generator-patch matrix")
    _expect("at least three distinct source cases" in methodology, "Krea2 A/B methodology memory lost source-case threshold")
    _expect("at least two sampler seeds" in methodology, "Krea2 A/B methodology memory lost sampler-seed threshold")
    _expect("provisional_generator_patch" in methodology, "Krea2 A/B methodology memory lost provisional generator rule")
    _expect("same-seed evidence shows it improves over baseline" in methodology, "Krea2 A/B methodology memory lost category-exit progress rule")
    _expect("two source subjects improved on the same sampler seed" in methodology, "Krea2 A/B methodology memory lost spread two-source update")
    _expect("baseline is already usable" in methodology, "Krea2 A/B methodology memory lost narrow-improvement update")
    _expect("attractive side-camera result" in methodology, "Krea2 A/B methodology memory lost non-target-viewpoint weak-case rule")
    _expect(
        "/media/p5/miniforge3/bin/python tools/sxcp_mcp_client.py call-tool comfy_push" in methodology,
        "Krea2 A/B methodology memory lost approved MCP push helper command",
    )
    _expect("source `46`" in methodology and "improved with explicit adult-male foreground ownership" in methodology, "Krea2 A/B methodology memory lost side-profile ownership update")
    _expect("source `47`" in methodology and "rejected a related `body-axis` cue" in methodology, "Krea2 A/B methodology memory lost body-axis rejection update")
    _expect("9753197531" in methodology and "lateral-edge body-line axis" in methodology, "Krea2 A/B methodology memory lost side-profile lateral-edge update")
    _expect("turn `206`" in methodology and "mouth-to-shaft-contact priority" in methodology, "Krea2 A/B methodology memory lost side-profile generated-route contact update")
    _expect("9595959595" in methodology and "lower-right torso anchor" in methodology, "Krea2 A/B methodology memory lost side-profile lower-right anchor update")
    _expect("9696969696" in methodology and "generated-route validation" in methodology, "Krea2 A/B methodology memory lost side-profile generated-route validation update")
    _expect("5858585858" in methodology and "three-woman generated-route repeat" in methodology, "Krea2 A/B methodology memory lost side-profile fresh-seed promotion update")
    _expect("5959595959" in methodology and "lip-oval" in methodology and "shaft/glans collapse" in methodology, "Krea2 A/B methodology memory lost ballsucking fresh weak-case update")
    _expect("6060606060" in methodology and "foreground occlusion" in methodology and "hand-guided" in methodology, "Krea2 A/B methodology memory lost ballsucking occlusion weak-case update")
    _expect("6161616161" in methodology and "exact mouth-sucking" in methodology and "generated-route controls" in methodology, "Krea2 A/B methodology memory lost ballsucking mouth-axis mixed-case update")
    _expect("7171717171" in methodology and "flat pelvis-valley" in methodology and "body-plane correction" in methodology, "Krea2 A/B methodology memory lost ballsucking pelvis-valley weak-case update")
    _expect("7272727272" in methodology and "flat-valley scrotal-skin" in methodology and "shaft-centered" in methodology, "Krea2 A/B methodology memory lost ballsucking flat-target hybrid weak-case update")
    _expect("2828282828" in methodology and "wide-thigh bridge axis" in methodology, "Krea2 A/B methodology memory lost cowgirl provisional patch update")
    _expect("blowjob laying frontal" in methodology, "Krea2 A/B methodology memory lost blowjob laying update")
    _expect("wide V-frame and low-horizontal torso" in methodology, "Krea2 A/B methodology memory lost blowjob laying hierarchy update")
    _expect("blowjob sitting upright" in methodology, "Krea2 A/B methodology memory lost blowjob sitting update")
    _expect("low-mouth seated hierarchy" in methodology, "Krea2 A/B methodology memory lost blowjob sitting hierarchy update")
    _expect("Corrected blowjob top-view criteria" in methodology, "Krea2 A/B methodology memory lost top-view correction update")
    _expect("Refined blowjob top-view prompt-axis search" in methodology, "Krea2 A/B methodology memory lost top-view axis-search update")
    _expect("Avoid `plumb-line` and `map` in generator prompts" in methodology, "Krea2 A/B methodology memory lost top-view literalization warning")
    _expect("batched prompt-probe" in methodology and "loop before analysis-heavy iteration" in methodology, "Krea2 A/B methodology memory lost batched prompt-probe loop")
    _expect("pull only until each new" in methodology and "`sxcp_eval_in` turn and image path exists" in methodology, "Krea2 A/B methodology memory lost image-presence probe rule")
    _expect("Preserve prompt-order controls" in methodology, "Krea2 A/B methodology memory lost prompt-order control rule")
    _expect("subject/look description first" in methodology, "Krea2 A/B methodology memory lost subject-look-first rule")
    _expect("geometry-only probes" in methodology, "Krea2 A/B methodology memory lost geometry-only probe caveat")
    _expect("vertical shaft alignment alone" in methodology, "Krea2 A/B methodology memory lost vertical-shaft insufficiency rule")
    _expect(
        "Never put negative-conditioning phrases inside the positive Krea2 prompt" in guide,
        "Krea2 prompt guide lost positive-only conditioning rule",
    )
    _expect(
        "no active negative-output contract" in guide,
        "Krea2 prompt guide lost no-negative-channel workflow rule",
    )
    _expect("## Stronger-Control / Low-Priority Cases" in guide, "Krea2 prompt guide lost stronger-control section")
    _expect("pov_sixty_nine_close_reversed_oral" in guide, "Krea2 prompt guide lost sixty-nine unstable route")
    _expect("hardest" in guide and "low-priority" in guide, "Krea2 prompt guide lost hardest low-priority wording")
    _expect("not a normal prompt-only fixed-seed candidate" in guide, "Krea2 prompt guide should not queue sixty-nine as normal prompt tuning")
    _expect("pose/control image" in guide or "image-guided" in guide, "Krea2 prompt guide lost control-first guidance")


def smoke_krea2_tuning_report_policy() -> None:
    rows = krea2_tuning_report.coverage_rows()
    catalog_keys = krea2_pose_variant_catalog.variant_keys()
    _expect([row.get("key") for row in rows] == catalog_keys, "Krea2 tuning report row order should follow catalog order")
    by_key = {row.get("key"): row for row in rows}
    boobjob = by_key.get("pov_boobjob_upright_cleavage") or {}
    _expect(boobjob.get("coverage_state") == "proven_with_evidence", "Boobjob report should be proven with evidence")
    _expect(boobjob.get("accepted_evidence_count", 0) >= 1, "Boobjob report lost accepted evidence count")
    boobjob_latest = boobjob.get("latest_evidence") or {}
    _expect(boobjob_latest.get("id") == "boobjob-7302-upright-cleavage", "Boobjob report lost latest evidence id")
    _expect(boobjob_latest.get("seed") == 7302, "Boobjob report lost latest fixed seed")
    _expect(boobjob_latest.get("result") == "accepted", "Boobjob report lost latest evidence result")
    _expect(boobjob_latest.get("decision") == "generator_patch", "Boobjob report lost latest evidence decision")
    _expect("upright frontal boobjob geometry" in str(boobjob_latest.get("candidate_prompt_summary") or ""), "Boobjob report lost latest candidate summary")
    ballsucking = by_key.get("pov_ballsucking_low_head") or {}
    _expect(ballsucking.get("coverage_state") == "tracked", "Ballsucking report should track accepted prompt-guide evidence without proving the candidate")
    _expect(ballsucking.get("accepted_evidence_count") == 6, "Ballsucking report lost fresh generated-route validation evidence")
    _expect(ballsucking.get("total_evidence_count") == 14, "Ballsucking report lost fresh flat-target hybrid weak-case history")
    ballsucking_latest = ballsucking.get("latest_evidence") or {}
    _expect(
        ballsucking_latest.get("id") == "ballsucking-7272727272-flat-target-hybrid-weak-case",
        "Ballsucking report lost latest fresh flat-target hybrid weak-case evidence id",
    )
    _expect(ballsucking_latest.get("seed") == 7272727272, "Ballsucking report lost fresh flat-target hybrid weak-case sampler seed evidence")
    _expect(ballsucking_latest.get("result") == "inconclusive", "Ballsucking latest weak-case evidence should be inconclusive")
    _expect(ballsucking_latest.get("decision") == "needs_more_tests", "Ballsucking latest weak-case evidence should keep route queued")
    _expect(
        "flat-valley scrotal-skin" in str(ballsucking_latest.get("candidate_prompt_summary") or "") and "side-low flat-valley" in str(ballsucking_latest.get("candidate_prompt_summary") or ""),
        "Ballsucking report lost fresh flat-target hybrid wording axes",
    )
    _expect(
        "turns 367-384" in str(ballsucking_latest.get("observation") or "") and "shaft-centered" in str(ballsucking_latest.get("observation") or ""),
        "Ballsucking report lost fresh flat-target hybrid weak-case turn observation",
    )
    ballsucking_latest_accepted = ballsucking.get("latest_accepted_evidence") or {}
    _expect(
        ballsucking_latest_accepted.get("id") == "ballsucking-9898989898-scrotal-skin-route-validation",
        "Ballsucking report lost latest accepted validation evidence id",
    )
    footjob = by_key.get("pov_footjob_frontal_sole_stroke") or {}
    _expect(footjob.get("coverage_state") == "proven_with_evidence", "Footjob report should be proven after fresh-seed generated-route repeat evidence")
    _expect(footjob.get("accepted_evidence_count") == 5, "Footjob report lost fresh-seed proven evidence")
    _expect(footjob.get("total_evidence_count") == 5, "Footjob report lost accepted A/B history")
    footjob_latest = footjob.get("latest_evidence") or {}
    _expect(
        footjob_latest.get("id") == "footjob-7373737373-generated-route-repeat-proven",
        "Footjob report lost latest fresh-seed proven evidence id",
    )
    _expect(footjob_latest.get("seed") == 7373737373, "Footjob report lost fresh sampler seed evidence")
    _expect(footjob_latest.get("result") == "accepted", "Footjob report lost accepted evidence result")
    _expect(footjob_latest.get("decision") == "proven_with_evidence", "Footjob report lost proven evidence decision")
    _expect(
        "narrow visible strip of shaft and glans" in str(footjob_latest.get("candidate_prompt_summary") or ""),
        "Footjob report lost narrow-strip visible-contact correction rule",
    )
    fingering = by_key.get("pov_fingering_reclined_open_thighs") or {}
    _expect(fingering.get("coverage_state") == "tracked", "Fingering report should track accepted prompt-guide evidence without proving the candidate")
    _expect(fingering.get("accepted_evidence_count") == 2, "Fingering report lost repeated prompt-guide evidence")
    _expect(fingering.get("total_evidence_count") == 5, "Fingering report should retain inconclusive/prompt-guide/generator/matrix/weak-case A/B history")
    fingering_latest = fingering.get("latest_evidence") or {}
    _expect(
        fingering_latest.get("id") == "fingering-1357913579-source52-own-hand-weak-case",
        "Fingering report lost latest source-52 weak-case evidence id",
    )
    _expect(fingering_latest.get("seed") == 1357913579, "Fingering report lost source-52 sampler seed evidence")
    _expect(fingering_latest.get("result") == "inconclusive", "Fingering source-52 weak case should stay inconclusive")
    _expect(fingering_latest.get("decision") == "needs_more_tests", "Fingering source-52 weak case should need more tests")
    _expect(
        "changed hand ownership" in str(fingering_latest.get("observation") or ""),
        "Fingering report lost hand-ownership weak-case note",
    )
    fingering_latest_accepted = fingering.get("latest_accepted_evidence") or {}
    _expect(
        fingering_latest_accepted.get("id") == "fingering-987654321-source50-office-chair-hand-hierarchy",
        "Fingering report lost latest accepted prompt-guide evidence id",
    )
    _expect(
        fingering_latest_accepted.get("decision") == "provisional_generator_patch",
        "Fingering latest accepted evidence should be a provisional generator patch",
    )
    wand = by_key.get("pov_wand_foreground_tool_contact") or {}
    _expect(wand.get("coverage_state") == "proven_with_evidence", "Wand report should be proven after repeated generated-route evidence")
    _expect(wand.get("accepted_evidence_count") == 3, "Wand report lost accepted evidence")
    _expect(wand.get("total_evidence_count") == 3, "Wand report lost accepted A/B history")
    wand_latest = wand.get("latest_evidence") or {}
    _expect(
        wand_latest.get("id") == "wand-7979797979-two-woman-teal-repeat-proven",
        "Wand report lost latest accepted evidence id",
    )
    _expect(wand_latest.get("seed") == 7979797979, "Wand report lost sampler seed evidence")
    _expect(wand_latest.get("decision") == "generator_patch", "Wand latest evidence should finalize the generator default")
    _expect(
        "white upper-left branch" in str(wand_latest.get("candidate_prompt_summary") or ""),
        "Wand report lost upper-left alternate evidence",
    )
    ready = by_key.get("pov_ejaculation_aftermath_open_thigh_candidate") or {}
    _expect(ready.get("coverage_state") == "tracked", "Ready aftermath report should track first accepted evidence")
    _expect(ready.get("accepted_evidence_count") == 1, "Ready aftermath report lost first accepted evidence")
    _expect(ready.get("total_evidence_count") == 1, "Ready aftermath report should have one recorded A/B result")
    ready_latest = ready.get("latest_evidence") or {}
    _expect(
        ready_latest.get("id") == "ready-1123581321-source52-wet-aftermath-hierarchy",
        "Ready aftermath report lost latest accepted evidence id",
    )
    _expect(ready_latest.get("seed") == 1123581321, "Ready aftermath report lost sampler seed evidence")
    _expect(ready_latest.get("decision") == "provisional_generator_patch", "Ready first evidence should be a provisional generator patch")
    _expect(
        "wet aftermath hierarchy" in str(ready_latest.get("candidate_prompt_summary") or ""),
        "Ready aftermath report lost wet hierarchy evidence",
    )
    sixty_nine = by_key.get("pov_sixty_nine_close_reversed_oral") or {}
    _expect(sixty_nine.get("coverage_state") == "needs_stronger_control", "Sixty-nine report should require stronger control")
    _expect(sixty_nine.get("accepted_evidence_count") == 0, "Sixty-nine report should not have accepted evidence yet")
    _expect(sixty_nine.get("difficulty") == "hardest", "Sixty-nine report lost hardest-route marker")
    _expect(sixty_nine.get("priority") == "low", "Sixty-nine report lost low-priority marker")
    _expect(
        sixty_nine.get("control_requirement") == "pose_or_image_guidance_first",
        "Sixty-nine report lost control-first marker",
    )
    spread = by_key.get("pov_spread_open_thigh_presentation") or {}
    _expect(spread.get("coverage_state") == "tracked", "Spread report should track provisional generator evidence")
    _expect(spread.get("accepted_evidence_count") == 1, "Spread report lost first accepted evidence")
    _expect(spread.get("total_evidence_count") == 1, "Spread report should have one recorded A/B result")
    spread_latest = spread.get("latest_evidence") or {}
    _expect(
        spread_latest.get("id") == "spread-3141592653-source50-47-raised-knee-v-frame",
        "Spread report lost latest accepted evidence id",
    )
    _expect(spread_latest.get("seed") == 3141592653, "Spread report lost sampler seed evidence")
    _expect(spread_latest.get("decision") == "provisional_generator_patch", "Spread first evidence should be a provisional generator patch")
    _expect(
        "atlas spread hierarchy" in str(spread_latest.get("candidate_prompt_summary") or ""),
        "Spread report lost atlas hierarchy evidence",
    )
    oral_top = by_key.get("pov_blowjob_top_down_vertical_shaft") or {}
    _expect(oral_top.get("coverage_state") == "tracked", "Blowjob top-view report should track provisional generator evidence")
    _expect(oral_top.get("accepted_evidence_count") == 2, "Blowjob top-view report lost accepted correction evidence")
    _expect(oral_top.get("total_evidence_count") == 3, "Blowjob top-view report should have three recorded A/B results")
    oral_top_latest = oral_top.get("latest_evidence") or {}
    _expect(
        oral_top_latest.get("id") == "blowjob-top-4242424242-turn67-70-nadir-floor-plane-axis",
        "Blowjob top-view report lost latest nadir-angle correction evidence id",
    )
    _expect(oral_top_latest.get("seed") == 4242424242, "Blowjob top-view report lost sampler seed evidence")
    _expect(
        oral_top_latest.get("decision") == "provisional_generator_patch",
        "Blowjob top-view first evidence should be a provisional generator patch",
    )
    _expect(
        "nadir-angle" in str(oral_top_latest.get("candidate_prompt_summary") or ""),
        "Blowjob top-view report lost nadir-angle hierarchy evidence",
    )
    oral_side = by_key.get("pov_blowjob_side_profile_oral") or {}
    _expect(oral_side.get("coverage_state") == "proven_with_evidence", "Blowjob side report should be proven after three-woman generated-route repeat evidence")
    _expect(oral_side.get("accepted_evidence_count") == 6, "Blowjob side report should count male-body-axis, lateral-edge, generated-route, lower-right torso, validation, and promotion evidence")
    _expect(oral_side.get("total_evidence_count") == 8, "Blowjob side report should retain fresh weak-case and promotion evidence")
    oral_side_latest = oral_side.get("latest_evidence") or {}
    _expect(
        oral_side_latest.get("id") == "blowjob-side-5858585858-three-woman-generated-route-proven",
        "Blowjob side report lost latest three-woman generated-route evidence id",
    )
    _expect(oral_side_latest.get("seed") == 5858585858, "Blowjob side report lost three-woman generated-route sampler seed evidence")
    _expect(oral_side_latest.get("result") == "accepted", "Blowjob side three-woman generated-route evidence should be accepted")
    _expect(oral_side_latest.get("decision") == "proven_with_evidence", "Blowjob side three-woman generated-route evidence should promote the route")
    _expect(oral_side_latest.get("needs_expansion") is False, "Blowjob side three-woman generated-route evidence should leave the expansion queue")
    _expect(
        "three-woman generated-route repeat" in str(oral_side_latest.get("observation") or ""),
        "Blowjob side report lost three-woman generated-route observation",
    )
    _expect(
        "turns 298, 301, and 304" in str(oral_side_latest.get("observation") or ""),
        "Blowjob side report lost three-woman generated-route turn evidence",
    )
    oral_laying = by_key.get("pov_blowjob_laying_frontal_oral") or {}
    _expect(oral_laying.get("coverage_state") == "tracked", "Blowjob laying report should track provisional generator evidence")
    _expect(oral_laying.get("accepted_evidence_count") == 1, "Blowjob laying report lost first accepted evidence")
    _expect(oral_laying.get("total_evidence_count") == 1, "Blowjob laying report should have one recorded A/B result")
    oral_laying_latest = oral_laying.get("latest_evidence") or {}
    _expect(
        oral_laying_latest.get("id") == "blowjob-laying-6767676767-source46-50-wide-v-frame",
        "Blowjob laying report lost latest wide-V evidence id",
    )
    _expect(oral_laying_latest.get("seed") == 6767676767, "Blowjob laying report lost sampler seed evidence")
    _expect(oral_laying_latest.get("decision") == "provisional_generator_patch", "Blowjob laying evidence should be provisional generator patch")
    _expect(
        "wide V-frame and low-horizontal torso hierarchy" in str(oral_laying_latest.get("observation") or ""),
        "Blowjob laying report lost low-horizontal hierarchy observation",
    )
    oral_sitting = by_key.get("pov_blowjob_sitting_upright_oral") or {}
    _expect(oral_sitting.get("coverage_state") == "tracked", "Blowjob sitting report should track provisional generator evidence")
    _expect(oral_sitting.get("accepted_evidence_count") == 1, "Blowjob sitting report lost first accepted evidence")
    _expect(oral_sitting.get("total_evidence_count") == 1, "Blowjob sitting report should have one recorded A/B result")
    oral_sitting_latest = oral_sitting.get("latest_evidence") or {}
    _expect(
        oral_sitting_latest.get("id") == "blowjob-sitting-7878787878-source46-50-low-mouth-contact",
        "Blowjob sitting report lost latest low-mouth evidence id",
    )
    _expect(oral_sitting_latest.get("seed") == 7878787878, "Blowjob sitting report lost sampler seed evidence")
    _expect(oral_sitting_latest.get("decision") == "provisional_generator_patch", "Blowjob sitting evidence should be provisional generator patch")
    _expect(
        "low-mouth hierarchy" in str(oral_sitting_latest.get("observation") or ""),
        "Blowjob sitting report lost low-mouth hierarchy observation",
    )
    missionary = by_key.get("pov_missionary_open_leg_penetration") or {}
    _expect(missionary.get("coverage_state") == "tracked", "Missionary report should track accepted elevated-edge evidence")
    _expect(missionary.get("accepted_evidence_count") == 1, "Missionary report lost accepted elevated-edge evidence")
    _expect(missionary.get("total_evidence_count") == 4, "Missionary report should retain mixed, prompt-axis, subject-first, and elevated-edge evidence entries")
    missionary_latest = missionary.get("latest_evidence") or {}
    _expect(
        missionary_latest.get("id") == "missionary-open-8989898989-turn81-84-elevated-edge-support",
        "Missionary report lost latest elevated-edge support evidence id",
    )
    _expect(missionary_latest.get("result") == "accepted", "Missionary latest evidence should be accepted")
    _expect(missionary_latest.get("decision") == "provisional_generator_patch", "Missionary latest evidence should be a provisional generator patch")
    missionary_latest_text = f"{missionary_latest.get('candidate_prompt_summary') or ''} {missionary_latest.get('observation') or ''}"
    _expect("elevated support" in missionary_latest_text and "keep generic missionary" in missionary_latest_text, "Missionary latest evidence lost elevated-support/generic-route boundary")
    missionary_folded = by_key.get("pov_missionary_folded_high_leg_penetration") or {}
    _expect(missionary_folded.get("coverage_state") == "tracked", "Folded missionary report should track accepted contact-first evidence")
    _expect(missionary_folded.get("accepted_evidence_count") == 1, "Folded missionary report lost accepted contact-first evidence")
    _expect(missionary_folded.get("total_evidence_count") == 1, "Folded missionary report should have one durable evidence entry")
    missionary_folded_latest = missionary_folded.get("latest_evidence") or {}
    _expect(
        missionary_folded_latest.get("id") == "missionary-folded-8989898989-turn85-92-contact-first-knee-block",
        "Folded missionary report lost latest contact-first evidence id",
    )
    _expect(missionary_folded_latest.get("decision") == "provisional_generator_patch", "Folded missionary latest evidence should be a provisional generator patch")
    cowgirl = by_key.get("pov_cowgirl_frontal_straddle_penetration") or {}
    _expect(cowgirl.get("coverage_state") == "proven_with_evidence", "Cowgirl report should be proven after fresh-seed wide-thigh evidence")
    _expect(cowgirl.get("accepted_evidence_count") == 4, "Cowgirl report lost fresh-seed accepted wide-thigh evidence")
    _expect(cowgirl.get("total_evidence_count") == 4, "Cowgirl report should have four durable evidence entries")
    cowgirl_latest = cowgirl.get("latest_evidence") or {}
    _expect(
        cowgirl_latest.get("id") == "cowgirl-frontal-9191919191-fresh-seed-wide-thigh-proven",
        "Cowgirl report lost latest fresh-seed wide-thigh evidence id",
    )
    _expect(cowgirl_latest.get("decision") == "proven_with_evidence", "Cowgirl latest evidence should promote the route")
    _expect(cowgirl_latest.get("needs_expansion") is False, "Cowgirl proven evidence should leave the expansion queue")
    _expect(
        "Fresh seed" in str(cowgirl_latest.get("observation") or ""),
        "Cowgirl latest evidence lost fresh-seed observation",
    )
    cowgirl_alt = by_key.get("pov_cowgirl_alt_low_squat_penetration") or {}
    _expect(cowgirl_alt.get("coverage_state") == "tracked", "Cowgirl alt report should track accepted flat-supine evidence")
    _expect(cowgirl_alt.get("accepted_evidence_count") == 1, "Cowgirl alt report lost accepted flat-supine evidence")
    _expect(cowgirl_alt.get("total_evidence_count") == 1, "Cowgirl alt report should have one durable evidence entry")
    cowgirl_alt_latest = cowgirl_alt.get("latest_evidence") or {}
    _expect(
        cowgirl_alt_latest.get("id") == "cowgirl-alt-8989898989-turn97-104-flat-supine-low-angle",
        "Cowgirl alt report lost latest flat-supine evidence id",
    )
    _expect(cowgirl_alt_latest.get("decision") == "provisional_generator_patch", "Cowgirl alt latest evidence should be a provisional generator patch")
    reverse_cowgirl = by_key.get("pov_reverse_cowgirl_back_facing_penetration") or {}
    _expect(reverse_cowgirl.get("coverage_state") == "tracked", "Reverse cowgirl report should track accepted close-back evidence")
    _expect(reverse_cowgirl.get("accepted_evidence_count") == 1, "Reverse cowgirl report lost accepted close-back evidence")
    _expect(reverse_cowgirl.get("total_evidence_count") == 1, "Reverse cowgirl report should have one durable evidence entry")
    reverse_cowgirl_latest = reverse_cowgirl.get("latest_evidence") or {}
    _expect(
        reverse_cowgirl_latest.get("id") == "reverse-cowgirl-8989898989-turn105-108-close-back-hip-dominant",
        "Reverse cowgirl report lost latest close-back evidence id",
    )
    _expect(
        reverse_cowgirl_latest.get("decision") == "provisional_generator_patch",
        "Reverse cowgirl latest evidence should be a provisional generator patch",
    )
    reverse_cowgirl_alt = by_key.get("pov_reverse_cowgirl_alt_upright_back_facing_penetration") or {}
    _expect(reverse_cowgirl_alt.get("coverage_state") == "tracked", "Reverse cowgirl alt report should track accepted upright evidence")
    _expect(reverse_cowgirl_alt.get("accepted_evidence_count") == 1, "Reverse cowgirl alt report lost accepted upright evidence")
    _expect(reverse_cowgirl_alt.get("total_evidence_count") == 1, "Reverse cowgirl alt report should have one durable evidence entry")
    reverse_cowgirl_alt_latest = reverse_cowgirl_alt.get("latest_evidence") or {}
    _expect(
        reverse_cowgirl_alt_latest.get("id") == "reverse-cowgirl-alt-8989898989-turn109-112-upright-hands-on-hips",
        "Reverse cowgirl alt report lost latest upright evidence id",
    )
    _expect(
        reverse_cowgirl_alt_latest.get("decision") == "provisional_generator_patch",
        "Reverse cowgirl alt latest evidence should be a provisional generator patch",
    )
    summary = krea2_tuning_report.coverage_summary()
    _expect(summary.get("status_counts", {}).get("proven") == 7, "Krea2 tuning report proven count changed")
    _expect(summary.get("status_counts", {}).get("candidate") == 12, "Krea2 tuning report candidate count changed")
    _expect(summary.get("status_counts", {}).get("unstable") == 1, "Krea2 tuning report unstable count changed")
    _expect(
        summary.get("variants_without_accepted_evidence") == [
            "pov_sixty_nine_close_reversed_oral",
        ],
        f"Krea2 tuning report missing-evidence set changed: {summary.get('variants_without_accepted_evidence')}",
    )
    _expect(
        summary.get("stronger_control_cases") == ["pov_sixty_nine_close_reversed_oral"],
        f"Krea2 tuning report stronger-control set changed: {summary.get('stronger_control_cases')}",
    )
    expansion_plans = krea2_tuning_report.guide_expansion_plans()
    _expect(
        [plan.get("key") for plan in expansion_plans]
        == [
            "pov_ballsucking_low_head",
        ],
        f"Krea2 guide expansion queue changed: {[plan.get('key') for plan in expansion_plans]}",
    )
    ballsucking_expansion = expansion_plans[0]
    _expect(
        ballsucking_expansion.get("latest_accepted_decision") == "provisional_generator_patch",
        "Ballsucking expansion plan should identify the provisional generator evidence",
    )
    _expect(
        ballsucking_expansion.get("target") == "multi_seed_multi_woman_matrix",
        "Guide expansion plan should target multi-seed/multi-woman evidence",
    )
    _expect(
        "pov_cowgirl_frontal_straddle_penetration" not in [plan.get("key") for plan in expansion_plans],
        "Cowgirl should leave the expansion queue after fresh-seed promotion",
    )
    _expect(
        "pov_blowjob_side_profile_oral" not in [plan.get("key") for plan in expansion_plans],
        "Blowjob side should leave the expansion queue after fresh-seed three-woman promotion",
    )
    plans = krea2_tuning_report.next_test_plans()
    _expect(plans == [], f"Krea2 tuning report should have no normal next-test plans after reverse-alt evidence: {plans}")
    _expect(
        "pov_sixty_nine_close_reversed_oral" not in [plan.get("key") for plan in plans],
        "Unstable sixty-nine route should not be queued as a normal fixed-seed candidate",
    )
    template_commands = krea2_tuning_report.next_eval_template_commands(seed_token="$SEED")
    _expect(template_commands == [], f"Krea2 eval template commands should be empty when no normal next-test plans remain: {template_commands}")
    _expect(
        [command.get("key") for command in template_commands]
        == [plan.get("key") for plan in plans],
        "Krea2 eval template commands should follow normal next-test candidates",
    )
    _expect(
        "pov_sixty_nine_close_reversed_oral" not in " ".join(str(command.get("command") or "") for command in template_commands),
        "Krea2 eval template commands should exclude low-priority stronger-control routes",
    )
    _expect([plan.get("key") for plan in plans] == [], "Krea2 tuning report next plans changed")
    plan_by_key = {plan.get("key"): plan for plan in plans}
    _expect(
        "pov_ballsucking_low_head" not in plan_by_key,
        "Ballsucking should leave the normal next-test queue after accepted prompt-guide evidence",
    )
    _expect(
        "pov_footjob_frontal_sole_stroke" not in plan_by_key,
        "Footjob should leave the normal next-test queue after accepted prompt-guide evidence",
    )
    _expect(
        "pov_fingering_reclined_open_thighs" not in plan_by_key,
        "Fingering should leave the normal next-test queue after accepted prompt-guide evidence",
    )
    _expect(
        "pov_wand_foreground_tool_contact" not in plan_by_key,
        "Wand should leave the normal next-test queue after accepted prompt-guide evidence",
    )
    _expect(
        "pov_ejaculation_aftermath_open_thigh_candidate" not in plan_by_key,
        "Ready aftermath should leave the normal next-test queue after provisional generator evidence",
    )
    _expect(
        "pov_spread_open_thigh_presentation" not in plan_by_key,
        "Spread should leave the normal next-test queue after provisional generator evidence",
    )
    _expect(
        "pov_blowjob_top_down_vertical_shaft" not in plan_by_key,
        "Blowjob top-view should leave the normal next-test queue after provisional generator evidence",
    )
    _expect(
        "pov_blowjob_side_profile_oral" not in plan_by_key,
        "Blowjob side-profile should leave the normal next-test queue after accepted fragile evidence",
    )
    _expect(
        "pov_blowjob_laying_frontal_oral" not in plan_by_key,
        "Blowjob laying should leave the normal next-test queue after provisional generator evidence",
    )
    _expect(
        "pov_blowjob_sitting_upright_oral" not in plan_by_key,
        "Blowjob sitting should leave the normal next-test queue after provisional generator evidence",
    )
    _expect(
        "pov_missionary_open_leg_penetration" not in plan_by_key,
        "Missionary should leave the normal next-test queue after accepted elevated-edge evidence",
    )
    _expect(
        "pov_missionary_folded_high_leg_penetration" not in plan_by_key,
        "Folded missionary should leave the normal next-test queue after accepted contact-first evidence",
    )
    _expect(
        "pov_cowgirl_frontal_straddle_penetration" not in plan_by_key,
        "Cowgirl should leave the normal next-test queue after accepted wide-thigh evidence",
    )
    _expect(
        "pov_cowgirl_alt_low_squat_penetration" not in plan_by_key,
        "Cowgirl alt should leave the normal next-test queue after accepted flat-supine evidence",
    )
    _expect(
        "pov_reverse_cowgirl_back_facing_penetration" not in plan_by_key,
        "Reverse cowgirl should leave the normal next-test queue after accepted close-back evidence",
    )
    _expect(
        "pov_reverse_cowgirl_alt_upright_back_facing_penetration" not in plan_by_key,
        "Reverse cowgirl alt should leave the normal next-test queue after accepted upright evidence",
    )
    with tempfile.TemporaryDirectory() as tmpdir:
        atlas_root = Path(tmpdir)
        for folder in ("doggy", "doggy_control", "custom_pose", "custom_pose_control", "ready", "ready_control", "69", "69_control", "bg", "woman", "doggy_bg"):
            folder_path = atlas_root / folder
            folder_path.mkdir()
            (folder_path / f"{folder}_sample.png").write_bytes(b"")
        (atlas_root / "custom_pose" / "custom_pose_b.png").write_bytes(b"")
        (atlas_root / "custom_pose_control" / "custom_pose_control_b.png").write_bytes(b"")
        (atlas_root / "ready" / "ready_b.png").write_bytes(b"")
        (atlas_root / "ready_control" / "ready_control_b.png").write_bytes(b"")
        (atlas_root / "69" / "69_b.png").write_bytes(b"")
        (atlas_root / "69_control" / "69_control_b.png").write_bytes(b"")
        atlas_rows = krea2_tuning_report.atlas_folder_rows(atlas_root=atlas_root)
        atlas_by_folder = {row.get("folder"): row for row in atlas_rows}
        _expect(atlas_by_folder.get("doggy", {}).get("mapped") is True, "Atlas report should mark catalog folders as mapped")
        _expect(atlas_by_folder.get("custom_pose", {}).get("mapped") is False, "Atlas report should expose unmapped pose folders")
        _expect(atlas_by_folder.get("ready", {}).get("mapped") is True, "Atlas report should mark ready as mapped once cataloged")
        _expect(atlas_by_folder.get("69", {}).get("mapped") is True, "Atlas report should mark sixty-nine as mapped once cataloged")
        _expect("doggy_control" not in atlas_by_folder, "Atlas report should exclude control folders")
        _expect("doggy_bg" not in atlas_by_folder, "Atlas report should exclude background folders")
        _expect("bg" not in atlas_by_folder, "Atlas report should exclude shared bg folder")
        _expect("woman" not in atlas_by_folder, "Atlas report should exclude non-pose woman folder")
        atlas_summary = krea2_tuning_report.atlas_coverage_summary(atlas_root=atlas_root)
        _expect(atlas_summary.get("pose_folder_count") == 4, "Atlas report should count only pose folders")
        _expect(atlas_summary.get("mapped_folder_count") == 3, "Atlas report should count mapped pose folders")
        _expect(atlas_summary.get("unmapped_folders") == ["custom_pose"], "Atlas report should identify unmapped pose folders")
        gap_plans = krea2_tuning_report.atlas_gap_plans(atlas_root=atlas_root, sample_limit=2)
        _expect([plan.get("folder") for plan in gap_plans] == ["custom_pose"], "Atlas gap plans should follow unmapped folders")
        gap_by_folder = {plan.get("folder"): plan for plan in gap_plans}
        custom_gap = gap_by_folder["custom_pose"]
        _expect(custom_gap.get("suggested_variant_key") == "pov_custom_pose_candidate", "Atlas gap plan should suggest stable variant key")
        _expect(len(custom_gap.get("sample_images") or []) == 2, "Atlas gap plan should include deterministic sample images")
        _expect(len(custom_gap.get("control_images") or []) == 2, "Atlas gap plan should include deterministic control images")
        _expect(
            krea2_tuning_report._suggested_variant_key("ready") == "pov_ejaculation_aftermath_open_thigh_candidate",
            "Atlas gap plan should not treat ready as a neutral setup pose",
        )
        atlas_markdown = krea2_tuning_report.markdown_report(atlas_root=atlas_root)
        _expect("Atlas Folder Coverage" in atlas_markdown, "Krea2 tuning report markdown lost atlas coverage section")
        _expect("custom_pose" in atlas_markdown, "Krea2 tuning report markdown lost unmapped atlas folder")
        _expect("pov_custom_pose_candidate" in atlas_markdown, "Krea2 tuning report markdown lost suggested gap key")
    markdown = krea2_tuning_report.markdown_report()
    _expect("## Latest Evidence" in markdown, "Krea2 tuning report markdown lost latest evidence section")
    _expect("boobjob-7302-upright-cleavage" in markdown, "Krea2 tuning report markdown lost boobjob evidence id")
    _expect("seed 7302" in markdown, "Krea2 tuning report markdown lost evidence seed")
    _expect("generator_patch" in markdown, "Krea2 tuning report markdown lost evidence decision")
    _expect("upright frontal boobjob geometry" in markdown, "Krea2 tuning report markdown lost evidence prompt summary")
    _expect("ballsucking-7272727272-flat-target-hybrid-weak-case" in markdown, "Krea2 tuning report markdown lost ballsucking fresh flat-target hybrid weak-case evidence id")
    _expect("flat-valley scrotal-skin" in markdown and "shaft-centered" in markdown, "Krea2 tuning report markdown lost ballsucking fresh flat-target hybrid weak-case evidence")
    _expect("footjob-7373737373-generated-route-repeat-proven" in markdown, "Krea2 tuning report markdown lost footjob fresh-seed proven evidence id")
    _expect("two large overlapping soles" in markdown, "Krea2 tuning report markdown lost overlapping-sole footjob evidence")
    _expect("narrow visible strip of shaft and glans" in markdown, "Krea2 tuning report markdown lost visible-strip footjob evidence")
    _expect("fingering-1357913579-source52-own-hand-weak-case" in markdown, "Krea2 tuning report markdown lost fingering source-52 weak-case evidence id")
    _expect("changed hand ownership" in markdown, "Krea2 tuning report markdown lost fingering hand-ownership weak-case evidence")
    _expect("provisional_generator_patch" in markdown, "Krea2 tuning report markdown lost provisional generator decision")
    _expect("wand-7979797979-two-woman-teal-repeat-proven" in markdown, "Krea2 tuning report markdown lost wand evidence id")
    _expect("single continuous teal wand" in markdown, "Krea2 tuning report markdown lost wand single-device evidence")
    _expect("ready-1123581321-source52-wet-aftermath-hierarchy" in markdown, "Krea2 tuning report markdown lost ready aftermath evidence id")
    _expect("wet aftermath hierarchy" in markdown, "Krea2 tuning report markdown lost ready wet hierarchy evidence")
    _expect("spread-3141592653-source50-47-raised-knee-v-frame" in markdown, "Krea2 tuning report markdown lost spread evidence id")
    _expect("atlas spread hierarchy" in markdown, "Krea2 tuning report markdown lost spread atlas hierarchy evidence")
    _expect("blowjob-top-4242424242-turn67-70-nadir-floor-plane-axis" in markdown, "Krea2 tuning report markdown lost blowjob top-view nadir evidence id")
    _expect("nadir-angle" in markdown, "Krea2 tuning report markdown lost blowjob top-view nadir evidence")
    _expect("blowjob-side-5858585858-three-woman-generated-route-proven" in markdown, "Krea2 tuning report markdown lost blowjob side three-woman generated-route evidence id")
    _expect("adult male viewer's own torso starts at the lower edge" in markdown, "Krea2 tuning report markdown lost side-profile self-torso ownership wording")
    _expect("three-woman generated-route repeat" in markdown, "Krea2 tuning report markdown lost side-profile promotion observation")
    _expect("lower-right torso anchor" in markdown, "Krea2 tuning report markdown lost side-profile lower-right torso wording")
    _expect("blowjob-laying-6767676767-source46-50-wide-v-frame" in markdown, "Krea2 tuning report markdown lost blowjob laying evidence id")
    _expect("wide V-frame and low-horizontal torso hierarchy" in markdown, "Krea2 tuning report markdown lost blowjob laying hierarchy wording")
    _expect("blowjob-sitting-7878787878-source46-50-low-mouth-contact" in markdown, "Krea2 tuning report markdown lost blowjob sitting evidence id")
    _expect("low-mouth seated hierarchy" in markdown, "Krea2 tuning report markdown lost blowjob sitting hierarchy wording")
    _expect("missionary-open-8989898989-turn81-84-elevated-edge-support" in markdown, "Krea2 tuning report markdown lost missionary elevated-edge evidence id")
    _expect("flat elevated-support" in markdown, "Krea2 tuning report markdown lost missionary elevated-support evidence")
    _expect("## Stronger Control Cases" in markdown, "Krea2 tuning report markdown lost stronger-control section")
    _expect("hardest" in markdown, "Krea2 tuning report markdown lost hardest-route marker")
    _expect("low priority" in markdown, "Krea2 tuning report markdown lost low-priority marker")
    _expect("pose_or_image_guidance_first" in markdown, "Krea2 tuning report markdown lost control-first marker")
    _expect("## Guide/Fragile Evidence Expansion" in markdown, "Krea2 tuning report markdown lost guide expansion section")
    _expect("multi_seed_multi_woman_matrix" in markdown, "Krea2 tuning report markdown lost expansion target")
    _expect("## Eval Entry Template Commands" not in markdown, "Krea2 tuning report should omit eval template commands when no normal next tests remain")
    _expect(
        "python tools/krea2_record_eval.py --print-template" not in markdown,
        "Krea2 tuning report should omit recorder template commands when no normal next tests remain",
    )
    _expect("--seed <fixed_seed>" not in markdown, "Krea2 tuning report should omit fixed-seed placeholder when no normal next tests remain")
    _expect("pov_ballsucking_low_head" in markdown, "Krea2 tuning report markdown lost candidate variant")
    _expect("pov_footjob_frontal_sole_stroke" in markdown, "Krea2 tuning report markdown lost footjob candidate variant")
    _expect("pov_wand_foreground_tool_contact" in markdown, "Krea2 tuning report markdown lost wand candidate variant")
    _expect("pov_ejaculation_aftermath_open_thigh_candidate" in markdown, "Krea2 tuning report markdown lost ready aftermath candidate variant")
    _expect("pov_sixty_nine_close_reversed_oral" in markdown, "Krea2 tuning report markdown lost sixty-nine unstable variant")
    _expect("needs_stronger_control" in markdown, "Krea2 tuning report markdown lost stronger-control coverage state")
    _expect("pov_spread_open_thigh_presentation" in markdown, "Krea2 tuning report markdown lost spread candidate variant")
    _expect("pov_blowjob_top_down_vertical_shaft" in markdown, "Krea2 tuning report markdown lost blowjob top-view candidate variant")
    _expect("pov_blowjob_side_profile_oral" in markdown, "Krea2 tuning report markdown lost blowjob side candidate variant")
    _expect("pov_blowjob_laying_frontal_oral" in markdown, "Krea2 tuning report markdown lost blowjob laying candidate variant")
    _expect("pov_blowjob_sitting_upright_oral" in markdown, "Krea2 tuning report markdown lost blowjob sitting candidate variant")
    _expect("pov_missionary_open_leg_penetration" in markdown, "Krea2 tuning report markdown lost missionary candidate variant")
    cli_result = subprocess.run(
        [sys.executable, str(ROOT / "krea2_tuning_report.py")],
        cwd=ROOT,
        capture_output=True,
        text=True,
        check=True,
    )
    _expect("# Krea2 Pose Variant Coverage" in cli_result.stdout, "Krea2 tuning report CLI should print markdown coverage")
    _expect("## Next Fixed-Seed Tests" not in cli_result.stdout, "Krea2 tuning report CLI should omit next fixed-seed tests when none remain")
    _expect("pov_missionary_folded_high_leg_penetration" in markdown, "Krea2 tuning report markdown lost folded missionary candidate variant")
    _expect("pov_cowgirl_frontal_straddle_penetration" in markdown, "Krea2 tuning report markdown lost cowgirl candidate variant")
    _expect("cowgirl-frontal-9191919191-fresh-seed-wide-thigh-proven" in markdown, "Krea2 tuning report markdown lost cowgirl fresh-seed promotion evidence id")
    _expect("pov_cowgirl_alt_low_squat_penetration" in markdown, "Krea2 tuning report markdown lost cowgirl alt candidate variant")
    _expect("pov_reverse_cowgirl_back_facing_penetration" in markdown, "Krea2 tuning report markdown lost reverse cowgirl candidate variant")
    _expect("pov_reverse_cowgirl_alt_upright_back_facing_penetration" in markdown, "Krea2 tuning report markdown lost reverse cowgirl alt candidate variant")
    _expect("needs_fixed_seed_tests" not in markdown, "Krea2 tuning report should not list normal fixed-seed gaps after reverse-alt evidence")
    _expect("Prompt cues" not in markdown, "Krea2 tuning report should omit next-test cue section when no normal tests remain")
    _expect("Avoid cues" not in markdown, "Krea2 tuning report should omit next-test avoid section when no normal tests remain")


def smoke_krea2_atlas_refine_manifest_policy() -> None:
    import importlib

    manifest_module = importlib.import_module("krea2_atlas_refine_manifest")
    with tempfile.TemporaryDirectory() as tmpdir:
        root = Path(tmpdir)
        known_prompt = root / "pov_footjob_frontal_sole_stroke_00001_.txt"
        known_image = root / "pov_footjob_frontal_sole_stroke_00001_.png"
        known_sidecar = root / "pov_footjob_frontal_sole_stroke_00001_.json"
        baseline_only_prompt = root / "pov_handjob_upright_centered_00001_.txt"
        baseline_only_image = root / "pov_handjob_upright_centered_00001_.png"
        unknown_prompt = root / "pov_unknown_pose_candidate_00001_.txt"
        unknown_image = root / "pov_unknown_pose_candidate_00001_.png"
        orphan_prompt = root / "pov_blowjob_side_profile_oral_00002_.txt"
        known_prompt.write_text(
            "A controlled same-subject footjob reference prompt with foreground soles.",
            encoding="utf-8",
        )
        known_image.write_bytes(b"fake-png")
        known_sidecar.write_text(
            json.dumps(
                {
                    "seed_metadata": {
                        "sampler_seed": 101,
                        "atlas_cue_seed": 202,
                        "micro_position_seed": 303,
                        "workspace_seed": 404,
                    },
                    "cue_axes": {
                        "foot_position": "soles_more_forward",
                        "workspace_surface": "floor_between_desks",
                    },
                    "score": {
                        "atlas_pose_match": "partial",
                        "workspace_continuity": "pass",
                        "subject_identity": "pass",
                    },
                    "prompt_variants": [
                        {
                            "id": "soles_more_forward",
                            "append_cues": [
                                "the woman's soles press farther forward along the same contact line"
                            ],
                            "reference_images": [
                                "blowjob_top_view/22_blowjob_top_view.png"
                            ],
                            "cue_axes": {
                                "foot_position": "soles_more_forward",
                                "contact_depth": "contact_line_farther_forward",
                            },
                            "seed_metadata": {
                                "atlas_cue_seed": 202,
                                "micro_position_seed": 303,
                            },
                            "notes": "explicit sidecar cue, not invented by the batch builder",
                        }
                    ],
                    "notes": "same-subject seedable foot placement frame",
                },
                ensure_ascii=True,
            ),
            encoding="utf-8",
        )
        baseline_only_prompt.write_text(
            "A controlled same-subject handjob reference prompt awaiting prompt variants.",
            encoding="utf-8",
        )
        baseline_only_image.write_bytes(b"fake-png")
        unknown_prompt.write_text("A controlled same-subject unknown pose reference prompt.", encoding="utf-8")
        unknown_image.write_bytes(b"fake-png")
        orphan_prompt.write_text("A prompt without a matching image should be reported.", encoding="utf-8")

        manifest = manifest_module.build_manifest(root, subject_id="same_woman_001")
        with tempfile.TemporaryDirectory() as duplicate_tmpdir:
            duplicate_root = Path(duplicate_tmpdir)
            duplicate_prompt = duplicate_root / "pov_footjob_frontal_sole_stroke_00001_.txt"
            duplicate_image = duplicate_root / "pov_footjob_frontal_sole_stroke_00001_.png"
            duplicate_sidecar = duplicate_root / "pov_footjob_frontal_sole_stroke_00001_.json"
            duplicate_prompt.write_text(
                "A controlled same-subject footjob reference prompt with foreground soles.",
                encoding="utf-8",
            )
            duplicate_image.write_bytes(b"fake-png")
            duplicate_sidecar.write_text(
                json.dumps(
                    {
                        "seed_metadata": {"sampler_seed": 101},
                        "prompt_variants": [
                            {
                                "id": "duplicate_axis",
                                "append_cues": ["first duplicate cue"],
                            },
                            {
                                "id": "duplicate_axis",
                                "append_cues": ["second duplicate cue"],
                            },
                        ],
                    },
                    ensure_ascii=True,
                ),
                encoding="utf-8",
            )
            try:
                manifest_module.build_manifest(duplicate_root, subject_id="same_woman_001")
            except ValueError as exc:
                _expect(
                    "duplicate_axis" in str(exc) and "duplicated" in str(exc),
                    f"Atlas refine manifest duplicate prompt-variant error should identify the duplicated id: {exc}",
                )
            else:
                raise AssertionError("Atlas refine manifest should reject duplicate sidecar prompt_variant ids")
        with tempfile.TemporaryDirectory() as source_mismatch_tmpdir:
            source_mismatch_root = Path(source_mismatch_tmpdir)
            source_mismatch_prompt = source_mismatch_root / "pov_footjob_frontal_sole_stroke_00001_.txt"
            source_mismatch_image = source_mismatch_root / "pov_footjob_frontal_sole_stroke_00001_.png"
            source_mismatch_sidecar = source_mismatch_root / "pov_footjob_frontal_sole_stroke_00001_.json"
            source_mismatch_prompt.write_text(
                "A controlled same-subject footjob reference prompt with foreground soles.",
                encoding="utf-8",
            )
            source_mismatch_image.write_bytes(b"fake-png")
            source_mismatch_sidecar.write_text(
                json.dumps(
                    {
                        "seed_metadata": {"sampler_seed": 101},
                        "prompt_variants": [
                            {
                                "id": "soles_more_forward",
                                "append_cues": [
                                    "the woman's soles press farther forward along the same contact line"
                                ],
                                "prompt_source": {
                                    "kind": "append_cues",
                                    "prompt_variant_id": "other_axis",
                                    "append_cues": [
                                        "the woman's soles press farther forward along the same contact line"
                                    ],
                                },
                            },
                        ],
                    },
                    ensure_ascii=True,
                ),
                encoding="utf-8",
            )
            try:
                manifest_module.build_manifest(source_mismatch_root, subject_id="same_woman_001")
            except ValueError as exc:
                _expect(
                    "prompt_source.prompt_variant_id" in str(exc)
                    and "soles_more_forward" in str(exc)
                    and "other_axis" in str(exc),
                    f"Atlas refine manifest prompt-source mismatch error should name both ids: {exc}",
                )
            else:
                raise AssertionError("Atlas refine manifest should reject mismatched prompt_source prompt_variant_id")
        with tempfile.TemporaryDirectory() as missing_reference_tmpdir:
            missing_reference_root = Path(missing_reference_tmpdir)
            missing_reference_prompt = missing_reference_root / "pov_footjob_frontal_sole_stroke_00001_.txt"
            missing_reference_image = missing_reference_root / "pov_footjob_frontal_sole_stroke_00001_.png"
            missing_reference_sidecar = missing_reference_root / "pov_footjob_frontal_sole_stroke_00001_.json"
            missing_reference_prompt.write_text(
                "A controlled same-subject footjob reference prompt with foreground soles.",
                encoding="utf-8",
            )
            missing_reference_image.write_bytes(b"fake-png")
            missing_reference_sidecar.write_text(
                json.dumps(
                    {
                        "seed_metadata": {"sampler_seed": 101},
                        "prompt_variants": [
                            {
                                "id": "missing_atlas_reference",
                                "append_cues": [
                                    "the woman's soles press farther forward along the same contact line"
                                ],
                                "reference_images": [
                                    "blowjob_top_view/does_not_exist.png"
                                ],
                            }
                        ],
                    },
                    ensure_ascii=True,
                ),
                encoding="utf-8",
            )
            try:
                manifest_module.build_manifest(missing_reference_root, subject_id="same_woman_001")
            except ValueError as exc:
                _expect(
                    "reference_images" in str(exc) and "does_not_exist.png" in str(exc),
                    f"Atlas refine manifest missing reference-image error should identify the stale atlas path: {exc}",
                )
            else:
                raise AssertionError("Atlas refine manifest should reject missing atlas reference_images when atlas root exists")
        cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--folder",
                str(root),
                "--subject-id",
                "same_woman_001",
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        cli_stdout = cli_result.stdout
        _expect(cli_result.returncode == 0, f"Atlas refine manifest CLI failed: {cli_result.stderr}")
        cli_manifest = json.loads(cli_stdout)
        explicit_manifest_cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--folder",
                str(root),
                "--subject-id",
                "same_woman_001",
                "--print-manifest",
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        _expect(
            explicit_manifest_cli_result.returncode == 0,
            f"Atlas refine explicit manifest CLI failed: {explicit_manifest_cli_result.stderr}",
        )
        explicit_cli_manifest = json.loads(explicit_manifest_cli_result.stdout)
        _expect(
            explicit_cli_manifest == cli_manifest,
            "Atlas refine --print-manifest should match the default manifest CLI output",
        )
        scaffold_cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--folder",
                str(root),
                "--subject-id",
                "same_woman_001",
                "--print-sidecar-scaffold",
                "--variant-key",
                "pov_handjob_upright_centered",
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        scaffold_cli_returncode = scaffold_cli_result.returncode
        scaffold_cli_stdout = scaffold_cli_result.stdout
        scaffold_cli_stderr = scaffold_cli_result.stderr
        baseline_score_cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--folder",
                str(root),
                "--subject-id",
                "same_woman_001",
                "--print-baseline-score-sheet",
                "--variant-key",
                "pov_handjob_upright_centered",
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        baseline_score_cli_returncode = baseline_score_cli_result.returncode
        baseline_score_cli_stdout = baseline_score_cli_result.stdout
        baseline_score_cli_stderr = baseline_score_cli_result.stderr
        prompt_noise_cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--folder",
                str(root),
                "--subject-id",
                "same_woman_001",
                "--print-prompt-noise-report",
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        prompt_noise_cli_returncode = prompt_noise_cli_result.returncode
        prompt_noise_cli_stdout = prompt_noise_cli_result.stdout
        prompt_noise_cli_stderr = prompt_noise_cli_result.stderr
        prompt_cleanup_cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--folder",
                str(root),
                "--subject-id",
                "same_woman_001",
                "--print-prompt-cleanup-sheet",
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        prompt_cleanup_cli_returncode = prompt_cleanup_cli_result.returncode
        prompt_cleanup_cli_stdout = prompt_cleanup_cli_result.stdout
        prompt_cleanup_cli_stderr = prompt_cleanup_cli_result.stderr

    _expect(manifest.get("schema") == "sxcp_krea2_atlas_refine_manifest_v1", "Atlas refine manifest lost schema")
    _expect(manifest.get("subject_id") == "same_woman_001", "Atlas refine manifest lost subject id")
    _expect(manifest.get("entry_count") == 3, f"Atlas refine manifest should include paired prompt/image entries: {manifest}")
    _expect(manifest.get("missing_pair_count") == 1, "Atlas refine manifest should report orphan prompt/image files")
    _expect(manifest.get("unknown_variant_count") == 1, "Atlas refine manifest should count unknown variant keys")
    entries = manifest.get("entries") or []
    by_variant = {entry.get("variant_key"): entry for entry in entries}
    footjob = by_variant.get("pov_footjob_frontal_sole_stroke") or {}
    _expect(footjob.get("known_variant") is True, "Atlas refine manifest should validate known variant keys")
    _expect(footjob.get("prompt_text", "").startswith("A controlled same-subject footjob"), "Atlas refine manifest lost prompt text")
    _expect(footjob.get("prompt_sha256"), "Atlas refine manifest should include a prompt hash for drift checks")
    _expect(footjob.get("image_size_bytes") == len(b"fake-png"), "Atlas refine manifest should record image byte size")
    seed_metadata = footjob.get("seed_metadata") or {}
    _expect(seed_metadata.get("sampler_seed") == 101, "Atlas refine manifest should merge sampler seed metadata")
    _expect(seed_metadata.get("atlas_cue_seed") == 202, "Atlas refine manifest should merge atlas cue seed metadata")
    _expect(seed_metadata.get("micro_position_seed") == 303, "Atlas refine manifest should merge micro-position seed metadata")
    _expect(seed_metadata.get("workspace_seed") == 404, "Atlas refine manifest should merge workspace seed metadata")
    _expect(seed_metadata.get("generator_seed") is None, "Atlas refine manifest should keep missing generator seed explicit")
    cue_axes = footjob.get("cue_axes") or {}
    for key in (
        "contact_depth",
        "hand_position",
        "foot_position",
        "body_angle",
        "camera_height",
        "workspace_surface",
        "clothing_visibility",
        "expression_eye_detail",
        "anatomy_shape_detail",
    ):
        _expect(key in cue_axes, f"Atlas refine manifest missing cue-axis slot {key}")
    _expect(cue_axes.get("foot_position") == "soles_more_forward", "Atlas refine manifest should merge cue-axis metadata")
    _expect(cue_axes.get("workspace_surface") == "floor_between_desks", "Atlas refine manifest should merge workspace cue axis")
    score = footjob.get("score") or {}
    for key in (
        "atlas_pose_match",
        "contact_match",
        "pose_ownership",
        "workspace_continuity",
        "clothing_visibility",
        "subject_identity",
        "expression_eye_control",
        "anatomy_proportion",
        "prompt_noise",
    ):
        _expect(key in score, f"Atlas refine manifest missing score slot {key}")
    _expect(score.get("atlas_pose_match") == "partial", "Atlas refine manifest should merge atlas pose score")
    _expect(score.get("workspace_continuity") == "pass", "Atlas refine manifest should merge workspace continuity score")
    _expect(score.get("subject_identity") == "pass", "Atlas refine manifest should merge subject identity score")
    _expect(footjob.get("notes") == "same-subject seedable foot placement frame", "Atlas refine manifest should merge notes")
    prompt_variants = footjob.get("prompt_variants") or []
    _expect(len(prompt_variants) == 1, f"Atlas refine manifest should keep sidecar prompt variants: {prompt_variants}")
    _expect(prompt_variants[0].get("id") == "soles_more_forward", "Atlas refine manifest lost prompt variant id")
    _expect(
        prompt_variants[0].get("reference_images") == ["blowjob_top_view/22_blowjob_top_view.png"],
        "Atlas refine manifest should keep nearest atlas reference-image provenance on prompt variants",
    )
    baseline_score_sheet = manifest_module.build_baseline_score_sheet(manifest)
    _expect(
        baseline_score_sheet.get("schema") == "sxcp_atlas_refine_baseline_score_sheet_v1",
        "Atlas refine baseline score sheet lost schema",
    )
    _expect(
        baseline_score_sheet.get("entry_count") == 3,
        "Atlas refine baseline score sheet should include every paired manifest entry",
    )
    _expect(
        baseline_score_sheet.get("unscored_count") == 2,
        "Atlas refine baseline score sheet should count unscored baselines",
    )
    _expect(
        baseline_score_sheet.get("partially_scored_count") == 1,
        "Atlas refine baseline score sheet should count partially scored baselines separately",
    )
    baseline_score_entries = {
        entry.get("variant_key"): entry for entry in baseline_score_sheet.get("entries") or []
    }
    footjob_baseline_score = baseline_score_entries.get("pov_footjob_frontal_sole_stroke") or {}
    _expect(
        footjob_baseline_score.get("score", {}).get("atlas_pose_match") == "partial",
        "Atlas refine baseline score sheet should preserve existing sidecar baseline scores",
    )
    _expect(
        footjob_baseline_score.get("score_state") == "partially_scored",
        "Atlas refine baseline score sheet should flag incomplete baseline scores as partial",
    )
    handjob_baseline_score = baseline_score_entries.get("pov_handjob_upright_centered") or {}
    _expect(
        handjob_baseline_score.get("score_state") == "needs_visual_score",
        "Atlas refine baseline score sheet should flag unscored baseline-only known entries",
    )
    _expect(
        baseline_score_cli_returncode == 0,
        f"Atlas refine baseline score sheet CLI failed: {baseline_score_cli_stderr}",
    )
    cli_baseline_score = json.loads(baseline_score_cli_stdout)
    _expect(
        cli_baseline_score.get("entry_count") == 1,
        "Atlas refine baseline score sheet CLI should honor variant-key filtering",
    )
    scored_baseline_sheet = json.loads(json.dumps(baseline_score_sheet))
    scored_baseline_entries = {
        entry.get("variant_key"): entry for entry in scored_baseline_sheet.get("entries") or []
    }
    scored_footjob_baseline = scored_baseline_entries.get("pov_footjob_frontal_sole_stroke") or {}
    scored_footjob_baseline["analysis_notes"] = "Partial baseline is still useful as scored evidence."
    scored_handjob_baseline = scored_baseline_entries.get("pov_handjob_upright_centered") or {}
    scored_handjob_baseline["cue_axes"]["hand_position"] = "centered_upright_base_grip"
    scored_handjob_baseline["score"].update(
        {
            "atlas_pose_match": "pass",
            "contact_match": "pass",
            "pose_ownership": "pass",
            "workspace_continuity": "pass",
            "clothing_visibility": "pass",
            "subject_identity": "pass",
            "expression_eye_control": "pass",
            "anatomy_proportion": "pass",
            "prompt_noise": "pass",
        }
    )
    scored_handjob_baseline["analysis_notes"] = "Baseline handjob frame preserves subject, pose, and workspace."
    baseline_score_update_draft = manifest_module.build_baseline_score_update_draft(scored_baseline_sheet)
    _expect(
        baseline_score_update_draft.get("schema") == "sxcp_atlas_refine_baseline_score_update_draft_v1",
        "Atlas refine baseline score update draft lost schema",
    )
    _expect(
        baseline_score_update_draft.get("update_count") == 2,
        f"Atlas refine baseline score update draft should include scored and partially scored baselines: {baseline_score_update_draft}",
    )
    baseline_score_updates = {
        update.get("variant_key"): update for update in baseline_score_update_draft.get("updates") or []
    }
    footjob_baseline_update = baseline_score_updates.get("pov_footjob_frontal_sole_stroke") or {}
    _expect(
        footjob_baseline_update.get("score_state") == "partially_scored",
        "Atlas refine baseline score update draft should preserve partial baseline scoring",
    )
    _expect(
        "prompt_variants" not in footjob_baseline_update,
        "Atlas refine baseline score update draft must not carry prompt variants",
    )
    handjob_baseline_update = baseline_score_updates.get("pov_handjob_upright_centered") or {}
    _expect(
        handjob_baseline_update.get("score_state") == "scored_pass",
        "Atlas refine baseline score update draft should recalculate filled baseline scores",
    )
    _expect(
        handjob_baseline_update.get("score", {}).get("pose_ownership") == "pass",
        "Atlas refine baseline score update draft should keep manual baseline score values",
    )
    _expect(
        handjob_baseline_update.get("cue_axes", {}).get("hand_position") == "centered_upright_base_grip",
        "Atlas refine baseline score update draft should keep manually reviewed cue axes",
    )
    baseline_score_update_validation = manifest_module.validate_baseline_score_update_draft(baseline_score_update_draft)
    _expect(
        baseline_score_update_validation.get("schema") == "sxcp_atlas_refine_baseline_score_update_validation_v1",
        "Atlas refine baseline score update validation lost schema",
    )
    _expect(
        baseline_score_update_validation.get("valid") is True,
        f"Atlas refine baseline score update draft should validate: {baseline_score_update_validation}",
    )
    _expect(
        baseline_score_update_validation.get("warning_count", 0) >= 1,
        "Atlas refine baseline score update validation should warn about partial baseline scores",
    )
    invalid_baseline_score_update = json.loads(json.dumps(baseline_score_update_draft))
    invalid_baseline_score_update["updates"][0]["prompt_variants"] = []
    invalid_baseline_score_update["updates"][1]["sidecar_filename"] = "wrong.json"
    invalid_baseline_score_validation = manifest_module.validate_baseline_score_update_draft(invalid_baseline_score_update)
    _expect(
        invalid_baseline_score_validation.get("valid") is False,
        "Atlas refine baseline score update validation should reject contaminated baseline updates",
    )
    _expect(
        any("prompt_variants" in error for error in invalid_baseline_score_validation.get("errors", [])),
        f"Atlas refine baseline score update validation should reject prompt variant fields: {invalid_baseline_score_validation}",
    )
    _expect(
        any("sidecar_filename" in error for error in invalid_baseline_score_validation.get("errors", [])),
        f"Atlas refine baseline score update validation should reject sidecar filename drift: {invalid_baseline_score_validation}",
    )
    with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as baseline_sheet_handle:
        json.dump(scored_baseline_sheet, baseline_sheet_handle)
        cli_baseline_sheet_path = Path(baseline_sheet_handle.name)
    try:
        baseline_update_cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--print-baseline-score-update-draft",
                "--baseline-score-sheet-json",
                str(cli_baseline_sheet_path),
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
    finally:
        cli_baseline_sheet_path.unlink(missing_ok=True)
    _expect(
        baseline_update_cli_result.returncode == 0,
        f"Atlas refine baseline score update draft CLI failed: {baseline_update_cli_result.stderr}",
    )
    cli_baseline_update_draft = json.loads(baseline_update_cli_result.stdout)
    _expect(
        cli_baseline_update_draft.get("update_count") == 2,
        "Atlas refine baseline score update draft CLI should keep scored baseline updates",
    )
    with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as baseline_update_handle:
        json.dump(baseline_score_update_draft, baseline_update_handle)
        cli_baseline_update_path = Path(baseline_update_handle.name)
    try:
        baseline_update_validation_cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--validate-baseline-score-update-draft",
                "--baseline-score-update-draft-json",
                str(cli_baseline_update_path),
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
    finally:
        cli_baseline_update_path.unlink(missing_ok=True)
    _expect(
        baseline_update_validation_cli_result.returncode == 0,
        f"Atlas refine baseline score update validation CLI failed: {baseline_update_validation_cli_result.stderr}",
    )
    cli_baseline_update_validation = json.loads(baseline_update_validation_cli_result.stdout)
    _expect(
        cli_baseline_update_validation.get("valid") is True,
        "Atlas refine baseline score update validation CLI should validate the draft",
    )
    with tempfile.TemporaryDirectory() as baseline_apply_tmpdir:
        baseline_apply_root = Path(baseline_apply_tmpdir)
        baseline_footjob_sidecar_path = baseline_apply_root / "pov_footjob_frontal_sole_stroke_00001_.json"
        baseline_footjob_sidecar_path.write_text(
            json.dumps(
                {
                    "notes": "preserve prompt variant sidecar notes",
                    "prompt_variants": [
                        {
                            "id": "existing_axis",
                            "text": "Existing tested prompt variant.",
                            "cue_axes": {"foot_position": "existing"},
                        }
                    ],
                },
                ensure_ascii=True,
            ),
            encoding="utf-8",
        )
        baseline_apply_report = manifest_module.apply_baseline_score_update_draft(
            baseline_score_update_draft,
            baseline_apply_root,
        )
        _expect(
            baseline_apply_report.get("schema") == "sxcp_atlas_refine_baseline_score_apply_report_v1",
            "Atlas refine baseline score apply report lost schema",
        )
        _expect(baseline_apply_report.get("applied") is True, "Atlas refine baseline score apply should mark applied")
        _expect(
            baseline_apply_report.get("updated_file_count") == 2,
            "Atlas refine baseline score apply should update each scored baseline sidecar",
        )
        applied_footjob_baseline_sidecar = json.loads(baseline_footjob_sidecar_path.read_text(encoding="utf-8"))
        _expect(
            [variant.get("id") for variant in applied_footjob_baseline_sidecar.get("prompt_variants", [])]
            == ["existing_axis"],
            "Atlas refine baseline score apply must preserve existing prompt variants without adding new ones",
        )
        _expect(
            applied_footjob_baseline_sidecar.get("score", {}).get("atlas_pose_match") == "partial",
            "Atlas refine baseline score apply should write partial baseline score metadata",
        )
        _expect(
            applied_footjob_baseline_sidecar.get("baseline_analysis_notes") == "Partial baseline is still useful as scored evidence.",
            "Atlas refine baseline score apply should keep manual baseline analysis notes",
        )
        baseline_handjob_sidecar_path = baseline_apply_root / "pov_handjob_upright_centered_00001_.json"
        applied_handjob_baseline_sidecar = json.loads(baseline_handjob_sidecar_path.read_text(encoding="utf-8"))
        _expect(
            applied_handjob_baseline_sidecar.get("score", {}).get("pose_ownership") == "pass",
            "Atlas refine baseline score apply should write passing baseline score metadata",
        )
        _expect(
            applied_handjob_baseline_sidecar.get("cue_axes", {}).get("hand_position") == "centered_upright_base_grip",
            "Atlas refine baseline score apply should write manually reviewed cue axes",
        )
        with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as baseline_apply_handle:
            json.dump(baseline_score_update_draft, baseline_apply_handle)
            cli_baseline_apply_path = Path(baseline_apply_handle.name)
        try:
            baseline_apply_cli_result = subprocess.run(
                [
                    sys.executable,
                    str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                    "--apply-baseline-score-update-draft",
                    "--baseline-score-update-draft-json",
                    str(cli_baseline_apply_path),
                    "--folder",
                    str(baseline_apply_root),
                ],
                cwd=ROOT,
                capture_output=True,
                text=True,
                check=False,
            )
        finally:
            cli_baseline_apply_path.unlink(missing_ok=True)
        _expect(
            baseline_apply_cli_result.returncode == 0,
            f"Atlas refine baseline score apply CLI failed: {baseline_apply_cli_result.stderr}",
        )
        cli_baseline_apply_report = json.loads(baseline_apply_cli_result.stdout)
        _expect(
            cli_baseline_apply_report.get("applied") is True,
            "Atlas refine baseline score apply CLI should report applied",
        )
        (baseline_apply_root / "pov_footjob_frontal_sole_stroke_00001_.txt").write_text(
            footjob.get("prompt_text", ""),
            encoding="utf-8",
        )
        (baseline_apply_root / "pov_footjob_frontal_sole_stroke_00001_.png").write_bytes(b"fake-png")
        (baseline_apply_root / "pov_handjob_upright_centered_00001_.txt").write_text(
            handjob_baseline_score.get("prompt_text", ""),
            encoding="utf-8",
        )
        (baseline_apply_root / "pov_handjob_upright_centered_00001_.png").write_bytes(b"fake-png")
        baseline_applied_manifest = manifest_module.build_manifest(baseline_apply_root, subject_id="same_woman_001")
        baseline_applied_sheet = manifest_module.build_baseline_score_sheet(baseline_applied_manifest)
        baseline_applied_entries = {
            entry.get("variant_key"): entry for entry in baseline_applied_sheet.get("entries") or []
        }
        _expect(
            baseline_applied_entries.get("pov_footjob_frontal_sole_stroke", {}).get("score_state")
            == "partially_scored",
            "Atlas refine baseline score apply should rescan partial baseline score state",
        )
    _expect(
        baseline_applied_entries.get("pov_handjob_upright_centered", {}).get("score_state") == "scored_pass",
        "Atlas refine baseline score apply should rescan passing baseline score state",
    )
    clean_noise_report = manifest_module.build_prompt_noise_report(manifest)
    _expect(
        clean_noise_report.get("schema") == "sxcp_atlas_refine_prompt_noise_report_v1",
        "Atlas refine prompt-noise report lost schema",
    )
    _expect(
        clean_noise_report.get("issue_count") == 0,
        f"Atlas refine prompt-noise report should not flag clean direct atlas prompts: {clean_noise_report}",
    )
    _expect(
        prompt_noise_cli_returncode == 0,
        f"Atlas refine prompt-noise report CLI failed: {prompt_noise_cli_stderr}",
    )
    cli_prompt_noise_report = json.loads(prompt_noise_cli_stdout)
    _expect(
        cli_prompt_noise_report.get("issue_count") == 0,
        "Atlas refine prompt-noise report CLI should preserve clean issue count",
    )
    clean_cleanup_sheet = manifest_module.build_prompt_cleanup_sheet(manifest)
    _expect(
        clean_cleanup_sheet.get("schema") == "sxcp_atlas_refine_prompt_cleanup_sheet_v1",
        "Atlas refine prompt-cleanup sheet lost schema",
    )
    _expect(
        clean_cleanup_sheet.get("cleanup_item_count") == 0,
        f"Atlas refine prompt-cleanup sheet should not emit cleanup items for clean prompts: {clean_cleanup_sheet}",
    )
    _expect(
        prompt_cleanup_cli_returncode == 0,
        f"Atlas refine prompt-cleanup sheet CLI failed: {prompt_cleanup_cli_stderr}",
    )
    cli_prompt_cleanup_sheet = json.loads(prompt_cleanup_cli_stdout)
    _expect(
        cli_prompt_cleanup_sheet.get("cleanup_item_count") == 0,
        "Atlas refine prompt-cleanup sheet CLI should preserve clean cleanup count",
    )
    noisy_manifest = json.loads(json.dumps(manifest))
    noisy_entries = {
        entry.get("variant_key"): entry for entry in noisy_manifest.get("entries") or []
    }
    noisy_handjob = noisy_entries.get("pov_handjob_upright_centered") or {}
    noisy_handjob["prompt_text"] += (
        " Keep the visible partner and the action primary; context stays beside or behind the bodies."
        " The viewer looks straight down from his torso."
        " The viewer looks straight down from his torso."
    )
    noisy_handjob["prompt_sha256"] = manifest_module._sha256_text(noisy_handjob["prompt_text"])
    noisy_footjob = noisy_entries.get("pov_footjob_frontal_sole_stroke") or {}
    noisy_footjob.setdefault("prompt_variants", []).append(
        {
            "id": "noisy_option_wording",
            "append_cues": [
                "either hand or foot may move optionally while the POV foreground clothing cue stays visible"
            ],
            "cue_axes": {"foot_position": "ambiguous_option_axis"},
        }
    )
    noisy_footjob.setdefault("prompt_variants", []).append(
        {
            "id": "noisy_negative_text",
            "text": "No lower torso should appear; keep the visible partner primary.",
            "cue_axes": {"body_angle": "negative_meta_text"},
        }
    )
    noisy_noise_report = manifest_module.build_prompt_noise_report(noisy_manifest)
    _expect(
        noisy_noise_report.get("issue_count", 0) >= 6,
        f"Atlas refine prompt-noise report should flag option/meta/negative prompt noise: {noisy_noise_report}",
    )
    noisy_issue_codes = [
        issue.get("code")
        for entry in noisy_noise_report.get("entries") or []
        for issue in entry.get("issues") or []
    ]
    _expect(
        "meta_instruction" in noisy_issue_codes,
        f"Atlas refine prompt-noise report should flag meta instructions: {noisy_noise_report}",
    )
    _expect(
        "option_word" in noisy_issue_codes,
        f"Atlas refine prompt-noise report should flag option-list wording: {noisy_noise_report}",
    )
    _expect(
        "negative_conditioning" in noisy_issue_codes,
        f"Atlas refine prompt-noise report should flag negative positive-channel wording: {noisy_noise_report}",
    )
    _expect(
        "duplicate_phrase" in noisy_issue_codes,
        f"Atlas refine prompt-noise report should flag repeated prompt phrases: {noisy_noise_report}",
    )
    _expect(
        any(issue.get("context") == "prompt_variant_append_cue" for entry in noisy_noise_report.get("entries") or [] for issue in entry.get("issues") or []),
        f"Atlas refine prompt-noise report should localize append-cue noise: {noisy_noise_report}",
    )
    noisy_coverage_report = manifest_module.build_coverage_report(noisy_manifest)
    _expect(
        noisy_coverage_report.get("needs_prompt_cleanup_count") == 2,
        f"Atlas refine coverage should count noisy entries as cleanup-needed: {noisy_coverage_report}",
    )
    _expect(
        noisy_coverage_report.get("prompt_noise_issue_count", 0) >= 6,
        f"Atlas refine coverage should carry prompt-noise issue totals: {noisy_coverage_report}",
    )
    noisy_coverage_by_variant = {
        entry.get("variant_key"): entry for entry in noisy_coverage_report.get("entries") or []
    }
    noisy_handjob_coverage = noisy_coverage_by_variant.get("pov_handjob_upright_centered") or {}
    _expect(
        noisy_handjob_coverage.get("state") == "needs_prompt_cleanup",
        f"Atlas refine coverage should block noisy baseline-only entries before scoring: {noisy_handjob_coverage}",
    )
    noisy_footjob_coverage = noisy_coverage_by_variant.get("pov_footjob_frontal_sole_stroke") or {}
    _expect(
        noisy_footjob_coverage.get("state") == "needs_prompt_cleanup",
        f"Atlas refine coverage should block noisy sidecar variants before seed selection: {noisy_footjob_coverage}",
    )
    _expect(
        noisy_footjob_coverage.get("prompt_noise_issue_count", 0) >= 4,
        f"Atlas refine coverage should localize sidecar prompt-noise counts: {noisy_footjob_coverage}",
    )
    noisy_cleanup_sheet = manifest_module.build_prompt_cleanup_sheet(noisy_manifest)
    _expect(
        noisy_cleanup_sheet.get("schema") == "sxcp_atlas_refine_prompt_cleanup_sheet_v1",
        "Atlas refine prompt-cleanup sheet lost schema for noisy prompts",
    )
    _expect(
        noisy_cleanup_sheet.get("cleanup_item_count") == 3,
        f"Atlas refine prompt-cleanup sheet should group issues by editable source text: {noisy_cleanup_sheet}",
    )
    _expect(
        noisy_cleanup_sheet.get("issue_count", 0) >= 6,
        f"Atlas refine prompt-cleanup sheet should carry all issue counts: {noisy_cleanup_sheet}",
    )
    cleanup_items = {
        (item.get("variant_key"), item.get("context"), item.get("prompt_variant_id")): item
        for item in noisy_cleanup_sheet.get("cleanup_items") or []
    }
    handjob_cleanup = cleanup_items.get(("pov_handjob_upright_centered", "baseline_prompt", "")) or {}
    _expect(
        handjob_cleanup.get("source_type") == "prompt_file",
        f"Atlas refine prompt-cleanup sheet should point baseline cleanup at prompt files: {handjob_cleanup}",
    )
    _expect(
        handjob_cleanup.get("source_path") == handjob_baseline_score.get("prompt_path"),
        f"Atlas refine prompt-cleanup sheet should keep exact prompt path: {handjob_cleanup}",
    )
    _expect(
        handjob_cleanup.get("source_prompt_sha256") == noisy_handjob.get("prompt_sha256"),
        f"Atlas refine prompt-cleanup sheet should preserve baseline source prompt hash: {handjob_cleanup}",
    )
    _expect(
        "Keep the visible partner" in handjob_cleanup.get("current_text", ""),
        "Atlas refine prompt-cleanup sheet should preserve current noisy baseline text",
    )
    _expect(
        handjob_cleanup.get("current_text_sha256") == manifest_module._sha256_text(handjob_cleanup.get("current_text", "")),
        f"Atlas refine prompt-cleanup sheet should hash current baseline text for drift checks: {handjob_cleanup}",
    )
    _expect(
        handjob_cleanup.get("replacement_text") == "",
        "Atlas refine prompt-cleanup sheet should leave replacement text blank for manual cleanup",
    )
    append_cleanup = cleanup_items.get(("pov_footjob_frontal_sole_stroke", "prompt_variant_append_cue", "noisy_option_wording")) or {}
    _expect(
        append_cleanup.get("source_type") == "sidecar_prompt_variant_append_cue",
        f"Atlas refine prompt-cleanup sheet should point append-cue cleanup at sidecar variants: {append_cleanup}",
    )
    _expect(
        append_cleanup.get("cue_index") == 0,
        "Atlas refine prompt-cleanup sheet should preserve append-cue index",
    )
    _expect(
        append_cleanup.get("sidecar_filename") == "pov_footjob_frontal_sole_stroke_00001_.json",
        f"Atlas refine prompt-cleanup sheet should preserve same-stem sidecar filename: {append_cleanup}",
    )
    _expect(
        append_cleanup.get("source_prompt_sha256") == noisy_footjob.get("prompt_sha256"),
        f"Atlas refine prompt-cleanup sheet should preserve sidecar baseline source prompt hash: {append_cleanup}",
    )
    _expect(
        append_cleanup.get("current_text_sha256") == manifest_module._sha256_text(append_cleanup.get("current_text", "")),
        f"Atlas refine prompt-cleanup sheet should hash current append-cue text for drift checks: {append_cleanup}",
    )
    invalid_cleanup_sheet = json.loads(json.dumps(noisy_cleanup_sheet))
    invalid_cleanup_sheet["cleanup_items"][0]["replacement_text"] = ""
    invalid_cleanup_sheet["cleanup_items"][1]["replacement_text"] = "either noisy option remains"
    invalid_cleanup_sheet["cleanup_items"][2]["current_text_sha256"] = "stale-hash"
    invalid_cleanup_sheet["cleanup_items"][2]["source_prompt_sha256"] = ""
    invalid_cleanup_validation = manifest_module.validate_prompt_cleanup_sheet(invalid_cleanup_sheet)
    _expect(
        invalid_cleanup_validation.get("schema") == "sxcp_atlas_refine_prompt_cleanup_validation_v1",
        "Atlas refine prompt-cleanup validation lost schema",
    )
    _expect(
        invalid_cleanup_validation.get("valid") is False,
        f"Atlas refine prompt-cleanup validation should reject blank or noisy replacements: {invalid_cleanup_validation}",
    )
    _expect(
        any("replacement_text is required" in error for error in invalid_cleanup_validation.get("errors", [])),
        f"Atlas refine prompt-cleanup validation should reject blank replacements: {invalid_cleanup_validation}",
    )
    _expect(
        any("replacement_text still has prompt-noise issues" in error for error in invalid_cleanup_validation.get("errors", [])),
        f"Atlas refine prompt-cleanup validation should reject noisy replacements: {invalid_cleanup_validation}",
    )
    _expect(
        any("current_text_sha256" in error for error in invalid_cleanup_validation.get("errors", [])),
        f"Atlas refine prompt-cleanup validation should reject stale current-text hashes: {invalid_cleanup_validation}",
    )
    _expect(
        any("source_prompt_sha256" in error for error in invalid_cleanup_validation.get("errors", [])),
        f"Atlas refine prompt-cleanup validation should reject missing source prompt hashes: {invalid_cleanup_validation}",
    )
    cleanup_sheet_for_apply = json.loads(json.dumps(noisy_cleanup_sheet))
    cleanup_sheet_for_apply["cleanup_items"] = [
        json.loads(json.dumps(handjob_cleanup)),
        json.loads(json.dumps(append_cleanup)),
    ]
    cleanup_sheet_for_apply["cleanup_items"][0]["replacement_text"] = (
        "A controlled same-subject handjob reference prompt with direct centered upright contact."
    )
    cleanup_sheet_for_apply["cleanup_items"][0]["cleanup_notes"] = "manual direct rewrite"
    cleanup_sheet_for_apply["cleanup_items"][1]["replacement_text"] = (
        "the woman's foot presses forward along the same contact line"
    )
    cleanup_validation = manifest_module.validate_prompt_cleanup_sheet(cleanup_sheet_for_apply)
    _expect(
        cleanup_validation.get("valid") is True,
        f"Atlas refine prompt-cleanup validation should accept clean manual replacements: {cleanup_validation}",
    )
    stale_source_hash_cleanup_sheet = json.loads(json.dumps(cleanup_sheet_for_apply))
    stale_source_hash_cleanup_sheet["cleanup_items"][0]["source_prompt_sha256"] = "stale-source-hash"
    stale_source_hash_validation = manifest_module.validate_prompt_cleanup_sheet(stale_source_hash_cleanup_sheet)
    _expect(
        stale_source_hash_validation.get("valid") is False
        and any(
            "source_prompt_sha256 must match current_text_sha256"
            in error
            for error in stale_source_hash_validation.get("errors", [])
        ),
        f"Atlas refine prompt-cleanup validation should reject stale baseline source prompt hashes: {stale_source_hash_validation}",
    )
    with tempfile.TemporaryDirectory() as cleanup_tmpdir:
        cleanup_root = Path(cleanup_tmpdir)
        cleanup_handjob_prompt = cleanup_root / "pov_handjob_upright_centered_00001_.txt"
        cleanup_handjob_image = cleanup_root / "pov_handjob_upright_centered_00001_.png"
        cleanup_footjob_prompt = cleanup_root / "pov_footjob_frontal_sole_stroke_00001_.txt"
        cleanup_footjob_image = cleanup_root / "pov_footjob_frontal_sole_stroke_00001_.png"
        cleanup_footjob_sidecar = cleanup_root / "pov_footjob_frontal_sole_stroke_00001_.json"
        cleanup_handjob_prompt.write_text(handjob_cleanup.get("current_text", ""), encoding="utf-8")
        cleanup_handjob_image.write_bytes(b"fake-png")
        cleanup_footjob_prompt.write_text(noisy_footjob.get("prompt_text", ""), encoding="utf-8")
        cleanup_footjob_image.write_bytes(b"fake-png")
        cleanup_footjob_sidecar.write_text(
            json.dumps(
                {
                    "notes": "preserve cleanup sidecar notes",
                    "prompt_variants": [
                        {
                            "id": "noisy_option_wording",
                            "append_cues": [
                                append_cleanup.get("current_text", ""),
                                "existing second cue stays",
                            ],
                            "cue_axes": {"foot_position": "ambiguous_option_axis"},
                        }
                    ],
                },
                ensure_ascii=True,
            ),
            encoding="utf-8",
        )
        cleanup_sheet_for_apply["cleanup_items"][0]["source_path"] = str(cleanup_handjob_prompt)
        cleanup_sheet_for_apply["cleanup_items"][1]["source_path"] = str(cleanup_footjob_sidecar)
        cleanup_apply_report = manifest_module.apply_prompt_cleanup_sheet(cleanup_sheet_for_apply, cleanup_root)
        _expect(
            cleanup_apply_report.get("schema") == "sxcp_atlas_refine_prompt_cleanup_apply_report_v1",
            "Atlas refine prompt-cleanup apply report lost schema",
        )
        _expect(
            cleanup_apply_report.get("applied") is True,
            f"Atlas refine prompt-cleanup apply should accept clean manual replacements: {cleanup_apply_report}",
        )
        _expect(
            cleanup_apply_report.get("updated_file_count") == 2,
            "Atlas refine prompt-cleanup apply should update prompt file and sidecar file",
        )
        _expect(
            cleanup_handjob_prompt.read_text(encoding="utf-8")
            == "A controlled same-subject handjob reference prompt with direct centered upright contact.",
            "Atlas refine prompt-cleanup apply should rewrite the prompt file with manual replacement",
        )
        applied_cleanup_sidecar = json.loads(cleanup_footjob_sidecar.read_text(encoding="utf-8"))
        applied_cleanup_variant = (applied_cleanup_sidecar.get("prompt_variants") or [{}])[0]
        _expect(
            applied_cleanup_sidecar.get("notes") == "preserve cleanup sidecar notes",
            "Atlas refine prompt-cleanup apply should preserve unrelated sidecar metadata",
        )
        _expect(
            applied_cleanup_variant.get("append_cues", [])[0]
            == "the woman's foot presses forward along the same contact line",
            "Atlas refine prompt-cleanup apply should replace the targeted append cue",
        )
        _expect(
            applied_cleanup_variant.get("append_cues", [])[1] == "existing second cue stays",
            "Atlas refine prompt-cleanup apply should preserve untargeted append cues",
        )
        cleanup_manifest = manifest_module.build_manifest(cleanup_root, subject_id="same_woman_001")
        cleanup_noise_report = manifest_module.build_prompt_noise_report(cleanup_manifest)
        _expect(
            cleanup_noise_report.get("issue_count") == 0,
            f"Atlas refine prompt-cleanup apply should remove prompt-noise issues from the cleaned temp deck: {cleanup_noise_report}",
        )
        with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as cleanup_sheet_handle:
            json.dump(cleanup_sheet_for_apply, cleanup_sheet_handle)
            cli_cleanup_sheet_path = Path(cleanup_sheet_handle.name)
        try:
            cleanup_validation_cli_result = subprocess.run(
                [
                    sys.executable,
                    str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                    "--validate-prompt-cleanup-sheet",
                    "--prompt-cleanup-sheet-json",
                    str(cli_cleanup_sheet_path),
                ],
                cwd=ROOT,
                capture_output=True,
                text=True,
                check=False,
            )
        finally:
            cli_cleanup_sheet_path.unlink(missing_ok=True)
        _expect(
            cleanup_validation_cli_result.returncode == 0,
            f"Atlas refine prompt-cleanup validation CLI failed: {cleanup_validation_cli_result.stderr}",
        )
        with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as cleanup_apply_sheet_handle:
            json.dump(cleanup_sheet_for_apply, cleanup_apply_sheet_handle)
            cli_cleanup_apply_sheet_path = Path(cleanup_apply_sheet_handle.name)
        try:
            cleanup_apply_cli_result = subprocess.run(
                [
                    sys.executable,
                    str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                    "--apply-prompt-cleanup-sheet",
                    "--prompt-cleanup-sheet-json",
                    str(cli_cleanup_apply_sheet_path),
                    "--folder",
                    str(cleanup_root),
                ],
                cwd=ROOT,
                capture_output=True,
                text=True,
                check=False,
            )
        finally:
            cli_cleanup_apply_sheet_path.unlink(missing_ok=True)
        _expect(
            cleanup_apply_cli_result.returncode == 0,
            f"Atlas refine prompt-cleanup apply CLI failed: {cleanup_apply_cli_result.stderr}",
        )
        cli_cleanup_apply = json.loads(cleanup_apply_cli_result.stdout)
        _expect(
            cli_cleanup_apply.get("applied") is True,
            "Atlas refine prompt-cleanup apply CLI should report applied",
        )
    coverage_report = manifest_module.build_coverage_report(manifest)
    _expect(
        coverage_report.get("schema") == "sxcp_atlas_refine_coverage_report_v1",
        "Atlas refine coverage report lost schema",
    )
    _expect(
        coverage_report.get("entry_count") == 3,
        "Atlas refine coverage report should keep manifest entry count",
    )
    coverage_by_variant = {entry.get("variant_key"): entry for entry in coverage_report.get("entries") or []}
    footjob_coverage = coverage_by_variant.get("pov_footjob_frontal_sole_stroke") or {}
    _expect(
        footjob_coverage.get("state") == "needs_visual_score",
        f"Atlas refine coverage should flag unscored prompt variants: {footjob_coverage}",
    )
    _expect(
        footjob_coverage.get("prompt_variant_count") == 1 and footjob_coverage.get("unscored_variant_count") == 1,
        "Atlas refine coverage should count unscored prompt variants",
    )
    unknown_coverage = coverage_by_variant.get("pov_unknown_pose_candidate") or {}
    _expect(
        unknown_coverage.get("state") == "unknown_variant",
        "Atlas refine coverage should flag unknown variant entries before seed testing",
    )
    handjob_coverage = coverage_by_variant.get("pov_handjob_upright_centered") or {}
    _expect(
        handjob_coverage.get("state") == "baseline_only",
        "Atlas refine coverage should flag known entries with no sidecar variants as baseline-only",
    )
    reference_pool_report = manifest_module.build_reference_pool_report(
        "pov_blowjob_top_down_vertical_shaft",
        supplemental_folders=["1.original/blowjob_top_view_1024"],
    )
    _expect(
        reference_pool_report.get("schema") == "sxcp_atlas_reference_pool_report_v1",
        "Atlas reference pool report lost schema",
    )
    _expect(
        reference_pool_report.get("variant_key") == "pov_blowjob_top_down_vertical_shaft",
        "Atlas reference pool report lost variant key",
    )
    _expect(
        reference_pool_report.get("canonical_image_count") == 17,
        f"Atlas reference pool report should count curated top-view references: {reference_pool_report}",
    )
    _expect(
        reference_pool_report.get("supplemental_image_count") == 27,
        f"Atlas reference pool report should count supplemental raw top-view references: {reference_pool_report}",
    )
    _expect(
        reference_pool_report.get("matched_image_count") == 17,
        f"Atlas reference pool report should match curated refs to raw counterparts by image id: {reference_pool_report}",
    )
    _expect(
        reference_pool_report.get("supplemental_extra_count") == 10,
        f"Atlas reference pool report should surface raw-only cue-expansion images: {reference_pool_report}",
    )
    _expect(
        "1.original/blowjob_top_view_1024/16.png" in reference_pool_report.get("supplemental_extra_images", []),
        f"Atlas reference pool report should include raw-only image ids for cue mining: {reference_pool_report}",
    )
    _expect(
        "blowjob_top_view/22_blowjob_top_view.png" in reference_pool_report.get("catalog_reference_images", []),
        f"Atlas reference pool report should keep curated catalog reference images: {reference_pool_report}",
    )
    reference_pool_cli_result = subprocess.run(
        [
            sys.executable,
            str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
            "--print-reference-pool-report",
            "--variant-key",
            "pov_blowjob_top_down_vertical_shaft",
            "--reference-pool-folder",
            "1.original/blowjob_top_view_1024",
        ],
        cwd=ROOT,
        capture_output=True,
        text=True,
        check=False,
    )
    _expect(
        reference_pool_cli_result.returncode == 0,
        f"Atlas reference pool report CLI failed: {reference_pool_cli_result.stderr}",
    )
    cli_reference_pool_report = json.loads(reference_pool_cli_result.stdout)
    _expect(
        cli_reference_pool_report.get("supplemental_extra_count") == 10,
        "Atlas reference pool report CLI should keep supplemental raw-only count",
    )
    reference_cue_review_sheet = manifest_module.build_reference_cue_review_sheet(
        "pov_blowjob_top_down_vertical_shaft",
        supplemental_folders=["1.original/blowjob_top_view_1024"],
    )
    _expect(
        reference_cue_review_sheet.get("schema") == "sxcp_atlas_reference_cue_review_sheet_v1",
        "Atlas reference cue-review sheet lost schema",
    )
    _expect(
        reference_cue_review_sheet.get("review_item_count") == 27,
        f"Atlas reference cue-review sheet should cover curated refs plus raw-only extras: {reference_cue_review_sheet}",
    )
    review_items_by_image = {
        item.get("canonical_image") or item.get("supplemental_image"): item
        for item in reference_cue_review_sheet.get("review_items") or []
    }
    catalog_reference_item = review_items_by_image.get("blowjob_top_view/22_blowjob_top_view.png") or {}
    _expect(
        catalog_reference_item.get("role") == "catalog_reference",
        f"Atlas reference cue-review sheet should mark curated catalog anchors: {catalog_reference_item}",
    )
    _expect(
        catalog_reference_item.get("supplemental_image") == "1.original/blowjob_top_view_1024/22.png",
        f"Atlas reference cue-review sheet should keep matched raw counterpart provenance: {catalog_reference_item}",
    )
    _expect(
        catalog_reference_item.get("reference_images_template") == ["blowjob_top_view/22_blowjob_top_view.png"],
        f"Atlas reference cue-review sheet should provide canonical sidecar reference template: {catalog_reference_item}",
    )
    _expect(
        catalog_reference_item.get("observed_positive_cues") == [],
        "Atlas reference cue-review sheet must leave positive cue extraction blank for manual review",
    )
    raw_extra_item = review_items_by_image.get("1.original/blowjob_top_view_1024/16.png") or {}
    _expect(
        raw_extra_item.get("role") == "supplemental_extra",
        f"Atlas reference cue-review sheet should mark raw-only extras separately: {raw_extra_item}",
    )
    _expect(
        raw_extra_item.get("reference_images_template") == [],
        f"Atlas reference cue-review sheet should not auto-promote raw-only extras into sidecar reference templates: {raw_extra_item}",
    )
    _expect(
        all(value == "" for value in (raw_extra_item.get("cue_axes") or {}).values()),
        f"Atlas reference cue-review sheet should leave cue axes blank for human labeling: {raw_extra_item}",
    )
    reference_cue_review_cli_result = subprocess.run(
        [
            sys.executable,
            str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
            "--print-reference-cue-review-sheet",
            "--variant-key",
            "pov_blowjob_top_down_vertical_shaft",
            "--reference-pool-folder",
            "1.original/blowjob_top_view_1024",
        ],
        cwd=ROOT,
        capture_output=True,
        text=True,
        check=False,
    )
    _expect(
        reference_cue_review_cli_result.returncode == 0,
        f"Atlas reference cue-review sheet CLI failed: {reference_cue_review_cli_result.stderr}",
    )
    cli_reference_cue_review_sheet = json.loads(reference_cue_review_cli_result.stdout)
    _expect(
        cli_reference_cue_review_sheet.get("review_item_count") == 27,
        "Atlas reference cue-review sheet CLI should keep review item count",
    )
    filled_reference_cue_review_sheet = json.loads(json.dumps(reference_cue_review_sheet))
    filled_review_items_by_image = {
        item.get("canonical_image") or item.get("supplemental_image"): item
        for item in filled_reference_cue_review_sheet.get("review_items") or []
    }
    filled_catalog_item = filled_review_items_by_image["blowjob_top_view/22_blowjob_top_view.png"]
    filled_catalog_item["prompt_variant_template"]["id"] = "atlas22_upper_body_stack"
    filled_catalog_item["observed_positive_cues"] = [
        "face, hair crown, shoulders, upper chest, and one hand form the primary visible partner stack"
    ]
    filled_catalog_item["cue_axes"]["camera_height"] = "near_vertical_overhead"
    filled_catalog_item["cue_axes"]["workspace_surface"] = "flat_floor_plane"
    filled_catalog_item["cue_axes"]["hand_position"] = "one_hand_base_contact"
    filled_catalog_item["review_notes"] = "canonical row reviewed from atlas image 22"
    filled_exact_text_item = filled_review_items_by_image["blowjob_top_view/27_blowjob_top_view.png"]
    filled_exact_text_item["prompt_variant_template"]["id"] = "atlas27_shaft_first_exact_text"
    filled_exact_text_item["prompt_variant_template"]["text"] = (
        "A controlled same-subject prompt. Straight-down male POV oral close-up. "
        "the centered shaft and mouth contact form the main vertical axis from the lower foreground to the woman's face. "
        "the woman's face, hair crown, shoulders, upper chest, and one hand stack around the shaft-contact axis. "
        "a flat floor plane fills the background as shallow overhead room evidence."
    )
    filled_exact_text_item["observed_positive_cues"] = [
        "the centered shaft and mouth contact form the main vertical axis from the lower foreground to the woman's face"
    ]
    filled_exact_text_item["cue_axes"]["camera_height"] = "straight_down_overhead_shaft_first"
    filled_exact_text_item["cue_axes"]["contact_depth"] = "shaft_contact_axis_centered_from_lower_foreground_to_mouth"
    filled_exact_text_item["review_notes"] = "canonical row reviewed as exact-text shaft-first ordering"
    filled_noisy_item = filled_review_items_by_image["blowjob_top_view/106_blowjob_top_view.png"]
    filled_noisy_item["prompt_variant_template"]["id"] = "noisy_option_axis"
    filled_noisy_item["observed_positive_cues"] = [
        "either a high top-down view or a flat top-down view"
    ]
    filled_raw_extra_item = filled_review_items_by_image["1.original/blowjob_top_view_1024/16.png"]
    filled_raw_extra_item["prompt_variant_template"]["id"] = "raw16_floor_plane_axis"
    filled_raw_extra_item["observed_positive_cues"] = [
        "carpet tile seams fill the support plane"
    ]
    filled_raw_extra_item["cue_axes"]["workspace_surface"] = "carpet_tile_floor_plane"
    reference_cue_candidate_draft = manifest_module.build_reference_cue_candidate_draft(
        filled_reference_cue_review_sheet
    )
    _expect(
        reference_cue_candidate_draft.get("schema") == "sxcp_atlas_reference_cue_candidate_draft_v1",
        "Atlas reference cue candidate draft lost schema",
    )
    _expect(
        reference_cue_candidate_draft.get("ready_candidate_count") == 2,
        f"Atlas reference cue candidate draft should only promote canonical filled cues: {reference_cue_candidate_draft}",
    )
    candidates_by_id = {
        candidate.get("prompt_variant_id"): candidate
        for candidate in reference_cue_candidate_draft.get("candidates") or []
    }
    candidate = candidates_by_id.get("atlas22_upper_body_stack") or {}
    _expect(
        candidate.get("reference_images") == ["blowjob_top_view/22_blowjob_top_view.png"],
        f"Atlas reference cue candidate draft should keep canonical reference image provenance: {candidate}",
    )
    _expect(
        candidate.get("prompt_variant", {}).get("append_cues") == [
            "face, hair crown, shoulders, upper chest, and one hand form the primary visible partner stack"
        ],
        f"Atlas reference cue candidate draft should use reviewed positive cues as append cues: {candidate}",
    )
    _expect(
        candidate.get("prompt_variant", {}).get("cue_axes", {}).get("camera_height") == "near_vertical_overhead",
        f"Atlas reference cue candidate draft should carry reviewed cue axes: {candidate}",
    )
    exact_text_candidate = candidates_by_id.get("atlas27_shaft_first_exact_text") or {}
    _expect(
        "text" in (exact_text_candidate.get("prompt_variant") or {})
        and "append_cues" not in (exact_text_candidate.get("prompt_variant") or {}),
        f"Atlas reference cue candidate draft should preserve reviewed exact text instead of append cues: {exact_text_candidate}",
    )
    _expect(
        (exact_text_candidate.get("prompt_variant") or {}).get("text", "").startswith(
            "A controlled same-subject prompt. Straight-down male POV oral close-up."
        ),
        f"Atlas reference cue candidate draft should keep exact-text wording order: {exact_text_candidate}",
    )
    skipped_reasons = {
        (item.get("id"), item.get("reason"))
        for item in reference_cue_candidate_draft.get("skipped") or []
    }
    _expect(
        ("106", "prompt_noise_issue") in skipped_reasons,
        f"Atlas reference cue candidate draft should skip noisy reviewed cues: {reference_cue_candidate_draft}",
    )
    _expect(
        ("16", "supplemental_extra_needs_canonical_reference") in skipped_reasons,
        f"Atlas reference cue candidate draft should not auto-promote raw-only extras: {reference_cue_candidate_draft}",
    )
    with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as cue_candidate_sheet_handle:
        json.dump(filled_reference_cue_review_sheet, cue_candidate_sheet_handle)
        cue_candidate_sheet_path = Path(cue_candidate_sheet_handle.name)
    try:
        reference_cue_candidate_cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--print-reference-cue-candidate-draft",
                "--reference-cue-review-sheet-json",
                str(cue_candidate_sheet_path),
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
    finally:
        cue_candidate_sheet_path.unlink(missing_ok=True)
    _expect(
        reference_cue_candidate_cli_result.returncode == 0,
        f"Atlas reference cue candidate draft CLI failed: {reference_cue_candidate_cli_result.stderr}",
    )
    cli_reference_cue_candidate_draft = json.loads(reference_cue_candidate_cli_result.stdout)
    _expect(
        cli_reference_cue_candidate_draft.get("ready_candidate_count") == 2,
        "Atlas reference cue candidate draft CLI should keep ready candidate count",
    )
    with tempfile.TemporaryDirectory() as reference_author_tmpdir:
        reference_author_root = Path(reference_author_tmpdir)
        reference_author_prompt = reference_author_root / "pov_blowjob_top_down_vertical_shaft_00001_.txt"
        reference_author_image = reference_author_root / "pov_blowjob_top_down_vertical_shaft_00001_.png"
        reference_author_prompt.write_text(
            "A controlled same-subject top-view oral baseline prompt awaiting atlas cue candidates.",
            encoding="utf-8",
        )
        reference_author_image.write_bytes(b"fake-png")
        reference_author_manifest = manifest_module.build_manifest(
            reference_author_root,
            subject_id="same_woman_001",
        )
        reference_author_draft = manifest_module.build_reference_cue_sidecar_author_draft(
            reference_author_manifest,
            reference_cue_candidate_draft,
            variant_key="pov_blowjob_top_down_vertical_shaft",
        )
        _expect(
            reference_author_draft.get("schema") == "sxcp_atlas_reference_cue_sidecar_author_draft_v1",
            "Atlas reference sidecar author draft lost schema",
        )
        _expect(
            reference_author_draft.get("update_count") == 1,
            f"Atlas reference sidecar author draft should target the same-stem baseline entry: {reference_author_draft}",
        )
        reference_author_update = (reference_author_draft.get("updates") or [{}])[0]
        _expect(
            reference_author_update.get("sidecar_filename") == "pov_blowjob_top_down_vertical_shaft_00001_.json",
            f"Atlas reference sidecar author draft should use same-stem sidecar filenames: {reference_author_update}",
        )
        _expect(
            [variant.get("id") for variant in reference_author_update.get("prompt_variants") or []]
            == ["atlas22_upper_body_stack", "atlas27_shaft_first_exact_text"],
            f"Atlas reference sidecar author draft should carry reviewed prompt variants: {reference_author_update}",
        )
        reference_author_variants_by_id = {
            variant.get("id"): variant
            for variant in reference_author_update.get("prompt_variants") or []
        }
        _expect(
            (reference_author_variants_by_id.get("atlas27_shaft_first_exact_text") or {})
            .get("prompt_source", {})
            .get("kind")
            == "text",
            f"Atlas reference sidecar author draft should mark exact-text provenance: {reference_author_update}",
        )
        reference_author_validation = manifest_module.validate_reference_cue_sidecar_author_draft(reference_author_draft)
        _expect(
            reference_author_validation.get("valid") is True,
            f"Atlas reference sidecar author draft should validate before apply: {reference_author_validation}",
        )
        invalid_reference_author_draft = json.loads(json.dumps(reference_author_draft))
        invalid_reference_author_draft["updates"][0]["prompt_variants"][0]["append_cues"][0] = (
            "either the reviewed cue or another cue"
        )
        invalid_reference_author_validation = manifest_module.validate_reference_cue_sidecar_author_draft(
            invalid_reference_author_draft
        )
        _expect(
            invalid_reference_author_validation.get("valid") is False
            and any("prompt_noise" in error for error in invalid_reference_author_validation.get("errors", [])),
            f"Atlas reference sidecar author draft validation should reject noisy append cues: {invalid_reference_author_validation}",
        )
        reference_author_apply_report = manifest_module.apply_reference_cue_sidecar_author_draft(
            reference_author_draft,
            reference_author_root,
        )
        _expect(
            reference_author_apply_report.get("schema") == "sxcp_atlas_reference_cue_sidecar_author_apply_report_v1",
            "Atlas reference sidecar author apply lost schema",
        )
        _expect(
            reference_author_apply_report.get("applied") is True
            and reference_author_apply_report.get("updated_file_count") == 1,
            f"Atlas reference sidecar author apply should write one sidecar: {reference_author_apply_report}",
        )
        authored_sidecar = json.loads(
            (reference_author_root / "pov_blowjob_top_down_vertical_shaft_00001_.json").read_text(encoding="utf-8")
        )
        _expect(
            authored_sidecar.get("prompt_variants", [{}])[0].get("reference_images") == [
                "blowjob_top_view/22_blowjob_top_view.png"
            ],
            f"Atlas reference sidecar author apply should preserve reference provenance: {authored_sidecar}",
        )
        rescanned_reference_author_manifest = manifest_module.build_manifest(
            reference_author_root,
            subject_id="same_woman_001",
        )
        reference_author_batch = manifest_module.build_prompt_batch(
            rescanned_reference_author_manifest,
            "pov_blowjob_top_down_vertical_shaft",
            sampler_seed=101,
        )
        reference_author_batch_probes = reference_author_batch.get("probes") or []
        _expect(
            len(reference_author_batch_probes) == 3
            and reference_author_batch_probes[1].get("reference_images") == [
                "blowjob_top_view/22_blowjob_top_view.png"
            ],
            f"Atlas reference sidecar author apply should rescan into testable prompt batches: {reference_author_batch}",
        )
        exact_text_probe = reference_author_batch_probes[2]
        _expect(
            exact_text_probe.get("text", "").startswith(
                "A controlled same-subject prompt. Straight-down male POV oral close-up."
            )
            and "awaiting atlas cue candidates" not in exact_text_probe.get("text", ""),
            f"Atlas exact-text sidecar variants should replace the baseline prompt in prompt batches: {exact_text_probe}",
        )
        with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as candidate_draft_handle:
            json.dump(reference_cue_candidate_draft, candidate_draft_handle)
            candidate_draft_path = Path(candidate_draft_handle.name)
        with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as author_draft_handle:
            json.dump(reference_author_draft, author_draft_handle)
            author_draft_path = Path(author_draft_handle.name)
        try:
            reference_author_cli_result = subprocess.run(
                [
                    sys.executable,
                    str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                    "--folder",
                    str(reference_author_root),
                    "--subject-id",
                    "same_woman_001",
                    "--print-reference-cue-sidecar-author-draft",
                    "--reference-cue-candidate-draft-json",
                    str(candidate_draft_path),
                    "--variant-key",
                    "pov_blowjob_top_down_vertical_shaft",
                ],
                cwd=ROOT,
                capture_output=True,
                text=True,
                check=False,
            )
            reference_author_validate_cli_result = subprocess.run(
                [
                    sys.executable,
                    str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                    "--validate-reference-cue-sidecar-author-draft",
                    "--reference-cue-sidecar-author-draft-json",
                    str(author_draft_path),
                ],
                cwd=ROOT,
                capture_output=True,
                text=True,
                check=False,
            )
            reference_author_apply_cli_result = subprocess.run(
                [
                    sys.executable,
                    str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                    "--apply-reference-cue-sidecar-author-draft",
                    "--reference-cue-sidecar-author-draft-json",
                    str(author_draft_path),
                    "--folder",
                    str(reference_author_root),
                ],
                cwd=ROOT,
                capture_output=True,
                text=True,
                check=False,
            )
        finally:
            candidate_draft_path.unlink(missing_ok=True)
            author_draft_path.unlink(missing_ok=True)
        _expect(
            reference_author_cli_result.returncode == 0,
            f"Atlas reference sidecar author draft CLI failed: {reference_author_cli_result.stderr}",
        )
        cli_reference_author_draft = json.loads(reference_author_cli_result.stdout)
        _expect(
            cli_reference_author_draft.get("update_count") == 1,
            "Atlas reference sidecar author draft CLI should keep update count",
        )
        _expect(
            reference_author_validate_cli_result.returncode == 0,
            f"Atlas reference sidecar author validation CLI failed: {reference_author_validate_cli_result.stderr}",
        )
        _expect(
            reference_author_apply_cli_result.returncode == 0,
            f"Atlas reference sidecar author apply CLI failed: {reference_author_apply_cli_result.stderr}",
        )
        cli_reference_author_apply_report = json.loads(reference_author_apply_cli_result.stdout)
        _expect(
            cli_reference_author_apply_report.get("applied") is True,
            "Atlas reference sidecar author apply CLI should report applied",
        )
    sidecar_scaffold = manifest_module.build_sidecar_scaffold(manifest)
    _expect(
        sidecar_scaffold.get("schema") == "sxcp_atlas_refine_sidecar_scaffold_v1",
        "Atlas refine sidecar scaffold lost schema",
    )
    _expect(
        sidecar_scaffold.get("scaffold_count") == 1,
        f"Atlas refine sidecar scaffold should include only known baseline-only entries: {sidecar_scaffold}",
    )
    scaffold_entry = (sidecar_scaffold.get("scaffolds") or [{}])[0]
    _expect(
        scaffold_entry.get("variant_key") == "pov_handjob_upright_centered",
        "Atlas refine sidecar scaffold should target the baseline-only known variant",
    )
    _expect(
        scaffold_entry.get("sidecar_filename") == "pov_handjob_upright_centered_00001_.json",
        "Atlas refine sidecar scaffold should use the same-stem sidecar filename",
    )
    _expect(
        scaffold_entry.get("sidecar_json", {}).get("prompt_variants") == [],
        "Atlas refine sidecar scaffold should not invent prompt variants",
    )
    _expect(
        scaffold_entry.get("prompt_variant_template", {}).get("append_cues") == [],
        "Atlas refine sidecar scaffold should leave append cues blank for user-authored variants",
    )
    _expect(
        scaffold_entry.get("prompt_variant_template", {}).get("reference_images") == [],
        "Atlas refine sidecar scaffold should include a blank nearest-reference image list",
    )
    _expect(scaffold_cli_returncode == 0, f"Atlas refine sidecar scaffold CLI failed: {scaffold_cli_stderr}")
    cli_scaffold = json.loads(scaffold_cli_stdout)
    _expect(
        cli_scaffold.get("scaffold_count") == 1,
        "Atlas refine sidecar scaffold CLI should keep scaffold count",
    )
    batch = manifest_module.build_prompt_batch(manifest, "pov_footjob_frontal_sole_stroke")
    _expect(batch.get("schema") == "sxcp_atlas_refine_prompt_batch_v1", "Atlas refine batch lost schema")
    _expect(batch.get("seed") == 101, "Atlas refine batch should use the entry sampler seed")
    _expect(batch.get("variant_key") == "pov_footjob_frontal_sole_stroke", "Atlas refine batch lost variant key")
    batch_probes = batch.get("probes") or []
    _expect([probe.get("id") for probe in batch_probes] == [
        "pov_footjob_frontal_sole_stroke_00001__baseline",
        "pov_footjob_frontal_sole_stroke_00001__soles_more_forward",
    ], f"Atlas refine batch should include baseline then sidecar variant probes: {batch_probes}")
    _expect(
        batch_probes[0].get("text") == footjob.get("prompt_text"),
        "Atlas refine batch baseline should preserve the exact source prompt",
    )
    _expect(
        "the woman's soles press farther forward along the same contact line" in batch_probes[1].get("text", ""),
        "Atlas refine batch should append explicit sidecar cue text",
    )
    _expect(
        batch_probes[1].get("prompt_source", {}).get("kind") == "append_cues",
        "Atlas refine batch should preserve that the candidate came from append_cues",
    )
    _expect(
        batch_probes[1].get("prompt_source", {}).get("append_cues") == [
            "the woman's soles press farther forward along the same contact line"
        ],
        "Atlas refine batch should preserve append-cue deltas for later catalog review",
    )
    _expect(
        batch_probes[1].get("cue_axes", {}).get("contact_depth") == "contact_line_farther_forward",
        "Atlas refine batch should preserve variant cue-axis metadata",
    )
    _expect(
        batch_probes[1].get("reference_images") == ["blowjob_top_view/22_blowjob_top_view.png"],
        "Atlas refine batch should preserve prompt-variant reference-image provenance",
    )
    _expect(
        batch_probes[1].get("seed_metadata", {}).get("micro_position_seed") == 303,
        "Atlas refine batch should preserve variant micro-position seed metadata",
    )
    override_batch = manifest_module.build_prompt_batch(
        manifest,
        "pov_footjob_frontal_sole_stroke",
        sampler_seed=909,
    )
    override_batch_probes = override_batch.get("probes") or []
    _expect(
        override_batch.get("seed") == 909,
        "Atlas refine batch should use explicit sampler seed overrides",
    )
    _expect(
        override_batch_probes
        and all(probe.get("seed_metadata", {}).get("sampler_seed") == 909 for probe in override_batch_probes),
        f"Atlas refine batch probe metadata should reflect the actual sampler seed override: {override_batch_probes}",
    )
    with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as handle:
        json.dump(batch, handle)
        batch_path = Path(handle.name)
    try:
        loaded_batch = sxcp_prompt_batch.load_batch(batch_path)
    finally:
        batch_path.unlink(missing_ok=True)
    _expect(loaded_batch.get("seed") == 101, "Atlas refine batch should load through sxcp prompt batch helper")
    _expect(len(loaded_batch.get("probes") or []) == 2, "Atlas refine batch should keep both probes through batch loader")
    _expect(
        (loaded_batch.get("probes") or [])[1].get("reference_images") == ["blowjob_top_view/22_blowjob_top_view.png"],
        "SxCP prompt batch loader should preserve reference-image provenance metadata",
    )
    results = {
        "seed": 101,
        "channel_in": "sxcp_eval_in",
        "probes": [
            {
                "id": "pov_footjob_frontal_sole_stroke_00001__baseline",
                "prompt_order": "subject_first",
                "turn": 11,
                "image_path": "/tmp/pov_footjob_baseline.png",
                "returned_seed": 101,
            },
            {
                "id": "pov_footjob_frontal_sole_stroke_00001__soles_more_forward",
                "prompt_order": "subject_first",
                "turn": 12,
                "image_path": "/tmp/pov_footjob_soles_more_forward.png",
                "returned_seed": 101,
            },
        ],
    }
    result_sheet = manifest_module.build_result_sheet(batch, results, notes="visual scoring pending")
    _expect(result_sheet.get("schema") == "sxcp_atlas_refine_result_sheet_v1", "Atlas refine result sheet lost schema")
    _expect(result_sheet.get("seed") == 101, "Atlas refine result sheet should keep the fixed sampler seed")
    _expect(
        result_sheet.get("variant_key") == "pov_footjob_frontal_sole_stroke",
        "Atlas refine result sheet lost variant key",
    )
    sheet_probes = result_sheet.get("probes") or []
    _expect([probe.get("id") for probe in sheet_probes] == [
        "pov_footjob_frontal_sole_stroke_00001__baseline",
        "pov_footjob_frontal_sole_stroke_00001__soles_more_forward",
    ], "Atlas refine result sheet should preserve batch/result probe order")
    _expect(
        sheet_probes[0].get("image_path") == "/tmp/pov_footjob_baseline.png",
        "Atlas refine result sheet should keep baseline image path",
    )
    _expect(
        sheet_probes[1].get("turn") == 12,
        "Atlas refine result sheet should keep returned turn numbers",
    )
    _expect(
        sheet_probes[1].get("text") == batch_probes[1].get("text"),
        "Atlas refine result sheet should keep the exact candidate prompt text",
    )
    _expect(
        sheet_probes[1].get("cue_axes", {}).get("contact_depth") == "contact_line_farther_forward",
        "Atlas refine result sheet should keep candidate cue axes",
    )
    _expect(
        sheet_probes[1].get("prompt_source", {}).get("kind") == "append_cues",
        "Atlas refine result sheet should keep append-cue provenance",
    )
    _expect(
        sheet_probes[1].get("reference_images") == ["blowjob_top_view/22_blowjob_top_view.png"],
        "Atlas refine result sheet should preserve nearest atlas reference-image provenance",
    )
    _expect(
        sheet_probes[1].get("score", {}).get("subject_identity") is None,
        "Atlas refine result sheet should leave subject-identity score unfilled for visual analysis",
    )
    for score_key in (
        "atlas_pose_match",
        "contact_match",
        "pose_ownership",
        "workspace_continuity",
        "clothing_visibility",
        "subject_identity",
        "expression_eye_control",
        "anatomy_proportion",
        "prompt_noise",
    ):
        _expect(score_key in sheet_probes[1].get("score", {}), f"Atlas refine result sheet missing score slot {score_key}")
    with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as batch_handle:
        json.dump(batch, batch_handle)
        cli_batch_path = Path(batch_handle.name)
    with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as result_handle:
        json.dump(results, result_handle)
        cli_result_path = Path(result_handle.name)
    try:
        sheet_cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--print-result-sheet",
                "--batch-json",
                str(cli_batch_path),
                "--result-json",
                str(cli_result_path),
                "--notes",
                "visual scoring pending",
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
    finally:
        cli_batch_path.unlink(missing_ok=True)
        cli_result_path.unlink(missing_ok=True)
    _expect(sheet_cli_result.returncode == 0, f"Atlas refine result-sheet CLI failed: {sheet_cli_result.stderr}")
    cli_sheet = json.loads(sheet_cli_result.stdout)
    _expect(
        cli_sheet.get("schema") == "sxcp_atlas_refine_result_sheet_v1",
        "Atlas refine result-sheet CLI lost schema",
    )
    _expect(
        len(cli_sheet.get("probes") or []) == 2,
        "Atlas refine result-sheet CLI should keep both probes",
    )
    scored_sheet = json.loads(json.dumps(result_sheet))
    scored_sheet["probes"][0]["score"].update(
        {
            "atlas_pose_match": "baseline",
            "contact_match": "baseline",
            "pose_ownership": "pass",
            "workspace_continuity": "pass",
            "clothing_visibility": "pass",
            "subject_identity": "pass",
            "expression_eye_control": "pass",
            "anatomy_proportion": "pass",
            "prompt_noise": "pass",
        }
    )
    scored_sheet["probes"][1]["score"].update(
        {
            "atlas_pose_match": "partial",
            "contact_match": "pass",
            "pose_ownership": "pass",
            "workspace_continuity": "pass",
            "clothing_visibility": "pass",
            "subject_identity": "pass",
            "expression_eye_control": "pass",
            "anatomy_proportion": "pass",
            "prompt_noise": "pass",
        }
    )
    scored_sheet["probes"][1]["analysis_notes"] = "Candidate keeps the same subject and moves the foot contact axis."
    promotion_report = manifest_module.build_promotion_report(scored_sheet)
    _expect(
        promotion_report.get("schema") == "sxcp_atlas_refine_promotion_report_v1",
        "Atlas refine promotion report lost schema",
    )
    _expect(promotion_report.get("seed") == 101, "Atlas refine promotion report should keep the fixed sampler seed")
    _expect(promotion_report.get("promotion_ready_count") == 1, "Atlas refine promotion report should count ready candidates")
    _expect(
        promotion_report.get("blocked_count") == 0,
        "Atlas refine promotion report should not block a fully scored passing candidate",
    )
    candidates = promotion_report.get("candidates") or []
    _expect(len(candidates) == 1, f"Atlas refine promotion report should include one non-baseline candidate: {candidates}")
    _expect(candidates[0].get("decision") == "seedable_candidate", "Atlas refine promotion report should mark passing candidate seedable")
    _expect(
        candidates[0].get("prompt_variant_id") == "soles_more_forward",
        "Atlas refine promotion report should recover sidecar prompt variant id",
    )
    _expect(
        candidates[0].get("cue_axes", {}).get("contact_depth") == "contact_line_farther_forward",
        "Atlas refine promotion report should keep candidate cue axes",
    )
    _expect(
        candidates[0].get("reference_images") == ["blowjob_top_view/22_blowjob_top_view.png"],
        "Atlas refine promotion report should keep candidate reference-image provenance",
    )
    _expect(
        candidates[0].get("prompt_source", {}).get("append_cues") == [
            "the woman's soles press farther forward along the same contact line"
        ],
        "Atlas refine promotion report should keep append-cue provenance for catalog drafts",
    )
    noisy_scored_sheet = json.loads(json.dumps(scored_sheet))
    noisy_scored_sheet["probes"][1]["text"] += " either the foot or hand moves while the contact stays."
    noisy_promotion_report = manifest_module.build_promotion_report(noisy_scored_sheet)
    noisy_candidate = (noisy_promotion_report.get("candidates") or [{}])[0]
    _expect(
        noisy_candidate.get("decision") == "rejected"
        and "prompt_noise_issue" in noisy_candidate.get("blockers", [])
        and any(issue.get("code") == "option_word" for issue in noisy_candidate.get("prompt_noise_issues", [])),
        f"Atlas refine promotion report should reject noisy candidate text even when manual prompt_noise score passes: {noisy_candidate}",
    )
    noisy_sidecar_draft = manifest_module.build_sidecar_update_draft(noisy_promotion_report)
    _expect(
        noisy_sidecar_draft.get("ready_candidate_count") == 0,
        f"Atlas refine sidecar draft should skip noisy promoted candidates: {noisy_sidecar_draft}",
    )
    scored_sheet["probes"][1]["score"]["subject_identity"] = "fail"
    rejected_report = manifest_module.build_promotion_report(scored_sheet)
    _expect(
        rejected_report.get("promotion_ready_count") == 0,
        "Atlas refine promotion report should block candidates that lose subject identity",
    )
    _expect(
        rejected_report.get("candidates", [{}])[0].get("decision") == "rejected",
        "Atlas refine promotion report should reject failed preservation gates",
    )
    scored_sheet["probes"][1]["score"]["subject_identity"] = "pass"
    with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as sheet_handle:
        json.dump(scored_sheet, sheet_handle)
        cli_sheet_path = Path(sheet_handle.name)
    try:
        promotion_cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--print-promotion-report",
                "--result-sheet-json",
                str(cli_sheet_path),
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
    finally:
        cli_sheet_path.unlink(missing_ok=True)
    _expect(promotion_cli_result.returncode == 0, f"Atlas refine promotion-report CLI failed: {promotion_cli_result.stderr}")
    cli_promotion_report = json.loads(promotion_cli_result.stdout)
    _expect(
        cli_promotion_report.get("promotion_ready_count") == 1,
        "Atlas refine promotion-report CLI should keep ready candidate count",
    )
    sidecar_draft = manifest_module.build_sidecar_update_draft(promotion_report)
    _expect(
        sidecar_draft.get("schema") == "sxcp_atlas_refine_sidecar_update_draft_v1",
        "Atlas refine sidecar update draft lost schema",
    )
    _expect(
        sidecar_draft.get("ready_candidate_count") == 1,
        "Atlas refine sidecar update draft should count ready candidates",
    )
    updates = sidecar_draft.get("updates") or []
    _expect(len(updates) == 1, f"Atlas refine sidecar update draft should include one sidecar update: {updates}")
    _expect(
        updates[0].get("sidecar_filename") == "pov_footjob_frontal_sole_stroke_00001_.json",
        "Atlas refine sidecar update draft should preserve the original same-stem sidecar filename",
    )
    drafted_variants = updates[0].get("prompt_variants") or []
    _expect(len(drafted_variants) == 1, "Atlas refine sidecar update draft should include one prompt variant")
    _expect(
        drafted_variants[0].get("id") == "soles_more_forward",
        "Atlas refine sidecar update draft should preserve prompt variant id",
    )
    _expect(
        drafted_variants[0].get("text") == sheet_probes[1].get("text"),
        "Atlas refine sidecar update draft should use the exact tested prompt text",
    )
    _expect(
        drafted_variants[0].get("prompt_source", {}).get("kind") == "append_cues",
        "Atlas refine sidecar update draft should keep source prompt-variant kind",
    )
    _expect(
        drafted_variants[0].get("prompt_source", {}).get("append_cues") == [
            "the woman's soles press farther forward along the same contact line"
        ],
        "Atlas refine sidecar update draft should keep tested append-cue deltas",
    )
    _expect(
        drafted_variants[0].get("cue_axes", {}).get("contact_depth") == "contact_line_farther_forward",
        "Atlas refine sidecar update draft should keep cue-axis metadata",
    )
    _expect(
        drafted_variants[0].get("reference_images") == ["blowjob_top_view/22_blowjob_top_view.png"],
        "Atlas refine sidecar update draft should keep nearest atlas reference-image provenance",
    )
    _expect(
        drafted_variants[0].get("evidence", {}).get("image_path") == "/tmp/pov_footjob_soles_more_forward.png",
        "Atlas refine sidecar update draft should keep candidate evidence image path",
    )
    _expect(
        drafted_variants[0].get("evidence", {}).get("score", {}).get("subject_identity") == "pass",
        "Atlas refine sidecar update draft should keep visual score evidence",
    )
    rejected_draft = manifest_module.build_sidecar_update_draft(rejected_report)
    _expect(
        rejected_draft.get("ready_candidate_count") == 0,
        "Atlas refine sidecar update draft should not include rejected candidates",
    )
    with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as report_handle:
        json.dump(promotion_report, report_handle)
        cli_report_path = Path(report_handle.name)
    try:
        sidecar_cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--print-sidecar-update-draft",
                "--promotion-report-json",
                str(cli_report_path),
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
    finally:
        cli_report_path.unlink(missing_ok=True)
    _expect(sidecar_cli_result.returncode == 0, f"Atlas refine sidecar-update CLI failed: {sidecar_cli_result.stderr}")
    cli_sidecar_draft = json.loads(sidecar_cli_result.stdout)
    _expect(
        cli_sidecar_draft.get("ready_candidate_count") == 1,
        "Atlas refine sidecar-update CLI should keep ready candidate count",
    )
    draft_validation = manifest_module.validate_sidecar_update_draft(sidecar_draft)
    _expect(
        draft_validation.get("schema") == "sxcp_atlas_refine_sidecar_update_validation_v1",
        "Atlas refine sidecar update validation lost schema",
    )
    _expect(draft_validation.get("valid") is True, f"Atlas refine sidecar update draft should validate: {draft_validation}")
    _expect(draft_validation.get("error_count") == 0, "Atlas refine sidecar update validation should have no errors")
    _expect(draft_validation.get("validated_variant_count") == 1, "Atlas refine sidecar update validation should count variants")
    invalid_draft = json.loads(json.dumps(sidecar_draft))
    invalid_draft["updates"][0]["prompt_variants"][0]["evidence"]["score"]["subject_identity"] = "fail"
    invalid_draft["updates"][0]["prompt_variants"][0]["negative_prompt"] = "do not include this"
    invalid_draft["updates"][0]["prompt_variants"][0]["prompt_source"]["prompt_variant_id"] = "wrong_axis"
    invalid_validation = manifest_module.validate_sidecar_update_draft(invalid_draft)
    _expect(invalid_validation.get("valid") is False, "Atlas refine sidecar update validation should reject failed evidence")
    _expect(
        any("subject_identity=fail" in error for error in invalid_validation.get("errors", [])),
        f"Atlas refine sidecar update validation should report failed subject identity: {invalid_validation}",
    )
    _expect(
        any("negative_prompt" in error for error in invalid_validation.get("errors", [])),
        f"Atlas refine sidecar update validation should reject negative prompt fields: {invalid_validation}",
    )
    _expect(
        any("prompt_source.prompt_variant_id" in error for error in invalid_validation.get("errors", [])),
        f"Atlas refine sidecar update validation should reject mismatched prompt-source ids: {invalid_validation}",
    )
    with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as draft_handle:
        json.dump(sidecar_draft, draft_handle)
        cli_draft_path = Path(draft_handle.name)
    try:
        validation_cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--validate-sidecar-update-draft",
                "--sidecar-update-draft-json",
                str(cli_draft_path),
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
    finally:
        cli_draft_path.unlink(missing_ok=True)
    _expect(validation_cli_result.returncode == 0, f"Atlas refine sidecar-update validation CLI failed: {validation_cli_result.stderr}")
    cli_draft_validation = json.loads(validation_cli_result.stdout)
    _expect(
        cli_draft_validation.get("valid") is True,
        "Atlas refine sidecar-update validation CLI should validate the draft",
    )
    with tempfile.TemporaryDirectory() as duplicate_apply_tmpdir:
        duplicate_apply_root = Path(duplicate_apply_tmpdir)
        duplicate_apply_sidecar = duplicate_apply_root / "pov_footjob_frontal_sole_stroke_00001_.json"
        duplicate_apply_sidecar.write_text(
            json.dumps(
                {
                    "notes": "ambiguous existing sidecar should not be silently rewritten",
                    "prompt_variants": [
                        {
                            "id": "old_axis",
                            "text": "First old reviewed prompt variant.",
                        },
                        {
                            "id": "old_axis",
                            "text": "Second old reviewed prompt variant.",
                        },
                    ],
                },
                ensure_ascii=True,
            ),
            encoding="utf-8",
        )
        try:
            manifest_module.apply_sidecar_update_draft(sidecar_draft, duplicate_apply_root)
        except ValueError as exc:
            _expect(
                "old_axis" in str(exc) and "duplicated" in str(exc),
                f"Atlas refine sidecar apply duplicate existing id error should identify the duplicated id: {exc}",
            )
        else:
            raise AssertionError("Atlas refine sidecar apply should reject existing duplicate prompt_variant ids")
    with tempfile.TemporaryDirectory() as apply_tmpdir:
        apply_root = Path(apply_tmpdir)
        sidecar_path = apply_root / "pov_footjob_frontal_sole_stroke_00001_.json"
        sidecar_path.write_text(
            json.dumps(
                {
                    "notes": "preserve existing sidecar notes",
                    "seed_metadata": {"sampler_seed": 101},
                    "prompt_variants": [
                        {
                            "id": "old_axis",
                            "text": "Old reviewed prompt variant.",
                            "cue_axes": {"foot_position": "old"},
                        }
                    ],
                },
                ensure_ascii=True,
            ),
            encoding="utf-8",
        )
        apply_report = manifest_module.apply_sidecar_update_draft(sidecar_draft, apply_root)
        _expect(
            apply_report.get("schema") == "sxcp_atlas_refine_sidecar_apply_report_v1",
            "Atlas refine sidecar apply report lost schema",
        )
        _expect(apply_report.get("applied") is True, "Atlas refine sidecar apply should mark applied")
        _expect(apply_report.get("updated_file_count") == 1, "Atlas refine sidecar apply should update one sidecar")
        applied_sidecar = json.loads(sidecar_path.read_text(encoding="utf-8"))
        _expect(
            applied_sidecar.get("notes") == "preserve existing sidecar notes",
            "Atlas refine sidecar apply should preserve unrelated sidecar metadata",
        )
        applied_variants = applied_sidecar.get("prompt_variants") or []
        _expect(
            [variant.get("id") for variant in applied_variants] == ["old_axis", "soles_more_forward"],
            f"Atlas refine sidecar apply should append the ready variant without dropping existing variants: {applied_variants}",
        )
        _expect(
            applied_variants[1].get("text") == sheet_probes[1].get("text"),
            "Atlas refine sidecar apply should write exact tested prompt text",
        )
        _expect(
            applied_variants[1].get("evidence", {}).get("score", {}).get("subject_identity") == "pass",
            "Atlas refine sidecar apply should preserve evidence scores",
        )
        _expect(
            applied_variants[1].get("prompt_source", {}).get("append_cues") == [
                "the woman's soles press farther forward along the same contact line"
            ],
            "Atlas refine sidecar apply should preserve append-cue provenance",
        )
        second_apply_report = manifest_module.apply_sidecar_update_draft(sidecar_draft, apply_root)
        _expect(
            second_apply_report.get("updated_file_count") == 1,
            "Atlas refine sidecar apply should be idempotent and still report the touched sidecar",
        )
        applied_again = json.loads(sidecar_path.read_text(encoding="utf-8"))
        _expect(
            [variant.get("id") for variant in applied_again.get("prompt_variants", [])].count("soles_more_forward") == 1,
            "Atlas refine sidecar apply should upsert prompt variants by id instead of duplicating them",
        )
        with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as apply_draft_handle:
            json.dump(sidecar_draft, apply_draft_handle)
            cli_apply_draft_path = Path(apply_draft_handle.name)
        try:
            apply_cli_result = subprocess.run(
                [
                    sys.executable,
                    str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                    "--apply-sidecar-update-draft",
                    "--sidecar-update-draft-json",
                    str(cli_apply_draft_path),
                    "--folder",
                    str(apply_root),
                ],
                cwd=ROOT,
                capture_output=True,
                text=True,
                check=False,
            )
        finally:
            cli_apply_draft_path.unlink(missing_ok=True)
        _expect(apply_cli_result.returncode == 0, f"Atlas refine sidecar apply CLI failed: {apply_cli_result.stderr}")
        cli_apply_report = json.loads(apply_cli_result.stdout)
        _expect(
            cli_apply_report.get("applied") is True,
            "Atlas refine sidecar apply CLI should report applied",
        )
        # Add the paired prompt/image artifacts after apply so the manifest scanner
        # can roundtrip the sidecar update through the normal atlas-refine path.
        (apply_root / "pov_footjob_frontal_sole_stroke_00001_.txt").write_text(
            footjob.get("prompt_text", ""),
            encoding="utf-8",
        )
        (apply_root / "pov_footjob_frontal_sole_stroke_00001_.png").write_bytes(b"fake-png")
        applied_manifest = manifest_module.build_manifest(apply_root, subject_id="same_woman_001")
        applied_entry = (applied_manifest.get("entries") or [{}])[0]
        applied_prompt_variants = {
            variant.get("id"): variant for variant in applied_entry.get("prompt_variants", [])
        }
        _expect(
            applied_prompt_variants.get("soles_more_forward", {}).get("text") == sheet_probes[1].get("text"),
            "Atlas refine applied sidecar should rescan with the exact tested prompt text",
        )
        _expect(
            applied_prompt_variants.get("soles_more_forward", {}).get("evidence", {}).get("image_path")
            == "/tmp/pov_footjob_soles_more_forward.png",
            "Atlas refine applied sidecar should rescan with evidence image provenance",
        )
        _expect(
            applied_prompt_variants.get("soles_more_forward", {}).get("prompt_source", {}).get("kind") == "append_cues",
            "Atlas refine applied sidecar should rescan with append-cue provenance",
        )
        _expect(
            applied_prompt_variants.get("soles_more_forward", {}).get("reference_images")
            == ["blowjob_top_view/22_blowjob_top_view.png"],
            "Atlas refine applied sidecar should rescan with reference-image provenance",
        )
        catalog_cue_draft = manifest_module.build_catalog_cue_draft(
            applied_manifest,
            variant_key="pov_footjob_frontal_sole_stroke",
        )
        _expect(
            catalog_cue_draft.get("schema") == "sxcp_atlas_refine_catalog_cue_draft_v1",
            "Atlas refine catalog cue draft lost schema",
        )
        _expect(
            catalog_cue_draft.get("ready_cue_count") == 1,
            f"Atlas refine catalog cue draft should include one seedable append-cue candidate: {catalog_cue_draft}",
        )
        catalog_cue_candidates = catalog_cue_draft.get("candidates") or []
        _expect(
            catalog_cue_candidates[0].get("prompt_variant_cues") == [
                "the woman's soles press farther forward along the same contact line"
            ],
            "Atlas refine catalog cue draft should carry exact tested append cues",
        )
        _expect(
            catalog_cue_candidates[0].get("evidence", {}).get("score", {}).get("subject_identity") == "pass",
            "Atlas refine catalog cue draft should require seedable visual evidence",
        )
        _expect(
            catalog_cue_candidates[0].get("reference_images") == ["blowjob_top_view/22_blowjob_top_view.png"],
            "Atlas refine catalog cue draft should carry nearest atlas reference-image provenance",
        )
        catalog_cue_cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--folder",
                str(apply_root),
                "--subject-id",
                "same_woman_001",
                "--print-catalog-cue-draft",
                "--variant-key",
                "pov_footjob_frontal_sole_stroke",
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        _expect(catalog_cue_cli_result.returncode == 0, f"Atlas refine catalog cue draft CLI failed: {catalog_cue_cli_result.stderr}")
        cli_catalog_cue_draft = json.loads(catalog_cue_cli_result.stdout)
        _expect(
            cli_catalog_cue_draft.get("ready_cue_count") == 1,
            "Atlas refine catalog cue draft CLI should keep ready cue count",
        )
        applied_coverage = manifest_module.build_coverage_report(applied_manifest)
        applied_footjob_coverage = (applied_coverage.get("entries") or [{}])[0]
        _expect(
            applied_footjob_coverage.get("state") == "ready_for_catalog_review",
            f"Atlas refine coverage should mark seedable append-cue sidecars ready for catalog review: {applied_footjob_coverage}",
        )
        _expect(
            applied_footjob_coverage.get("seedable_variant_count") == 1,
            "Atlas refine coverage should count seedable variants",
        )
        _expect(
            applied_footjob_coverage.get("catalog_cue_candidate_count") == 1,
            "Atlas refine coverage should count seedable append-cue catalog candidates",
        )
        coverage_cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--folder",
                str(apply_root),
                "--subject-id",
                "same_woman_001",
                "--print-coverage-report",
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        _expect(coverage_cli_result.returncode == 0, f"Atlas refine coverage report CLI failed: {coverage_cli_result.stderr}")
        cli_coverage = json.loads(coverage_cli_result.stdout)
        _expect(
            cli_coverage.get("ready_for_catalog_review_count") == 1,
            "Atlas refine coverage report CLI should count catalog-review-ready entries",
        )
        applied_batch = manifest_module.build_prompt_batch(applied_manifest, "pov_footjob_frontal_sole_stroke")
        applied_batch_probes = applied_batch.get("probes") or []
        applied_variant_probe = next(
            (
                probe
                for probe in applied_batch_probes
                if probe.get("id") == "pov_footjob_frontal_sole_stroke_00001__soles_more_forward"
            ),
            {},
        )
        _expect(
            applied_variant_probe.get("text") == sheet_probes[1].get("text"),
            "Atlas refine applied sidecar should regenerate the exact tested prompt in the next batch",
        )
        _expect(
            applied_variant_probe.get("evidence", {}).get("score", {}).get("subject_identity") == "pass",
            "Atlas refine applied sidecar should carry evidence into regenerated batch probes",
        )
        seed_selection = manifest_module.select_seeded_prompt_variant(
            applied_manifest,
            "pov_footjob_frontal_sole_stroke",
            selection_seed=202,
            seed_slot="atlas_cue_seed",
        )
        _expect(
            seed_selection.get("schema") == "sxcp_atlas_refine_seed_selection_v1",
            "Atlas refine seed selection lost schema",
        )
        _expect(seed_selection.get("selection_seed") == 202, "Atlas refine seed selection should keep the cue seed")
        _expect(seed_selection.get("seed_slot") == "atlas_cue_seed", "Atlas refine seed selection should keep seed slot")
        _expect(seed_selection.get("eligible_candidate_count") == 1, "Atlas refine seed selection should only use promoted candidates")
        _expect(
            seed_selection.get("selected", {}).get("prompt_variant_id") == "soles_more_forward",
            "Atlas refine seed selection should select the promoted prompt variant",
        )
        _expect(
            seed_selection.get("selected", {}).get("text") == sheet_probes[1].get("text"),
            "Atlas refine seed selection should preserve exact tested prompt text",
        )
        _expect(
            seed_selection.get("selected", {}).get("evidence", {}).get("score", {}).get("subject_identity") == "pass",
            "Atlas refine seed selection should carry evidence score",
        )
        _expect(
            seed_selection.get("ineligible", [{}])[0].get("prompt_variant_id") == "old_axis",
            "Atlas refine seed selection should report unproven variants as ineligible",
        )
        _expect(
            "missing_seedable_evidence" in seed_selection.get("ineligible", [{}])[0].get("reason", ""),
            "Atlas refine seed selection should explain why unproven variants are ineligible",
        )
        try:
            manifest_module.select_seeded_prompt_variant(
                applied_manifest,
                "pov_footjob_frontal_sole_stroke",
                selection_seed=202,
                seed_slot="sampler_seed",
            )
        except ValueError as exc:
            _expect(
                "seed_slot" in str(exc) and "sampler_seed" in str(exc),
                f"Atlas refine seed selection should reject sampler_seed as a cue slot: {exc}",
            )
        else:
            raise AssertionError("Atlas refine seed selection should not allow cue selection through sampler_seed")
        seed_selection_again = manifest_module.select_seeded_prompt_variant(
            applied_manifest,
            "pov_footjob_frontal_sole_stroke",
            selection_seed=202,
            seed_slot="atlas_cue_seed",
        )
        _expect(
            seed_selection_again.get("selected", {}).get("prompt_variant_id")
            == seed_selection.get("selected", {}).get("prompt_variant_id"),
            "Atlas refine seed selection should be deterministic for the same cue seed",
        )
        order_manifest = json.loads(json.dumps(applied_manifest))
        order_entry = (order_manifest.get("entries") or [{}])[0]
        soles_variant = json.loads(json.dumps(applied_prompt_variants.get("soles_more_forward") or {}))
        ankle_variant = json.loads(json.dumps(soles_variant))
        ankle_variant["id"] = "ankle_angle_shift"
        ankle_variant["text"] = (
            footjob.get("prompt_text", "")
            + " the woman's ankle angle turns inward while the same contact line stays centered"
        )
        ankle_variant["cue_axes"]["foot_position"] = "ankle_angle_shift"
        ankle_variant["prompt_source"] = {
            "kind": "append_cues",
            "prompt_variant_id": "ankle_angle_shift",
            "append_cues": [
                "the woman's ankle angle turns inward while the same contact line stays centered"
            ],
            "tested_text_sha256": manifest_module._sha256_text(ankle_variant["text"]),
        }
        ankle_variant["evidence"]["image_path"] = "/tmp/pov_footjob_ankle_angle_shift.png"
        order_entry["prompt_variants"] = [soles_variant, ankle_variant]
        reversed_order_manifest = json.loads(json.dumps(order_manifest))
        (reversed_order_manifest.get("entries") or [{}])[0]["prompt_variants"] = [
            ankle_variant,
            soles_variant,
        ]
        ordered_selection = manifest_module.select_seeded_prompt_variant(
            order_manifest,
            "pov_footjob_frontal_sole_stroke",
            selection_seed=1,
            seed_slot="atlas_cue_seed",
        )
        reversed_order_selection = manifest_module.select_seeded_prompt_variant(
            reversed_order_manifest,
            "pov_footjob_frontal_sole_stroke",
            selection_seed=1,
            seed_slot="atlas_cue_seed",
        )
        _expect(
            ordered_selection.get("selected", {}).get("prompt_variant_id")
            == reversed_order_selection.get("selected", {}).get("prompt_variant_id")
            == "soles_more_forward",
            f"Atlas refine seed selection should be independent of sidecar prompt-variant order: {ordered_selection} vs {reversed_order_selection}",
        )
        selection_cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--folder",
                str(apply_root),
                "--subject-id",
                "same_woman_001",
                "--print-seed-selection",
                "--variant-key",
                "pov_footjob_frontal_sole_stroke",
                "--selection-seed",
                "202",
                "--seed-slot",
                "atlas_cue_seed",
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        _expect(selection_cli_result.returncode == 0, f"Atlas refine seed-selection CLI failed: {selection_cli_result.stderr}")
        cli_seed_selection = json.loads(selection_cli_result.stdout)
        _expect(
            cli_seed_selection.get("selected", {}).get("prompt_variant_id") == "soles_more_forward",
            "Atlas refine seed-selection CLI should select the promoted prompt variant",
        )
        selected_batch = manifest_module.build_seed_selected_prompt_batch(
            applied_manifest,
            "pov_footjob_frontal_sole_stroke",
            selection_seed=202,
            sampler_seed=101,
            seed_slot="atlas_cue_seed",
        )
        _expect(
            selected_batch.get("schema") == "sxcp_atlas_refine_prompt_batch_v1",
            "Atlas refine seed-selected batch should use prompt batch schema",
        )
        _expect(selected_batch.get("seed") == 101, "Atlas refine seed-selected batch should keep sampler seed")
        _expect(
            selected_batch.get("selection", {}).get("selected", {}).get("prompt_variant_id") == "soles_more_forward",
            "Atlas refine seed-selected batch should include the seed selection report",
        )
        selected_batch_probes = selected_batch.get("probes") or []
        _expect(
            [probe.get("id") for probe in selected_batch_probes]
            == [
                "pov_footjob_frontal_sole_stroke_00001__baseline",
                "pov_footjob_frontal_sole_stroke_00001__soles_more_forward",
            ],
            f"Atlas refine seed-selected batch should contain baseline and selected candidate only: {selected_batch_probes}",
        )
        _expect(
            selected_batch_probes[1].get("text") == sheet_probes[1].get("text"),
            "Atlas refine seed-selected batch should use exact selected prompt text",
        )
        _expect(
            selected_batch_probes[1].get("seed_metadata", {}).get("atlas_cue_seed") == 202,
            "Atlas refine seed-selected batch should record selection seed in the requested seed slot",
        )
        _expect(
            selected_batch_probes[1].get("evidence", {}).get("image_path") == "/tmp/pov_footjob_soles_more_forward.png",
            "Atlas refine seed-selected batch should preserve selected evidence",
        )
        with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as selected_batch_handle:
            json.dump(selected_batch, selected_batch_handle)
            selected_batch_path = Path(selected_batch_handle.name)
        try:
            loaded_selected_batch = sxcp_prompt_batch.load_batch(selected_batch_path)
        finally:
            selected_batch_path.unlink(missing_ok=True)
        _expect(
            len(loaded_selected_batch.get("probes") or []) == 2,
            "Atlas refine seed-selected batch should load through sxcp prompt batch helper",
        )
        seed_matrix = manifest_module.build_seed_matrix(
            applied_manifest,
            "pov_footjob_frontal_sole_stroke",
            selection_seeds=[202, 203],
            sampler_seeds=[101, 102],
            seed_slot="atlas_cue_seed",
        )
        _expect(
            seed_matrix.get("schema") == "sxcp_atlas_refine_seed_matrix_v1",
            "Atlas refine seed matrix lost schema",
        )
        _expect(
            seed_matrix.get("job_count") == 4,
            f"Atlas refine seed matrix should include every sampler/cue seed pair: {seed_matrix}",
        )
        matrix_jobs = seed_matrix.get("jobs") or []
        _expect(
            [(job.get("sampler_seed"), job.get("selection_seed")) for job in matrix_jobs]
            == [(101, 202), (101, 203), (102, 202), (102, 203)],
            f"Atlas refine seed matrix should keep deterministic sampler-major job order: {matrix_jobs}",
        )
        _expect(
            matrix_jobs[0].get("batch", {}).get("seed") == 101,
            "Atlas refine seed matrix should embed sampler-seeded batches",
        )
        sampler_102_job = next(
            (
                job
                for job in matrix_jobs
                if job.get("sampler_seed") == 102 and job.get("selection_seed") == 202
            ),
            {},
        )
        sampler_102_probes = sampler_102_job.get("batch", {}).get("probes") or []
        _expect(
            sampler_102_job.get("batch", {}).get("seed") == 102
            and sampler_102_job.get("candidate_probe", {}).get("seed_metadata", {}).get("sampler_seed") == 102
            and sampler_102_probes
            and all(probe.get("seed_metadata", {}).get("sampler_seed") == 102 for probe in sampler_102_probes),
            f"Atlas refine seed matrix should propagate the actual per-job sampler seed into probe metadata: {sampler_102_job}",
        )
        _expect(
            matrix_jobs[0].get("batch", {}).get("selection", {}).get("selection_seed") == 202,
            "Atlas refine seed matrix should embed cue-seed selection reports",
        )
        _expect(
            matrix_jobs[0].get("selected", {}).get("prompt_variant_id") == "soles_more_forward",
            "Atlas refine seed matrix should preserve selected prompt variant ids",
        )
        _expect(
            matrix_jobs[0].get("candidate_probe", {}).get("text") == sheet_probes[1].get("text"),
            "Atlas refine seed matrix should preserve exact selected candidate prompt text",
        )
        _expect(
            matrix_jobs[0].get("candidate_probe", {}).get("seed_metadata", {}).get("atlas_cue_seed") == 202,
            "Atlas refine seed matrix should record cue seed on candidate probes",
        )
        _expect(
            matrix_jobs[0].get("candidate_probe", {}).get("reference_images") == ["blowjob_top_view/22_blowjob_top_view.png"],
            "Atlas refine seed matrix should preserve selected candidate reference-image provenance",
        )
        with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as matrix_batch_handle:
            json.dump(matrix_jobs[0].get("batch"), matrix_batch_handle)
            matrix_batch_path = Path(matrix_batch_handle.name)
        try:
            loaded_matrix_batch = sxcp_prompt_batch.load_batch(matrix_batch_path)
        finally:
            matrix_batch_path.unlink(missing_ok=True)
        _expect(
            loaded_matrix_batch.get("seed") == 101 and len(loaded_matrix_batch.get("probes") or []) == 2,
            "Atlas refine seed matrix embedded batches should load through sxcp prompt batch helper",
        )
        _expect(
            (loaded_matrix_batch.get("probes") or [])[1].get("reference_images") == ["blowjob_top_view/22_blowjob_top_view.png"],
            "SxCP prompt batch loader should preserve matrix candidate reference-image provenance",
        )
        for duplicate_field, duplicate_kwargs in (
            ("sampler_seeds", {"selection_seeds": [202, 203], "sampler_seeds": [101, 101]}),
            ("selection_seeds", {"selection_seeds": [202, 202], "sampler_seeds": [101, 102]}),
        ):
            try:
                manifest_module.build_seed_matrix(
                    applied_manifest,
                    "pov_footjob_frontal_sole_stroke",
                    seed_slot="atlas_cue_seed",
                    **duplicate_kwargs,
                )
            except ValueError as exc:
                _expect(
                    duplicate_field in str(exc) and "duplicate" in str(exc),
                    f"Atlas refine seed matrix duplicate {duplicate_field} error should be explicit: {exc}",
                )
            else:
                raise AssertionError(f"Atlas refine seed matrix should reject duplicate {duplicate_field}")
        seed_matrix_cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--folder",
                str(apply_root),
                "--subject-id",
                "same_woman_001",
                "--print-seed-matrix",
                "--variant-key",
                "pov_footjob_frontal_sole_stroke",
                "--selection-seeds",
                "202,203",
                "--sampler-seeds",
                "101,102",
                "--seed-slot",
                "atlas_cue_seed",
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        _expect(seed_matrix_cli_result.returncode == 0, f"Atlas refine seed matrix CLI failed: {seed_matrix_cli_result.stderr}")
        cli_seed_matrix = json.loads(seed_matrix_cli_result.stdout)
        _expect(
            cli_seed_matrix.get("job_count") == 4,
            "Atlas refine seed matrix CLI should preserve matrix job count",
        )
        matrix_results = {
            "schema": "sxcp_atlas_refine_seed_matrix_results_v1",
            "jobs": [
                {
                    "id": job.get("id"),
                    "results": {
                        "seed": job.get("sampler_seed"),
                        "channel_in": "sxcp_eval_in",
                        "probes": [
                            {
                                "id": job.get("batch", {}).get("probes", [{}])[0].get("id"),
                                "prompt_order": "subject_first",
                                "turn": 300 + index * 2,
                                "image_path": f"/tmp/atlas_matrix_{index}_baseline.png",
                                "returned_seed": job.get("sampler_seed"),
                            },
                            {
                                "id": job.get("batch", {}).get("probes", [{}, {}])[1].get("id"),
                                "prompt_order": "subject_first",
                                "turn": 301 + index * 2,
                                "image_path": f"/tmp/atlas_matrix_{index}_candidate.png",
                                "returned_seed": job.get("sampler_seed"),
                            },
                        ],
                    },
                }
                for index, job in enumerate(matrix_jobs)
            ],
        }
        matrix_result_sheet = manifest_module.build_seed_matrix_result_sheet(
            seed_matrix,
            matrix_results,
            notes="matrix scoring pending",
        )
        _expect(
            matrix_result_sheet.get("schema") == "sxcp_atlas_refine_seed_matrix_result_sheet_v1",
            "Atlas refine seed matrix result sheet lost schema",
        )
        _expect(
            matrix_result_sheet.get("job_count") == 4,
            "Atlas refine seed matrix result sheet should preserve every matrix job",
        )
        matrix_sheet_jobs = matrix_result_sheet.get("jobs") or []
        _expect(
            matrix_sheet_jobs[0].get("sampler_seed") == 101
            and matrix_sheet_jobs[0].get("selection_seed") == 202,
            "Atlas refine seed matrix result sheet should keep sampler and cue seeds",
        )
        _expect(
            matrix_sheet_jobs[0].get("selected", {}).get("prompt_variant_id") == "soles_more_forward",
            "Atlas refine seed matrix result sheet should keep selected prompt variant id",
        )
        _expect(
            matrix_sheet_jobs[0].get("result_sheet", {}).get("selection", {}).get("selection_seed") == 202,
            "Atlas refine seed matrix result sheet should keep per-job selection reports",
        )
        matrix_job_probes = matrix_sheet_jobs[0].get("result_sheet", {}).get("probes") or []
        _expect(
            matrix_job_probes[1].get("text") == sheet_probes[1].get("text"),
            "Atlas refine seed matrix result sheet should preserve exact candidate prompt text",
        )
        _expect(
            matrix_job_probes[1].get("score", {}).get("pose_ownership") is None,
            "Atlas refine seed matrix result sheet should leave score slots empty for visual scoring",
        )
        _expect(
            matrix_job_probes[1].get("seed_metadata", {}).get("atlas_cue_seed") == 202,
            "Atlas refine seed matrix result sheet should keep cue seed metadata on candidate probes",
        )
        _expect(
            matrix_job_probes[1].get("reference_images") == ["blowjob_top_view/22_blowjob_top_view.png"],
            "Atlas refine seed matrix result sheet should keep candidate reference-image provenance",
        )
        duplicate_job_matrix = json.loads(json.dumps(seed_matrix))
        duplicate_job_matrix["jobs"][1]["id"] = duplicate_job_matrix["jobs"][0]["id"]
        try:
            manifest_module.build_seed_matrix_result_sheet(
                duplicate_job_matrix,
                matrix_results,
                notes="duplicate matrix job id should fail",
            )
        except ValueError as exc:
            _expect(
                "seed matrix jobs" in str(exc) and "duplicated" in str(exc),
                f"Atlas refine seed matrix result sheet duplicate job-id error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine seed matrix result sheet should reject duplicate matrix job ids")
        with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as matrix_handle:
            json.dump(seed_matrix, matrix_handle)
            cli_matrix_path = Path(matrix_handle.name)
        with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as matrix_results_handle:
            json.dump(matrix_results, matrix_results_handle)
            cli_matrix_results_path = Path(matrix_results_handle.name)
        try:
            matrix_result_sheet_cli_result = subprocess.run(
                [
                    sys.executable,
                    str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                    "--print-seed-matrix-result-sheet",
                    "--seed-matrix-json",
                    str(cli_matrix_path),
                    "--seed-matrix-results-json",
                    str(cli_matrix_results_path),
                    "--notes",
                    "matrix scoring pending",
                ],
                cwd=ROOT,
                capture_output=True,
                text=True,
                check=False,
            )
        finally:
            cli_matrix_path.unlink(missing_ok=True)
            cli_matrix_results_path.unlink(missing_ok=True)
        _expect(
            matrix_result_sheet_cli_result.returncode == 0,
            f"Atlas refine seed matrix result-sheet CLI failed: {matrix_result_sheet_cli_result.stderr}",
        )
        cli_matrix_result_sheet = json.loads(matrix_result_sheet_cli_result.stdout)
        _expect(
            cli_matrix_result_sheet.get("job_count") == 4,
            "Atlas refine seed matrix result-sheet CLI should preserve job count",
        )
        scored_matrix_result_sheet = json.loads(json.dumps(matrix_result_sheet))
        for index, job in enumerate(scored_matrix_result_sheet.get("jobs") or []):
            probes = job.get("result_sheet", {}).get("probes") or []
            probes[0]["score"].update(
                {
                    "atlas_pose_match": "baseline",
                    "contact_match": "baseline",
                    "pose_ownership": "pass",
                    "workspace_continuity": "pass",
                    "clothing_visibility": "pass",
                    "subject_identity": "pass",
                    "expression_eye_control": "pass",
                    "anatomy_proportion": "pass",
                    "prompt_noise": "pass",
                }
            )
            probes[1]["score"].update(
                {
                    "atlas_pose_match": "pass",
                    "contact_match": "pass",
                    "pose_ownership": "pass",
                    "workspace_continuity": "pass",
                    "clothing_visibility": "pass",
                    "subject_identity": "fail" if index == 3 else "pass",
                    "expression_eye_control": "pass",
                    "anatomy_proportion": "pass",
                    "prompt_noise": "pass",
                }
            )
            probes[1]["analysis_notes"] = f"matrix visual score {index}"
        matrix_promotion_report = manifest_module.build_seed_matrix_promotion_report(scored_matrix_result_sheet)
        _expect(
            matrix_promotion_report.get("schema") == "sxcp_atlas_refine_seed_matrix_promotion_report_v1",
            "Atlas refine seed matrix promotion report lost schema",
        )
        _expect(
            matrix_promotion_report.get("job_count") == 4,
            "Atlas refine seed matrix promotion report should preserve job count",
        )
        _expect(
            matrix_promotion_report.get("promotion_ready_job_count") == 3,
            f"Atlas refine seed matrix promotion report should count passing matrix jobs: {matrix_promotion_report}",
        )
        _expect(
            matrix_promotion_report.get("blocked_job_count") == 1,
            f"Atlas refine seed matrix promotion report should count blocked matrix jobs: {matrix_promotion_report}",
        )
        matrix_groups = matrix_promotion_report.get("groups") or []
        _expect(
            len(matrix_groups) == 2,
            f"Atlas refine seed matrix promotion report should group by cue seed and selected variant: {matrix_groups}",
        )
        first_group = matrix_groups[0]
        _expect(
            first_group.get("selection_seed") == 202
            and first_group.get("prompt_variant_id") == "soles_more_forward",
            f"Atlas refine seed matrix promotion report should preserve cue seed and variant group identity: {first_group}",
        )
        _expect(
            first_group.get("sampler_seed_count") == 2
            and first_group.get("promotion_ready_count") == 2
            and first_group.get("stable") is True,
            f"Atlas refine seed matrix promotion report should mark all-passing cue groups stable: {first_group}",
        )
        first_matrix_job = (matrix_promotion_report.get("jobs") or [{}])[0]
        _expect(
            first_matrix_job.get("candidate", {}).get("reference_images") == ["blowjob_top_view/22_blowjob_top_view.png"],
            f"Atlas refine seed matrix promotion report should preserve candidate reference-image provenance: {first_matrix_job}",
        )
        second_group = matrix_groups[1]
        _expect(
            second_group.get("selection_seed") == 203
            and second_group.get("promotion_ready_count") == 1
            and second_group.get("blocked_count") == 1
            and second_group.get("stable") is False,
            f"Atlas refine seed matrix promotion report should mark failed cue groups unstable: {second_group}",
        )
        _expect(
            any("subject_identity=fail" in blocker for blocker in second_group.get("blockers", [])),
            f"Atlas refine seed matrix promotion report should aggregate blockers: {second_group}",
        )
        duplicate_promotion_result_sheet = json.loads(json.dumps(scored_matrix_result_sheet))
        duplicate_promotion_result_sheet["jobs"][1]["id"] = duplicate_promotion_result_sheet["jobs"][0]["id"]
        try:
            manifest_module.build_seed_matrix_promotion_report(duplicate_promotion_result_sheet)
        except ValueError as exc:
            _expect(
                "seed matrix result sheet jobs" in str(exc) and "duplicated" in str(exc),
                f"Atlas refine seed matrix promotion duplicate job-id error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine seed matrix promotion should reject duplicate result-sheet job ids")
        mismatched_selected_result_sheet = json.loads(json.dumps(scored_matrix_result_sheet))
        mismatched_selected_result_sheet["jobs"][0]["selected"]["prompt_variant_id"] = "wrong_prompt_variant"
        try:
            manifest_module.build_seed_matrix_promotion_report(mismatched_selected_result_sheet)
        except ValueError as exc:
            _expect(
                "selected.prompt_variant_id" in str(exc) and "wrong_prompt_variant" in str(exc),
                f"Atlas refine seed matrix promotion selected/candidate mismatch error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine seed matrix promotion should reject selected/candidate prompt variant mismatches")
        mismatched_seed_slot_result_sheet = json.loads(json.dumps(scored_matrix_result_sheet))
        mismatched_seed_slot_result_sheet["jobs"][0]["seed_slot"] = "workspace_seed"
        try:
            manifest_module.build_seed_matrix_promotion_report(mismatched_seed_slot_result_sheet)
        except ValueError as exc:
            _expect(
                "seed_slot" in str(exc) and "workspace_seed" in str(exc) and "atlas_cue_seed" in str(exc),
                f"Atlas refine seed matrix promotion seed-slot mismatch error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine seed matrix promotion should reject job seed-slot drift")
        duplicate_declared_sampler_result_sheet = json.loads(json.dumps(scored_matrix_result_sheet))
        duplicate_declared_sampler_result_sheet["sampler_seeds"] = [101, 101]
        try:
            manifest_module.build_seed_matrix_promotion_report(duplicate_declared_sampler_result_sheet)
        except ValueError as exc:
            _expect(
                "sampler_seeds" in str(exc) and "duplicate" in str(exc),
                f"Atlas refine seed matrix promotion duplicate declared sampler-seed error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine seed matrix promotion should reject duplicate declared sampler seeds")
        undeclared_sampler_result_sheet = json.loads(json.dumps(scored_matrix_result_sheet))
        undeclared_sampler_result_sheet["jobs"][0]["sampler_seed"] = 999
        try:
            manifest_module.build_seed_matrix_promotion_report(undeclared_sampler_result_sheet)
        except ValueError as exc:
            _expect(
                "sampler_seed" in str(exc) and "999" in str(exc) and "sampler_seeds" in str(exc),
                f"Atlas refine seed matrix promotion undeclared sampler-seed error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine seed matrix promotion should reject jobs outside declared sampler seeds")
        duplicate_sampler_job_result_sheet = json.loads(json.dumps(scored_matrix_result_sheet))
        duplicate_sampler_job = json.loads(json.dumps(duplicate_sampler_job_result_sheet["jobs"][0]))
        duplicate_sampler_job["id"] = f"{duplicate_sampler_job['id']}__duplicate_sampler"
        duplicate_sampler_job_result_sheet["jobs"].append(duplicate_sampler_job)
        try:
            manifest_module.build_seed_matrix_promotion_report(duplicate_sampler_job_result_sheet)
        except ValueError as exc:
            _expect(
                "sampler_seed" in str(exc) and "duplicated" in str(exc),
                f"Atlas refine seed matrix promotion duplicate sampler job error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine seed matrix promotion should reject duplicate sampler jobs inside a cue group")
        duplicate_declared_selection_result_sheet = json.loads(json.dumps(scored_matrix_result_sheet))
        duplicate_declared_selection_result_sheet["selection_seeds"] = [202, 202]
        try:
            manifest_module.build_seed_matrix_promotion_report(duplicate_declared_selection_result_sheet)
        except ValueError as exc:
            _expect(
                "selection_seeds" in str(exc) and "duplicate" in str(exc),
                f"Atlas refine seed matrix promotion duplicate declared cue-seed error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine seed matrix promotion should reject duplicate declared cue seeds")
        undeclared_selection_result_sheet = json.loads(json.dumps(scored_matrix_result_sheet))
        undeclared_selection_result_sheet["jobs"][0]["selection_seed"] = 999
        try:
            manifest_module.build_seed_matrix_promotion_report(undeclared_selection_result_sheet)
        except ValueError as exc:
            _expect(
                "selection_seed" in str(exc) and "999" in str(exc) and "selection_seeds" in str(exc),
                f"Atlas refine seed matrix promotion undeclared cue-seed error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine seed matrix promotion should reject jobs outside declared cue seeds")
        mismatched_variant_result_sheet = json.loads(json.dumps(scored_matrix_result_sheet))
        mismatched_variant_job = mismatched_variant_result_sheet["jobs"][2]
        mismatched_variant_job["result_sheet"]["variant_key"] = "pov_other_pose_candidate"
        mismatched_variant_candidate = mismatched_variant_job["result_sheet"]["probes"][1]
        mismatched_variant_candidate["variant_key"] = "pov_other_pose_candidate"
        try:
            manifest_module.build_seed_matrix_promotion_report(mismatched_variant_result_sheet)
        except ValueError as exc:
            _expect(
                "variant_key" in str(exc) and "pov_other_pose_candidate" in str(exc),
                f"Atlas refine seed matrix promotion variant-key mismatch error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine seed matrix promotion should reject candidate variant-key drift inside a stable group")
        mismatched_source_result_sheet = json.loads(json.dumps(scored_matrix_result_sheet))
        mismatched_source_job = mismatched_source_result_sheet["jobs"][2]
        mismatched_source_job["result_sheet"]["source_entry_id"] = "pov_other_pose_00001"
        mismatched_source_job["result_sheet"]["source_stem"] = "pov_other_pose_00001_"
        mismatched_source_candidate = mismatched_source_job["result_sheet"]["probes"][1]
        mismatched_source_candidate["source_entry_id"] = "pov_other_pose_00001"
        mismatched_source_candidate["source_stem"] = "pov_other_pose_00001_"
        try:
            manifest_module.build_seed_matrix_promotion_report(mismatched_source_result_sheet)
        except ValueError as exc:
            _expect(
                "source_stem" in str(exc) and "pov_other_pose_00001_" in str(exc),
                f"Atlas refine seed matrix promotion source-stem mismatch error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine seed matrix promotion should reject source-stem drift inside a stable group")
        mismatched_prompt_text_result_sheet = json.loads(json.dumps(scored_matrix_result_sheet))
        mismatched_prompt_text_job = mismatched_prompt_text_result_sheet["jobs"][2]
        mismatched_prompt_text_candidate = mismatched_prompt_text_job["result_sheet"]["probes"][1]
        mismatched_prompt_text_candidate["text"] = (
            "A controlled same-subject footjob reference prompt with foreground soles. "
            "the woman's heels stay farther back along the same contact line"
        )
        try:
            manifest_module.build_seed_matrix_promotion_report(mismatched_prompt_text_result_sheet)
        except ValueError as exc:
            _expect(
                "prompt text" in str(exc) and "does not match" in str(exc),
                f"Atlas refine seed matrix promotion prompt-text drift error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine seed matrix promotion should reject prompt-text drift inside a stable group")
        incomplete_matrix_result_sheet = json.loads(json.dumps(scored_matrix_result_sheet))
        incomplete_matrix_result_sheet["jobs"] = [
            job
            for job in incomplete_matrix_result_sheet.get("jobs", [])
            if job.get("sampler_seed") == 101
        ]
        incomplete_matrix_promotion_report = manifest_module.build_seed_matrix_promotion_report(incomplete_matrix_result_sheet)
        incomplete_matrix_groups = incomplete_matrix_promotion_report.get("groups") or []
        _expect(
            incomplete_matrix_promotion_report.get("stable_group_count") == 0,
            f"Atlas refine seed matrix promotion should not mark incomplete sampler coverage stable: {incomplete_matrix_promotion_report}",
        )
        _expect(
            incomplete_matrix_groups
            and all(102 in group.get("missing_sampler_seeds", []) for group in incomplete_matrix_groups)
            and all("missing_sampler_coverage" in group.get("blockers", []) for group in incomplete_matrix_groups),
            f"Atlas refine seed matrix promotion should expose missing sampler coverage per group: {incomplete_matrix_groups}",
        )
        one_sampler_matrix_result_sheet = json.loads(json.dumps(scored_matrix_result_sheet))
        one_sampler_matrix_result_sheet["sampler_seeds"] = [101]
        one_sampler_matrix_result_sheet["jobs"] = [
            job
            for job in one_sampler_matrix_result_sheet.get("jobs", [])
            if job.get("sampler_seed") == 101
        ]
        one_sampler_matrix_promotion_report = manifest_module.build_seed_matrix_promotion_report(one_sampler_matrix_result_sheet)
        one_sampler_matrix_groups = one_sampler_matrix_promotion_report.get("groups") or []
        _expect(
            one_sampler_matrix_promotion_report.get("stable_group_count") == 0,
            f"Atlas refine seed matrix promotion should not mark single-sampler matrices stable: {one_sampler_matrix_promotion_report}",
        )
        _expect(
            one_sampler_matrix_groups
            and all("insufficient_sampler_coverage" in group.get("blockers", []) for group in one_sampler_matrix_groups),
            f"Atlas refine seed matrix promotion should expose insufficient sampler coverage per group: {one_sampler_matrix_groups}",
        )
        with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as scored_matrix_handle:
            json.dump(scored_matrix_result_sheet, scored_matrix_handle)
            cli_scored_matrix_path = Path(scored_matrix_handle.name)
        try:
            matrix_promotion_cli_result = subprocess.run(
                [
                    sys.executable,
                    str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                    "--print-seed-matrix-promotion-report",
                    "--seed-matrix-result-sheet-json",
                    str(cli_scored_matrix_path),
                ],
                cwd=ROOT,
                capture_output=True,
                text=True,
                check=False,
            )
        finally:
            cli_scored_matrix_path.unlink(missing_ok=True)
        _expect(
            matrix_promotion_cli_result.returncode == 0,
            f"Atlas refine seed matrix promotion CLI failed: {matrix_promotion_cli_result.stderr}",
        )
        cli_matrix_promotion_report = json.loads(matrix_promotion_cli_result.stdout)
        _expect(
            cli_matrix_promotion_report.get("promotion_ready_job_count") == 3,
            "Atlas refine seed matrix promotion CLI should preserve ready job count",
        )
        matrix_sidecar_draft = manifest_module.build_matrix_sidecar_update_draft(matrix_promotion_report)
        _expect(
            matrix_sidecar_draft.get("schema") == "sxcp_atlas_refine_matrix_sidecar_update_draft_v1",
            "Atlas refine matrix sidecar draft lost schema",
        )
        _expect(
            matrix_sidecar_draft.get("ready_group_count") == 1
            and matrix_sidecar_draft.get("skipped_group_count") == 1,
            f"Atlas refine matrix sidecar draft should include only stable cue groups: {matrix_sidecar_draft}",
        )
        matrix_updates = matrix_sidecar_draft.get("updates") or []
        _expect(
            len(matrix_updates) == 1
            and matrix_updates[0].get("sidecar_filename") == "pov_footjob_frontal_sole_stroke_00001_.json",
            f"Atlas refine matrix sidecar draft should preserve same-stem sidecar filename: {matrix_updates}",
        )
        matrix_variant = (matrix_updates[0].get("prompt_variants") or [{}])[0]
        _expect(
            matrix_variant.get("id") == "soles_more_forward",
            "Atlas refine matrix sidecar draft should preserve prompt variant id",
        )
        _expect(
            matrix_variant.get("text") == sheet_probes[1].get("text"),
            "Atlas refine matrix sidecar draft should keep exact selected prompt text",
        )
        matrix_evidence = matrix_variant.get("matrix_evidence") or {}
        _expect(
            matrix_evidence.get("stable") is True
            and matrix_evidence.get("selection_seed") == 202
            and matrix_evidence.get("sampler_seeds") == [101, 102],
            f"Atlas refine matrix sidecar draft should preserve stable sampler/cue evidence: {matrix_evidence}",
        )
        missing_job_promotion_report = json.loads(json.dumps(matrix_promotion_report))
        missing_job_promotion_report["groups"][0]["job_ids"].append("missing_matrix_job_id")
        try:
            manifest_module.build_matrix_sidecar_update_draft(missing_job_promotion_report)
        except ValueError as exc:
            _expect(
                "job_ids" in str(exc) and "missing_matrix_job_id" in str(exc),
                f"Atlas refine matrix sidecar draft missing job-id error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine matrix sidecar draft should reject stable groups with missing job ids")
        duplicate_group_job_id_promotion_report = json.loads(json.dumps(matrix_promotion_report))
        duplicate_group_job_id = duplicate_group_job_id_promotion_report["groups"][0]["job_ids"][0]
        duplicate_group_job_id_promotion_report["groups"][0]["job_ids"].append(duplicate_group_job_id)
        duplicate_group_job_id_promotion_report["groups"][0]["job_count"] = 3
        duplicate_group_job_id_promotion_report["groups"][0]["promotion_ready_count"] = 3
        try:
            manifest_module.build_matrix_sidecar_update_draft(duplicate_group_job_id_promotion_report)
        except ValueError as exc:
            _expect(
                "job_ids" in str(exc) and "duplicated" in str(exc) and duplicate_group_job_id in str(exc),
                f"Atlas refine matrix sidecar draft duplicate job-id error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine matrix sidecar draft should reject stable groups with duplicated job ids")
        mismatched_group_job_promotion_report = json.loads(json.dumps(matrix_promotion_report))
        stable_group = mismatched_group_job_promotion_report["groups"][0]
        mismatched_group_job = next(
            job
            for job in mismatched_group_job_promotion_report["jobs"]
            if job.get("selection_seed") != stable_group.get("selection_seed")
            and job.get("sampler_seed") == stable_group.get("sampler_seeds", [None])[0]
            and job.get("decision") == "seedable_candidate"
        )
        stable_group["job_ids"][0] = mismatched_group_job["id"]
        try:
            manifest_module.build_matrix_sidecar_update_draft(mismatched_group_job_promotion_report)
        except ValueError as exc:
            _expect(
                "selection_seed" in str(exc) and str(mismatched_group_job["selection_seed"]) in str(exc),
                f"Atlas refine matrix sidecar draft group identity drift error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine matrix sidecar draft should reject jobs outside the stable group identity")
        mismatched_group_prompt_text_report = json.loads(json.dumps(matrix_promotion_report))
        mismatched_group_prompt_text_job = next(
            job
            for job in mismatched_group_prompt_text_report["jobs"]
            if job.get("selection_seed") == 202 and job.get("sampler_seed") == 102
        )
        mismatched_group_prompt_text_job["candidate"]["text"] = (
            "A controlled same-subject footjob reference prompt with foreground soles. "
            "the woman's heels stay farther back along the same contact line"
        )
        try:
            manifest_module.build_matrix_sidecar_update_draft(mismatched_group_prompt_text_report)
        except ValueError as exc:
            _expect(
                "prompt text" in str(exc) and "expected" in str(exc),
                f"Atlas refine matrix sidecar draft prompt-text drift error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine matrix sidecar draft should reject prompt-text drift inside stable group job ids")
        incomplete_group_promotion_report = json.loads(json.dumps(matrix_promotion_report))
        incomplete_group_promotion_report["groups"][0]["job_ids"] = incomplete_group_promotion_report["groups"][0]["job_ids"][:1]
        try:
            manifest_module.build_matrix_sidecar_update_draft(incomplete_group_promotion_report)
        except ValueError as exc:
            _expect(
                "sampler_seeds" in str(exc) and "job_ids" in str(exc),
                f"Atlas refine matrix sidecar draft incomplete sampler coverage error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine matrix sidecar draft should reject stable groups whose job ids omit sampler coverage")
        inflated_count_promotion_report = json.loads(json.dumps(matrix_promotion_report))
        inflated_count_promotion_report["groups"][0]["job_count"] = 99
        inflated_count_promotion_report["groups"][0]["promotion_ready_count"] = 99
        try:
            manifest_module.build_matrix_sidecar_update_draft(inflated_count_promotion_report)
        except ValueError as exc:
            _expect(
                "job_count" in str(exc) and "promotion_ready_count" in str(exc),
                f"Atlas refine matrix sidecar draft count-drift error should be explicit: {exc}",
            )
        else:
            raise AssertionError("Atlas refine matrix sidecar draft should reject stable groups with inflated evidence counts")
        _expect(
            len(matrix_evidence.get("jobs") or []) == 2,
            "Atlas refine matrix sidecar draft should keep every passing matrix job",
        )
        _expect(
            matrix_variant.get("evidence", {}).get("seed") == 101,
            "Atlas refine matrix sidecar draft should keep representative single-image evidence for compatibility",
        )
        skipped_matrix_groups = matrix_sidecar_draft.get("skipped") or []
        _expect(
            skipped_matrix_groups
            and skipped_matrix_groups[0].get("selection_seed") == 203
            and "subject_identity=fail" in skipped_matrix_groups[0].get("blockers", []),
            f"Atlas refine matrix sidecar draft should skip unstable groups with blockers: {skipped_matrix_groups}",
        )
        with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as matrix_promotion_handle:
            json.dump(matrix_promotion_report, matrix_promotion_handle)
            cli_matrix_promotion_path = Path(matrix_promotion_handle.name)
        try:
            matrix_sidecar_cli_result = subprocess.run(
                [
                    sys.executable,
                    str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                    "--print-matrix-sidecar-update-draft",
                    "--seed-matrix-promotion-report-json",
                    str(cli_matrix_promotion_path),
                ],
                cwd=ROOT,
                capture_output=True,
                text=True,
                check=False,
            )
        finally:
            cli_matrix_promotion_path.unlink(missing_ok=True)
        _expect(
            matrix_sidecar_cli_result.returncode == 0,
            f"Atlas refine matrix sidecar draft CLI failed: {matrix_sidecar_cli_result.stderr}",
        )
        cli_matrix_sidecar_draft = json.loads(matrix_sidecar_cli_result.stdout)
        _expect(
            cli_matrix_sidecar_draft.get("ready_group_count") == 1,
            "Atlas refine matrix sidecar draft CLI should keep ready group count",
        )
        matrix_validation = manifest_module.validate_matrix_sidecar_update_draft(matrix_sidecar_draft)
        _expect(
            matrix_validation.get("schema") == "sxcp_atlas_refine_matrix_sidecar_update_validation_v1",
            "Atlas refine matrix sidecar validation lost schema",
        )
        _expect(
            matrix_validation.get("valid") is True,
            f"Atlas refine matrix sidecar draft should validate: {matrix_validation}",
        )
        _expect(
            matrix_validation.get("validated_variant_count") == 1,
            "Atlas refine matrix sidecar validation should count prompt variants",
        )
        duplicate_matrix_job_draft = json.loads(json.dumps(matrix_sidecar_draft))
        duplicate_matrix_evidence = duplicate_matrix_job_draft["updates"][0]["prompt_variants"][0]["matrix_evidence"]
        duplicate_matrix_evidence["jobs"].append(json.loads(json.dumps(duplicate_matrix_evidence["jobs"][0])))
        duplicate_matrix_evidence["job_count"] = len(duplicate_matrix_evidence["jobs"])
        duplicate_matrix_evidence["promotion_ready_count"] = len(duplicate_matrix_evidence["jobs"])
        duplicate_matrix_validation = manifest_module.validate_matrix_sidecar_update_draft(duplicate_matrix_job_draft)
        _expect(
            duplicate_matrix_validation.get("valid") is False,
            "Atlas refine matrix sidecar validation should reject duplicated matrix evidence jobs",
        )
        _expect(
            any("matrix_evidence.jobs" in error and "duplicated" in error for error in duplicate_matrix_validation.get("errors", [])),
            f"Atlas refine matrix sidecar validation duplicate matrix job error should be explicit: {duplicate_matrix_validation}",
        )
        duplicate_declared_sampler_draft = json.loads(json.dumps(matrix_sidecar_draft))
        duplicate_declared_sampler_evidence = duplicate_declared_sampler_draft["updates"][0]["prompt_variants"][0]["matrix_evidence"]
        duplicate_declared_sampler_evidence["sampler_seeds"] = [
            duplicate_declared_sampler_evidence["sampler_seeds"][0],
            duplicate_declared_sampler_evidence["sampler_seeds"][0],
            *duplicate_declared_sampler_evidence["sampler_seeds"][1:],
        ]
        duplicate_declared_sampler_validation = manifest_module.validate_matrix_sidecar_update_draft(duplicate_declared_sampler_draft)
        _expect(
            duplicate_declared_sampler_validation.get("valid") is False,
            "Atlas refine matrix sidecar validation should reject duplicated declared matrix sampler seeds",
        )
        _expect(
            any("matrix_evidence.sampler_seeds" in error and "duplicated" in error for error in duplicate_declared_sampler_validation.get("errors", [])),
            f"Atlas refine matrix sidecar validation duplicate declared sampler-seed error should be explicit: {duplicate_declared_sampler_validation}",
        )
        single_sampler_matrix_draft = json.loads(json.dumps(matrix_sidecar_draft))
        single_sampler_matrix_evidence = single_sampler_matrix_draft["updates"][0]["prompt_variants"][0]["matrix_evidence"]
        single_sampler_matrix_evidence["jobs"] = single_sampler_matrix_evidence["jobs"][:1]
        single_sampler_matrix_evidence["sampler_seeds"] = [single_sampler_matrix_evidence["jobs"][0]["sampler_seed"]]
        single_sampler_matrix_evidence["job_count"] = 1
        single_sampler_matrix_evidence["promotion_ready_count"] = 1
        single_sampler_matrix_validation = manifest_module.validate_matrix_sidecar_update_draft(single_sampler_matrix_draft)
        _expect(
            single_sampler_matrix_validation.get("valid") is False,
            "Atlas refine matrix sidecar validation should reject single-sampler stable matrix evidence",
        )
        _expect(
            any("matrix_evidence.sampler_seeds" in error and "at least 2" in error for error in single_sampler_matrix_validation.get("errors", [])),
            f"Atlas refine matrix sidecar validation single-sampler error should be explicit: {single_sampler_matrix_validation}",
        )
        invalid_matrix_turn_draft = json.loads(json.dumps(matrix_sidecar_draft))
        invalid_matrix_turn_draft["updates"][0]["prompt_variants"][0]["matrix_evidence"]["jobs"][0]["turn"] = "not-an-int"
        invalid_matrix_turn_validation = manifest_module.validate_matrix_sidecar_update_draft(invalid_matrix_turn_draft)
        _expect(
            invalid_matrix_turn_validation.get("valid") is False,
            "Atlas refine matrix sidecar validation should reject non-integer matrix job turns",
        )
        _expect(
            any("matrix_evidence.jobs[0].turn" in error and "integer" in error for error in invalid_matrix_turn_validation.get("errors", [])),
            f"Atlas refine matrix sidecar validation matrix job turn error should be explicit: {invalid_matrix_turn_validation}",
        )
        invalid_representative_turn_draft = json.loads(json.dumps(matrix_sidecar_draft))
        invalid_representative_turn_draft["updates"][0]["prompt_variants"][0]["evidence"]["turn"] = "not-an-int"
        invalid_representative_turn_validation = manifest_module.validate_matrix_sidecar_update_draft(invalid_representative_turn_draft)
        _expect(
            invalid_representative_turn_validation.get("valid") is False,
            "Atlas refine matrix sidecar validation should reject non-integer representative evidence turns",
        )
        _expect(
            any("evidence.turn" in error and "integer" in error for error in invalid_representative_turn_validation.get("errors", [])),
            f"Atlas refine matrix sidecar validation representative evidence turn error should be explicit: {invalid_representative_turn_validation}",
        )
        mismatched_seed_metadata_draft = json.loads(json.dumps(matrix_sidecar_draft))
        mismatched_seed_metadata_variant = mismatched_seed_metadata_draft["updates"][0]["prompt_variants"][0]
        mismatched_seed_metadata_variant["seed_metadata"]["atlas_cue_seed"] = 999
        mismatched_seed_metadata_validation = manifest_module.validate_matrix_sidecar_update_draft(mismatched_seed_metadata_draft)
        _expect(
            mismatched_seed_metadata_validation.get("valid") is False,
            "Atlas refine matrix sidecar validation should reject cue seed_metadata drift",
        )
        _expect(
            any("seed_metadata.atlas_cue_seed" in error and "999" in error and "202" in error for error in mismatched_seed_metadata_validation.get("errors", [])),
            f"Atlas refine matrix sidecar validation seed metadata drift error should be explicit: {mismatched_seed_metadata_validation}",
        )
        mismatched_representative_evidence_draft = json.loads(json.dumps(matrix_sidecar_draft))
        mismatched_representative_variant = mismatched_representative_evidence_draft["updates"][0]["prompt_variants"][0]
        mismatched_representative_variant["evidence"]["image_path"] = "/tmp/atlas_matrix_wrong_representative.png"
        mismatched_representative_validation = manifest_module.validate_matrix_sidecar_update_draft(
            mismatched_representative_evidence_draft
        )
        _expect(
            mismatched_representative_validation.get("valid") is False,
            "Atlas refine matrix sidecar validation should reject representative evidence drift",
        )
        _expect(
            any("evidence.image_path" in error and "matrix_evidence.jobs" in error for error in mismatched_representative_validation.get("errors", [])),
            f"Atlas refine matrix sidecar validation representative evidence drift error should be explicit: {mismatched_representative_validation}",
        )
        invalid_matrix_draft = json.loads(json.dumps(matrix_sidecar_draft))
        invalid_matrix_variant = invalid_matrix_draft["updates"][0]["prompt_variants"][0]
        invalid_matrix_variant["matrix_evidence"]["stable"] = False
        invalid_matrix_variant["matrix_evidence"]["seed_slot"] = "sampler_seed"
        invalid_matrix_variant["matrix_evidence"]["jobs"][0]["score"]["subject_identity"] = "fail"
        invalid_matrix_variant["negative_prompt"] = "do not include this"
        invalid_matrix_variant["prompt_source"]["prompt_variant_id"] = "wrong_axis"
        invalid_matrix_validation = manifest_module.validate_matrix_sidecar_update_draft(invalid_matrix_draft)
        _expect(
            invalid_matrix_validation.get("valid") is False,
            "Atlas refine matrix sidecar validation should reject unstable or failed matrix evidence",
        )
        _expect(
            any("matrix_evidence.stable" in error for error in invalid_matrix_validation.get("errors", [])),
            f"Atlas refine matrix sidecar validation should report unstable matrix evidence: {invalid_matrix_validation}",
        )
        _expect(
            any("matrix_evidence.seed_slot" in error and "sampler_seed" in error for error in invalid_matrix_validation.get("errors", [])),
            f"Atlas refine matrix sidecar validation should reject sampler_seed as a cue slot: {invalid_matrix_validation}",
        )
        _expect(
            any("matrix_evidence.jobs[0].score" in error for error in invalid_matrix_validation.get("errors", [])),
            f"Atlas refine matrix sidecar validation should report failed matrix job scores: {invalid_matrix_validation}",
        )
        _expect(
            any("negative_prompt" in error for error in invalid_matrix_validation.get("errors", [])),
            f"Atlas refine matrix sidecar validation should reject negative prompt fields: {invalid_matrix_validation}",
        )
        _expect(
            any("prompt_source.prompt_variant_id" in error for error in invalid_matrix_validation.get("errors", [])),
            f"Atlas refine matrix sidecar validation should reject mismatched prompt-source ids: {invalid_matrix_validation}",
        )
        with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as matrix_draft_handle:
            json.dump(matrix_sidecar_draft, matrix_draft_handle)
            cli_matrix_draft_path = Path(matrix_draft_handle.name)
        try:
            matrix_validation_cli_result = subprocess.run(
                [
                    sys.executable,
                    str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                    "--validate-matrix-sidecar-update-draft",
                    "--matrix-sidecar-update-draft-json",
                    str(cli_matrix_draft_path),
                ],
                cwd=ROOT,
                capture_output=True,
                text=True,
                check=False,
            )
        finally:
            cli_matrix_draft_path.unlink(missing_ok=True)
        _expect(
            matrix_validation_cli_result.returncode == 0,
            f"Atlas refine matrix sidecar validation CLI failed: {matrix_validation_cli_result.stderr}",
        )
        cli_matrix_validation = json.loads(matrix_validation_cli_result.stdout)
        _expect(
            cli_matrix_validation.get("valid") is True,
            "Atlas refine matrix sidecar validation CLI should validate the draft",
        )
        with tempfile.TemporaryDirectory() as matrix_apply_tmpdir:
            matrix_apply_root = Path(matrix_apply_tmpdir)
            matrix_sidecar_path = matrix_apply_root / "pov_footjob_frontal_sole_stroke_00001_.json"
            matrix_sidecar_path.write_text(
                json.dumps(
                    {
                        "notes": "preserve existing matrix sidecar notes",
                        "prompt_variants": [
                            {
                                "id": "old_axis",
                                "text": "Old reviewed prompt variant.",
                                "cue_axes": {"foot_position": "old"},
                            }
                        ],
                    },
                    ensure_ascii=True,
                ),
                encoding="utf-8",
            )
            matrix_apply_report = manifest_module.apply_matrix_sidecar_update_draft(
                matrix_sidecar_draft,
                matrix_apply_root,
            )
            _expect(
                matrix_apply_report.get("schema") == "sxcp_atlas_refine_matrix_sidecar_apply_report_v1",
                "Atlas refine matrix sidecar apply report lost schema",
            )
            _expect(
                matrix_apply_report.get("applied") is True,
                "Atlas refine matrix sidecar apply should mark applied",
            )
            _expect(
                matrix_apply_report.get("updated_file_count") == 1,
                "Atlas refine matrix sidecar apply should update one sidecar",
            )
            matrix_applied_sidecar = json.loads(matrix_sidecar_path.read_text(encoding="utf-8"))
            _expect(
                matrix_applied_sidecar.get("notes") == "preserve existing matrix sidecar notes",
                "Atlas refine matrix sidecar apply should preserve unrelated sidecar fields",
            )
            matrix_applied_variants = matrix_applied_sidecar.get("prompt_variants") or []
            _expect(
                [variant.get("id") for variant in matrix_applied_variants] == ["old_axis", "soles_more_forward"],
                f"Atlas refine matrix sidecar apply should append stable variants: {matrix_applied_variants}",
            )
            applied_matrix_evidence = matrix_applied_variants[1].get("matrix_evidence") or {}
            _expect(
                applied_matrix_evidence.get("stable") is True
                and applied_matrix_evidence.get("selection_seed") == 202
                and len(applied_matrix_evidence.get("jobs") or []) == 2,
                f"Atlas refine matrix sidecar apply should preserve full matrix evidence: {applied_matrix_evidence}",
            )
            matrix_second_apply_report = manifest_module.apply_matrix_sidecar_update_draft(
                matrix_sidecar_draft,
                matrix_apply_root,
            )
            _expect(
                matrix_second_apply_report.get("updated_file_count") == 1,
                "Atlas refine matrix sidecar apply should be idempotent and still report the touched sidecar",
            )
            matrix_applied_again = json.loads(matrix_sidecar_path.read_text(encoding="utf-8"))
            _expect(
                [variant.get("id") for variant in matrix_applied_again.get("prompt_variants", [])].count("soles_more_forward") == 1,
                "Atlas refine matrix sidecar apply should upsert matrix variants by id instead of duplicating them",
            )
            with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False, encoding="utf-8") as matrix_apply_draft_handle:
                json.dump(matrix_sidecar_draft, matrix_apply_draft_handle)
                cli_matrix_apply_draft_path = Path(matrix_apply_draft_handle.name)
            try:
                matrix_apply_cli_result = subprocess.run(
                    [
                        sys.executable,
                        str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                        "--apply-matrix-sidecar-update-draft",
                        "--matrix-sidecar-update-draft-json",
                        str(cli_matrix_apply_draft_path),
                        "--folder",
                        str(matrix_apply_root),
                    ],
                    cwd=ROOT,
                    capture_output=True,
                    text=True,
                    check=False,
                )
            finally:
                cli_matrix_apply_draft_path.unlink(missing_ok=True)
            _expect(
                matrix_apply_cli_result.returncode == 0,
                f"Atlas refine matrix sidecar apply CLI failed: {matrix_apply_cli_result.stderr}",
            )
            cli_matrix_apply_report = json.loads(matrix_apply_cli_result.stdout)
            _expect(
                cli_matrix_apply_report.get("applied") is True,
                "Atlas refine matrix sidecar apply CLI should report applied",
            )
            (matrix_apply_root / "pov_footjob_frontal_sole_stroke_00001_.txt").write_text(
                footjob.get("prompt_text", ""),
                encoding="utf-8",
            )
            (matrix_apply_root / "pov_footjob_frontal_sole_stroke_00001_.png").write_bytes(b"fake-png")
            matrix_applied_manifest = manifest_module.build_manifest(matrix_apply_root, subject_id="same_woman_001")
            matrix_applied_entry = (matrix_applied_manifest.get("entries") or [{}])[0]
            matrix_applied_prompt_variants = {
                variant.get("id"): variant for variant in matrix_applied_entry.get("prompt_variants", [])
            }
            rescanned_matrix_evidence = matrix_applied_prompt_variants.get("soles_more_forward", {}).get("matrix_evidence") or {}
            _expect(
                rescanned_matrix_evidence.get("stable") is True
                and rescanned_matrix_evidence.get("selection_seed") == 202,
                f"Atlas refine applied matrix sidecar should rescan with matrix evidence: {rescanned_matrix_evidence}",
            )
            matrix_prompt_batch = manifest_module.build_prompt_batch(
                matrix_applied_manifest,
                "pov_footjob_frontal_sole_stroke",
                sampler_seed=101,
            )
            matrix_batch_variant = next(
                (
                    probe
                    for probe in matrix_prompt_batch.get("probes", [])
                    if probe.get("id") == "pov_footjob_frontal_sole_stroke_00001__soles_more_forward"
                ),
                {},
            )
            _expect(
                matrix_batch_variant.get("matrix_evidence", {}).get("stable") is True
                and matrix_batch_variant.get("matrix_evidence", {}).get("selection_seed") == 202,
                f"Atlas refine normal prompt batch should carry stable matrix evidence: {matrix_batch_variant}",
            )
            matrix_batch_results = {
                "seed": matrix_prompt_batch.get("seed"),
                "channel_in": "sxcp_eval_in",
                "probes": [
                    {
                        "id": probe.get("id"),
                        "prompt_order": probe.get("prompt_order"),
                        "turn": 40 + index,
                        "image_path": f"/tmp/atlas_matrix_prompt_batch_{index}.png",
                        "returned_seed": matrix_prompt_batch.get("seed"),
                    }
                    for index, probe in enumerate(matrix_prompt_batch.get("probes") or [])
                ],
            }
            matrix_batch_result_sheet = manifest_module.build_result_sheet(
                matrix_prompt_batch,
                matrix_batch_results,
                notes="normal matrix batch scoring pending",
            )
            matrix_sheet_variant = next(
                (
                    probe
                    for probe in matrix_batch_result_sheet.get("probes", [])
                    if probe.get("id") == "pov_footjob_frontal_sole_stroke_00001__soles_more_forward"
                ),
                {},
            )
            _expect(
                matrix_sheet_variant.get("matrix_evidence", {}).get("stable") is True
                and matrix_sheet_variant.get("matrix_evidence", {}).get("selection_seed") == 202,
                f"Atlas refine normal result sheet should keep stable matrix evidence: {matrix_sheet_variant}",
            )
            scored_matrix_batch_result_sheet = json.loads(json.dumps(matrix_batch_result_sheet))
            for probe in scored_matrix_batch_result_sheet.get("probes") or []:
                if probe.get("id") == "pov_footjob_frontal_sole_stroke_00001__soles_more_forward":
                    probe["score"].update(
                        {
                            "atlas_pose_match": "pass",
                            "contact_match": "pass",
                            "pose_ownership": "pass",
                            "workspace_continuity": "pass",
                            "clothing_visibility": "pass",
                            "subject_identity": "pass",
                            "expression_eye_control": "pass",
                            "anatomy_proportion": "pass",
                            "prompt_noise": "pass",
                        }
                    )
            matrix_single_promotion = manifest_module.build_promotion_report(scored_matrix_batch_result_sheet)
            matrix_single_candidate = next(
                (
                    candidate
                    for candidate in matrix_single_promotion.get("candidates", [])
                    if candidate.get("prompt_variant_id") == "soles_more_forward"
                ),
                {},
            )
            _expect(
                matrix_single_candidate.get("matrix_evidence", {}).get("stable") is True
                and matrix_single_candidate.get("matrix_evidence", {}).get("selection_seed") == 202,
                f"Atlas refine normal promotion report should preserve matrix evidence: {matrix_single_candidate}",
            )
            matrix_single_sidecar_draft = manifest_module.build_sidecar_update_draft(matrix_single_promotion)
            matrix_single_variant = ((matrix_single_sidecar_draft.get("updates") or [{}])[0].get("prompt_variants") or [{}])[0]
            _expect(
                matrix_single_variant.get("matrix_evidence", {}).get("stable") is True
                and matrix_single_variant.get("matrix_evidence", {}).get("selection_seed") == 202,
                f"Atlas refine normal sidecar draft should preserve matrix evidence: {matrix_single_variant}",
            )
            unstable_matrix_result_sheet = json.loads(json.dumps(scored_matrix_batch_result_sheet))
            for probe in unstable_matrix_result_sheet.get("probes") or []:
                if probe.get("id") == "pov_footjob_frontal_sole_stroke_00001__soles_more_forward":
                    probe["matrix_evidence"]["stable"] = False
            unstable_matrix_promotion = manifest_module.build_promotion_report(unstable_matrix_result_sheet)
            unstable_matrix_candidate = next(
                (
                    candidate
                    for candidate in unstable_matrix_promotion.get("candidates", [])
                    if candidate.get("prompt_variant_id") == "soles_more_forward"
                ),
                {},
            )
            _expect(
                unstable_matrix_candidate.get("decision") == "rejected"
                and "unstable_matrix_evidence" in unstable_matrix_candidate.get("blockers", []),
                f"Atlas refine promotion report should reject unstable matrix evidence: {unstable_matrix_candidate}",
            )
            unstable_matrix_sidecar_draft = manifest_module.build_sidecar_update_draft(unstable_matrix_promotion)
            _expect(
                unstable_matrix_sidecar_draft.get("ready_candidate_count") == 0,
                f"Atlas refine sidecar draft should skip unstable matrix evidence: {unstable_matrix_sidecar_draft}",
            )
            matrix_selection = manifest_module.select_seeded_prompt_variant(
                matrix_applied_manifest,
                "pov_footjob_frontal_sole_stroke",
                selection_seed=202,
                seed_slot="atlas_cue_seed",
            )
            selected_matrix_evidence = matrix_selection.get("selected", {}).get("matrix_evidence") or {}
            _expect(
                selected_matrix_evidence.get("stable") is True
                and selected_matrix_evidence.get("selection_seed") == 202
                and selected_matrix_evidence.get("sampler_seeds") == [101, 102],
                f"Atlas refine seed selection should preserve stable matrix evidence: {selected_matrix_evidence}",
            )
            malformed_stable_matrix_manifest = json.loads(json.dumps(matrix_applied_manifest))
            malformed_entry = (malformed_stable_matrix_manifest.get("entries") or [{}])[0]
            for malformed_variant in malformed_entry.get("prompt_variants") or []:
                if malformed_variant.get("id") == "soles_more_forward":
                    malformed_matrix_evidence = malformed_variant["matrix_evidence"]
                    malformed_matrix_evidence["sampler_seeds"] = [
                        malformed_matrix_evidence["sampler_seeds"][0],
                        malformed_matrix_evidence["sampler_seeds"][0],
                        *malformed_matrix_evidence["sampler_seeds"][1:],
                    ]
            malformed_stable_matrix_selection = manifest_module.select_seeded_prompt_variant(
                malformed_stable_matrix_manifest,
                "pov_footjob_frontal_sole_stroke",
                selection_seed=202,
                seed_slot="atlas_cue_seed",
            )
            _expect(
                malformed_stable_matrix_selection.get("eligible_candidate_count") == 0
                and malformed_stable_matrix_selection.get("selected") == {},
                f"Atlas refine seed selection should reject malformed stable matrix evidence: {malformed_stable_matrix_selection}",
            )
            _expect(
                any("unstable_matrix_evidence" in item.get("reason", "") for item in malformed_stable_matrix_selection.get("ineligible", [])),
                f"Atlas refine seed selection should explain malformed stable matrix evidence rejection: {malformed_stable_matrix_selection}",
            )
            context_drift_matrix_manifest = json.loads(json.dumps(matrix_applied_manifest))
            context_drift_entry = (context_drift_matrix_manifest.get("entries") or [{}])[0]
            for context_drift_variant in context_drift_entry.get("prompt_variants") or []:
                if context_drift_variant.get("id") == "soles_more_forward":
                    context_drift_variant["seed_metadata"]["atlas_cue_seed"] = 999
            context_drift_matrix_selection = manifest_module.select_seeded_prompt_variant(
                context_drift_matrix_manifest,
                "pov_footjob_frontal_sole_stroke",
                selection_seed=202,
                seed_slot="atlas_cue_seed",
            )
            _expect(
                context_drift_matrix_selection.get("eligible_candidate_count") == 0
                and context_drift_matrix_selection.get("selected") == {},
                f"Atlas refine seed selection should reject context-drifted stable matrix evidence: {context_drift_matrix_selection}",
            )
            _expect(
                any("unstable_matrix_evidence" in item.get("reason", "") for item in context_drift_matrix_selection.get("ineligible", [])),
                f"Atlas refine seed selection should explain context-drifted stable matrix evidence rejection: {context_drift_matrix_selection}",
            )
            unstable_matrix_manifest = json.loads(json.dumps(matrix_applied_manifest))
            unstable_entry = (unstable_matrix_manifest.get("entries") or [{}])[0]
            for unstable_variant in unstable_entry.get("prompt_variants") or []:
                if unstable_variant.get("id") == "soles_more_forward":
                    unstable_variant["matrix_evidence"]["stable"] = False
            unstable_matrix_selection = manifest_module.select_seeded_prompt_variant(
                unstable_matrix_manifest,
                "pov_footjob_frontal_sole_stroke",
                selection_seed=202,
                seed_slot="atlas_cue_seed",
            )
            _expect(
                unstable_matrix_selection.get("eligible_candidate_count") == 0
                and unstable_matrix_selection.get("selected") == {},
                f"Atlas refine seed selection should reject explicitly unstable matrix evidence: {unstable_matrix_selection}",
            )
            _expect(
                any("unstable_matrix_evidence" in item.get("reason", "") for item in unstable_matrix_selection.get("ineligible", [])),
                f"Atlas refine seed selection should explain unstable matrix evidence rejection: {unstable_matrix_selection}",
            )
            matrix_catalog_cue_draft = manifest_module.build_catalog_cue_draft(
                matrix_applied_manifest,
                variant_key="pov_footjob_frontal_sole_stroke",
            )
            matrix_catalog_candidates = matrix_catalog_cue_draft.get("candidates") or []
            _expect(
                matrix_catalog_cue_draft.get("ready_cue_count") == 1
                and matrix_catalog_candidates[0].get("matrix_evidence", {}).get("stable") is True,
                f"Atlas refine catalog cue draft should preserve stable matrix evidence: {matrix_catalog_cue_draft}",
            )
            unstable_catalog_cue_draft = manifest_module.build_catalog_cue_draft(
                unstable_matrix_manifest,
                variant_key="pov_footjob_frontal_sole_stroke",
            )
            _expect(
                unstable_catalog_cue_draft.get("ready_cue_count") == 0,
                f"Atlas refine catalog cue draft should reject unstable matrix evidence: {unstable_catalog_cue_draft}",
            )
            _expect(
                any("unstable_matrix_evidence" in item.get("reason", "") for item in unstable_catalog_cue_draft.get("skipped", [])),
                f"Atlas refine catalog cue draft should explain unstable matrix evidence rejection: {unstable_catalog_cue_draft}",
            )
            unstable_coverage = manifest_module.build_coverage_report(unstable_matrix_manifest)
            unstable_coverage_entry = (unstable_coverage.get("entries") or [{}])[0]
            _expect(
                unstable_coverage_entry.get("catalog_cue_candidate_count") == 0
                and unstable_coverage_entry.get("state") != "ready_for_catalog_review",
                f"Atlas refine coverage should not mark unstable matrix evidence ready for catalog review: {unstable_coverage_entry}",
            )
            unstable_variant_summary = next(
                (
                    variant
                    for variant in unstable_coverage_entry.get("prompt_variants", [])
                    if variant.get("prompt_variant_id") == "soles_more_forward"
                ),
                {},
            )
            _expect(
                unstable_variant_summary.get("decision") == "rejected"
                and "unstable_matrix_evidence" in unstable_variant_summary.get("blockers", []),
                f"Atlas refine coverage should attach unstable matrix evidence blockers: {unstable_variant_summary}",
            )
            matrix_selected_batch = manifest_module.build_seed_selected_prompt_batch(
                matrix_applied_manifest,
                "pov_footjob_frontal_sole_stroke",
                selection_seed=202,
                sampler_seed=101,
                seed_slot="atlas_cue_seed",
            )
            matrix_selected_candidate = (matrix_selected_batch.get("probes") or [{}])[-1]
            candidate_matrix_evidence = matrix_selected_candidate.get("matrix_evidence") or {}
            _expect(
                candidate_matrix_evidence.get("stable") is True
                and candidate_matrix_evidence.get("selection_seed") == 202,
                f"Atlas refine selected batch should carry stable matrix evidence: {candidate_matrix_evidence}",
            )
            matrix_selected_results = {
                "seed": 101,
                "channel_in": "sxcp_eval_in",
                "probes": [
                    {
                        "id": (matrix_selected_batch.get("probes") or [{}])[0].get("id"),
                        "prompt_order": "subject_first",
                        "turn": 31,
                        "image_path": "/tmp/pov_footjob_matrix_selected_baseline.png",
                        "returned_seed": 101,
                    },
                    {
                        "id": matrix_selected_candidate.get("id"),
                        "prompt_order": "subject_first",
                        "turn": 32,
                        "image_path": "/tmp/pov_footjob_matrix_selected_candidate.png",
                        "returned_seed": 101,
                    },
                ],
            }
            matrix_selected_result_sheet = manifest_module.build_result_sheet(
                matrix_selected_batch,
                matrix_selected_results,
                notes="matrix-selected visual scoring pending",
            )
            matrix_selected_sheet_candidate = (matrix_selected_result_sheet.get("probes") or [{}])[-1]
            sheet_matrix_evidence = matrix_selected_sheet_candidate.get("matrix_evidence") or {}
            _expect(
                sheet_matrix_evidence.get("stable") is True
                and sheet_matrix_evidence.get("selection_seed") == 202,
                f"Atlas refine selected result sheet should preserve matrix evidence for scoring: {sheet_matrix_evidence}",
            )
        selected_results = {
            "seed": 101,
            "channel_in": "sxcp_eval_in",
            "probes": [
                {
                    "id": "pov_footjob_frontal_sole_stroke_00001__baseline",
                    "prompt_order": "subject_first",
                    "turn": 21,
                    "image_path": "/tmp/pov_footjob_selected_baseline.png",
                    "returned_seed": 101,
                },
                {
                    "id": "pov_footjob_frontal_sole_stroke_00001__soles_more_forward",
                    "prompt_order": "subject_first",
                    "turn": 22,
                    "image_path": "/tmp/pov_footjob_selected_candidate.png",
                    "returned_seed": 101,
                },
            ],
        }
        selected_result_sheet = manifest_module.build_result_sheet(
            selected_batch,
            selected_results,
            notes="selected seed visual scoring pending",
        )
        _expect(
            selected_result_sheet.get("selection", {}).get("selection_seed") == 202,
            "Atlas refine selected result sheet should keep cue seed selection metadata",
        )
        selected_sheet_probes = selected_result_sheet.get("probes") or []
        _expect(
            selected_sheet_probes[1].get("selection", {}).get("prompt_variant_id") == "soles_more_forward",
            "Atlas refine selected result sheet should keep selected prompt variant id on the candidate",
        )
        _expect(
            selected_sheet_probes[1].get("seed_metadata", {}).get("atlas_cue_seed") == 202,
            "Atlas refine selected result sheet should keep candidate cue seed metadata",
        )
        selected_batch_cli_result = subprocess.run(
            [
                sys.executable,
                str(ROOT / "tools" / "krea2_atlas_refine_manifest.py"),
                "--folder",
                str(apply_root),
                "--subject-id",
                "same_woman_001",
                "--print-seed-selected-batch",
                "--variant-key",
                "pov_footjob_frontal_sole_stroke",
                "--selection-seed",
                "202",
                "--sampler-seed",
                "101",
                "--seed-slot",
                "atlas_cue_seed",
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        _expect(selected_batch_cli_result.returncode == 0, f"Atlas refine seed-selected batch CLI failed: {selected_batch_cli_result.stderr}")
        cli_selected_batch = json.loads(selected_batch_cli_result.stdout)
        _expect(
            cli_selected_batch.get("selection", {}).get("selected", {}).get("prompt_variant_id") == "soles_more_forward",
            "Atlas refine seed-selected batch CLI should include the selected prompt variant",
        )
    unknown = by_variant.get("pov_unknown_pose_candidate") or {}
    _expect(unknown.get("known_variant") is False, "Atlas refine manifest should keep unknown pairs but flag them")
    missing = manifest.get("missing_pairs") or []
    _expect(
        missing and missing[0].get("stem") == "pov_blowjob_side_profile_oral_00002_",
        f"Atlas refine manifest should report the orphan stem: {missing}",
    )
    _expect(cli_manifest.get("entry_count") == 3, "Atlas refine manifest CLI lost paired entries")
    _expect("pov_footjob_frontal_sole_stroke" in cli_stdout, "Atlas refine manifest CLI lost known variant key")


def smoke_krea_pov_penetration_route() -> None:
    pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=3411,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(
            softcore_camera_mode="from_camera_config",
            hardcore_camera_mode="from_camera_config",
            camera_detail="compact",
        ),
        character_cast=_character_cast(pov_man=True),
        hardcore_position_config=_action_filter("penetration_only"),
        location_config=_coworking_location_config(),
        hardcore_camera_config=_orbit_camera(
            horizontal_angle=45,
            vertical_angle=0,
            zoom=5.5,
            subject_focus="action",
        ),
    )
    _expect_pair(pair, "krea_pov_penetration_route")
    hard_row = pair.get("hardcore_row") or {}
    _expect("Man A" in (hard_row.get("pov_character_labels") or []), "POV penetration hard row lost Man A POV label")
    _expect(not hard_row.get("camera_directive"), "POV penetration should suppress normal camera directive")
    krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
    prompt = _expect_text("krea_pov_penetration_route.krea_prompt", krea.get("krea_prompt"), 60)
    lower = prompt.lower()
    _expect("metadata" in krea.get("method", ""), "POV penetration route did not use metadata")
    _expect("viewer" in lower and "first-person" in lower, "POV penetration lost first-person wording")
    cast_index = lower.find("adult woman")
    viewer_index = lower.find("viewer")
    _expect(
        0 <= cast_index < viewer_index,
        "POV hardcore prompt should establish the visible woman before viewer/action wording",
    )
    _expect("penetrates" in lower or "penetration" in lower, "POV penetration lost penetration action wording")
    _expect("woman" in lower and "thigh" in lower, "POV penetration lost body-position anchors")
    _expect("action geography" not in lower, "POV penetration leaked abstract action-geography wording")
    _expect("use the multiangle camera" not in lower, "POV penetration leaked internal multiangle instruction")
    _expect("camera:" not in prompt, "POV penetration emitted normal third-person camera directive")
    _expect("role graph:" not in lower and "sexual scene:" not in lower, "POV penetration leaked raw prompt labels")
    style_index = lower.find("explicit consensual")
    _expect(
        style_index > 0 and lower[:style_index].rstrip().endswith("."),
        "POV penetration composition sentence should keep punctuation before style suffix",
    )


def smoke_pov_outercourse_position_routes() -> None:
    cases = [
        (
            "pov_outercourse_boobjob",
            "boobjob",
            ("breasts inward around", "directly above the glans"),
            ("pov boobjob position", "kneels upright", "pressed-together breasts", "woman's own fingers", "glans emerging"),
        ),
        (
            "pov_outercourse_testicle",
            "testicle_sucking",
            (
                "cheek against the pov viewer's inner thigh",
                "scrotum is the mouth surface",
                "testicles resting across her open lips",
                "face is the closest visible partner part",
            ),
            (
                "low side-pelvis pov",
                "face is the closest visible partner part",
                "cheek against the viewer's inner thigh",
                "scrotum is the mouth surface",
                "scrotal skin is the nearest mouth surface",
                "testicles resting across her open lips",
                "both testicles rest against her tongue from below",
            ),
        ),
        (
            "pov_outercourse_penis_licking",
            "penis_licking",
            ("head low under the pov viewer's penis", "tongue touches the underside"),
            ("tongue touches the underside", "glans at the tip", "viewer"),
        ),
        (
            "pov_outercourse_handjob",
            "handjob",
            ("one hand grips and strokes the pov viewer's penis", "strokes toward the glans"),
            ("pov handjob position", "woman's right hand wraps", "left hand steadies", "without his hands covering"),
        ),
        (
            "pov_outercourse_footjob",
            "footjob",
            (
                "two large overlapping soles dominate the pov viewer's lower center foreground",
                "inner arches press inward",
                "toes curl around both edges",
                "narrow visible strip of shaft and glans rises between the compressed feet",
            ),
            (
                "two large overlapping soles dominate the lower center foreground",
                "inner arches press inward",
                "toes curl around both edges",
                "narrow visible strip of shaft and glans rises between the compressed feet",
                "face and torso stay visible behind the large foreground feet",
            ),
        ),
    ]
    for offset, (name, position_key, role_terms, krea_terms) in enumerate(cases, start=3601):
        pair = pb.build_insta_of_pair(
            row_number=1,
            start_index=1,
            seed=offset,
            ethnicity="any",
            figure="random",
            no_plus_women=False,
            no_black=False,
            trigger=Trigger,
            prepend_trigger_to_prompt=True,
            options_json=_insta_options(
                softcore_camera_mode="from_camera_config",
                hardcore_camera_mode="from_camera_config",
                camera_detail="compact",
            ),
            character_cast=_character_cast(pov_man=True),
            hardcore_position_config=_position_filter("outercourse_only", "outercourse", [position_key]),
            location_config=_coworking_location_config(),
            hardcore_camera_config=_orbit_camera(
                horizontal_angle=45,
                vertical_angle=0,
                zoom=7.5,
                subject_focus="action",
            ),
        )
        _expect_pair(pair, name)
        hard_row = pair.get("hardcore_row") or {}
        _expect(hard_row.get("action_family") == "outercourse", f"{name} action_family should be outercourse")
        _expect(hard_row.get("position_family") == "outercourse", f"{name} position_family should be outercourse")
        _expect(hard_row.get("position_key") == position_key, f"{name} selected position should be primary position_key")
        _expect(position_key in (hard_row.get("position_keys") or []), f"{name} lost position key {position_key!r}")
        role_graph = _expect_text(f"{name}.source_role_graph", hard_row.get("source_role_graph"), 40).lower()
        for term in role_terms:
            _expect(term in role_graph, f"{name} role graph missing {term!r}: {role_graph}")
        krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
        prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 60).lower()
        _expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
        _expect("viewer" in prompt and "first-person" in prompt, f"{name} Krea prompt lost POV wording")
        _expect("camera:" not in krea.get("krea_prompt", ""), f"{name} Krea prompt emitted normal third-person camera directive")
        for term in krea_terms:
            _expect(term in prompt, f"{name} Krea prompt missing {term!r}: {prompt}")
        if position_key == "handjob":
            _expect("behind the penis shaft" not in prompt, f"{name} Krea prompt kept vague shaft-only wording: {prompt}")
        if position_key == "testicle_sucking":
            _expect("head is tucked under the penis shaft" not in prompt, f"{name} Krea prompt kept high-head testicle wording: {prompt}")
        if position_key == "footjob":
            _expect(" only " not in f" {prompt} ", f"{name} Krea prompt kept limiting-only wording: {prompt}")

    prone_item = "reclining penis-licking position while slow tongue licking on the underside of the penis"
    prone_axis = {"position": "reclining penis-licking position"}
    prone_role_graph = hardcore_role_outercourse.build_outercourse_role_graph(
        "Woman A",
        "Man A",
        prone_item,
        prone_axis,
        pov_labels=["Man A"],
    ).lower()
    for term in (
        "belly-down",
        "wide v-frame",
        "torso stretched low and horizontal",
        "hands wrap the base",
    ):
        _expect(term in prone_role_graph, f"Prone frontal penis-licking role graph missing {term!r}: {prone_role_graph}")
    _expect("head low under" not in prone_role_graph, f"Prone frontal penis-licking role graph kept under-shaft wording: {prone_role_graph}")

    prone_pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=3717,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(
            softcore_camera_mode="from_camera_config",
            hardcore_camera_mode="from_camera_config",
            camera_detail="compact",
        ),
        character_cast=_character_cast(pov_man=True),
        hardcore_position_config=_position_filter("outercourse_only", "outercourse", ["penis_licking"]),
        location_config=_coworking_location_config(),
        hardcore_camera_config=_orbit_camera(
            horizontal_angle=45,
            vertical_angle=0,
            zoom=7.5,
            subject_focus="action",
        ),
    )
    prone_row = prone_pair["hardcore_row"]
    prone_row["item"] = prone_item
    prone_row["item_axis_values"] = prone_axis
    prone_row["position_key"] = "penis_licking"
    prone_row["position_keys"] = ["reclining_oral", "penis_licking"]
    prone_row["source_role_graph"] = prone_role_graph
    prone_krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(prone_pair), target="hardcore")
    prone_prompt = _expect_text("pov_outercourse_prone_frontal_penis_licking.krea_prompt", prone_krea.get("krea_prompt"), 60).lower()
    for term in (
        "pov prone frontal oral position",
        "wide symmetrical v-frame",
        "torso stretched low and horizontal",
        "hands wrap the base",
        "centered mouth-to-shaft contact",
    ):
        _expect(term in prone_prompt, f"Prone frontal penis-licking Krea prompt missing {term!r}: {prone_prompt}")
    _expect("head low under the viewer's penis" not in prone_prompt, f"Prone frontal penis-licking Krea prompt kept under-shaft wording: {prone_prompt}")


def smoke_pov_oral_position_routes() -> None:
    cases = [
        (
            "pov_oral_kneeling",
            "kneeling",
            ("viewer's penis", "takes the viewer's penis in her mouth"),
            ("nadir-angle standing male pov top-view oral position", "short centered vertical column", "top-down office anchors"),
        ),
        (
            "pov_oral_face_sitting",
            "face_sitting",
            ("straddling the viewer's face", "pussy directly over the viewer's mouth"),
            ("close first-person underview", "straddling the viewer's face", "tongue contact visible"),
        ),
        (
            "pov_oral_sixty_nine",
            "sixty_nine",
            ("head-to-hips", "viewer's mouth on woman a's pussy"),
            ("pov sixty-nine oral position", "head-to-hips", "viewer's mouth on the woman's pussy", "lower-foreground body cues aligned"),
        ),
        (
            "pov_oral_edge_supported",
            "edge_supported",
            ("raised edge with thighs open", "viewer kneels between her legs"),
            ("pov raised-edge cunnilingus position", "raised edge with thighs open", "face at pussy height"),
        ),
        (
            "pov_oral_side_lying",
            "side_lying",
            ("woman a lies on her side", "viewer lies beside her hips"),
            ("pov side-lying cunnilingus position", "woman lies on her side", "top thigh lifted", "viewer lies beside her hips"),
        ),
        (
            "pov_oral_chair",
            "chair_oral",
            ("viewer sits in a chair", "kneels between his thighs"),
            ("pov chair oral position", "viewer sits in a chair", "kneels between the viewer's thighs", "head is low at his pelvis"),
        ),
    ]
    for offset, (name, position_key, role_terms, krea_terms) in enumerate(cases, start=3701):
        pair = pb.build_insta_of_pair(
            row_number=1,
            start_index=1,
            seed=offset,
            ethnicity="any",
            figure="random",
            no_plus_women=False,
            no_black=False,
            trigger=Trigger,
            prepend_trigger_to_prompt=True,
            options_json=_insta_options(
                softcore_camera_mode="from_camera_config",
                hardcore_camera_mode="from_camera_config",
                camera_detail="compact",
            ),
            character_cast=_character_cast(pov_man=True),
            hardcore_position_config=_position_filter("oral_only", "oral", [position_key]),
            location_config=_coworking_location_config(),
            hardcore_camera_config=_orbit_camera(
                horizontal_angle=45,
                vertical_angle=0,
                zoom=7.5,
                subject_focus="action",
            ),
        )
        _expect_pair(pair, name)
        hard_row = pair.get("hardcore_row") or {}
        _expect(hard_row.get("action_family") == "oral", f"{name} action_family should be oral")
        _expect(hard_row.get("position_family") == "oral", f"{name} position_family should be oral")
        _expect(position_key in (hard_row.get("position_keys") or []), f"{name} lost position key {position_key!r}")
        role_graph = _expect_text(f"{name}.source_role_graph", hard_row.get("source_role_graph"), 40).lower()
        for term in role_terms:
            _expect(term in role_graph, f"{name} role graph missing {term!r}: {role_graph}")
        krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
        prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 60).lower()
        _expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
        _expect("viewer" in prompt and "first-person" in prompt, f"{name} Krea prompt lost POV wording")
        _expect("viewer lies on the viewer" not in prompt, f"{name} Krea prompt kept recursive POV wording: {prompt}")
        _expect("camera:" not in krea.get("krea_prompt", ""), f"{name} Krea prompt emitted normal third-person camera directive")
        _expect("as he looks down" not in prompt, f"{name} Krea prompt kept unresolved male pronoun: {prompt}")
        _expect(
            "the woman takes the viewer's penis in her mouth with" not in prompt,
            f"{name} Krea prompt repeated oral contact detail after POV rewrite: {prompt}",
        )
        _expect("edge-supported oral position;" not in prompt, f"{name} Krea prompt kept source oral-position scaffold: {prompt}")
        if position_key == "kneeling":
            for term in (
                "nadir-angle standing male pov top-view oral position",
                "viewer looks almost straight down from his torso toward the floor",
                "nearby carpet/floor plane dominating the image",
                "short centered vertical column",
                "directly below the viewer between his feet",
                "hair crown, forehead, shoulders, hands, knees",
                "top-down office anchors",
            ):
                _expect(term in prompt, f"{name} Krea prompt lost top-down oral hierarchy term {term!r}: {prompt}")
            _expect("plumb-line" not in prompt and "map" not in prompt, f"{name} Krea prompt kept literalized top-view wording: {prompt}")
        for term in krea_terms:
            _expect(term in prompt, f"{name} Krea prompt missing {term!r}: {prompt}")

    top_variant_config = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2POVOralFilter"]().build(
        "replace",
        "",
        include_blowjob_top_down_vertical_shaft=True,
    )[0]
    top_variant_pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=3828,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(
            softcore_camera_mode="from_camera_config",
            hardcore_camera_mode="from_camera_config",
            camera_detail="compact",
        ),
        character_cast=_character_cast(pov_man=True),
        hardcore_position_config=top_variant_config,
        location_config=_coworking_location_config(),
        hardcore_camera_config=_orbit_camera(
            horizontal_angle=45,
            vertical_angle=0,
            zoom=7.5,
            subject_focus="action",
        ),
    )
    _expect_pair(top_variant_pair, "pov_oral_top_view_variant_filter")
    top_variant_row = top_variant_pair.get("hardcore_row") or {}
    top_variant_config_row = top_variant_row.get("hardcore_position_config") or {}
    _expect(
        top_variant_config_row.get("krea2_variant_keys") == ["pov_blowjob_top_down_vertical_shaft"],
        "Top-view POV filter lost exact variant key in row metadata",
    )
    top_axis_values = top_variant_row.get("item_axis_values") or {}
    _expect(
        top_axis_values.get("krea2_variant_keys") == ["pov_blowjob_top_down_vertical_shaft"],
        "Top-view POV filter exact variant key did not reach formatter axis metadata",
    )
    top_krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(top_variant_pair), target="hardcore")
    top_prompt = _expect_text("pov_oral_top_view_variant_filter.krea_prompt", top_krea.get("krea_prompt"), 60).lower()
    _expect("nadir-angle standing male pov top-view oral position" in top_prompt, "Top-view variant prompt lost exact top-view route")
    _expect("eye-level shot" not in top_prompt, f"Top-view variant prompt kept contradictory eye-level camera text: {top_prompt}")
    _expect("tongue extended toward genitals" not in top_prompt, f"Top-view variant prompt kept contradictory tongue-extension expression: {top_prompt}")

    restored_top_config = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2POVPromptRestore"]().build(
        True,
        True,
        True,
        True,
        True,
        top_variant_config,
    )[0]
    restored_top_pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=3828,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(
            softcore_camera_mode="from_camera_config",
            hardcore_camera_mode="from_camera_config",
            camera_detail="compact",
        ),
        character_cast=_character_cast(pov_man=True),
        hardcore_position_config=restored_top_config,
        location_config=_coworking_location_config(),
        hardcore_camera_config=_orbit_camera(
            horizontal_angle=45,
            vertical_angle=0,
            zoom=7.5,
            subject_focus="action",
        ),
    )
    restored_top_row = restored_top_pair.get("hardcore_row") or {}
    restored_top_axis = restored_top_row.get("item_axis_values") or {}
    restored_details = restored_top_axis.get("restored_prompt_details") or []
    _expect(isinstance(restored_details, list), "Krea2 POV Prompt Restore should keep restored prompt details as a list")
    _expect(restored_details, "Krea2 POV Prompt Restore should add sampled restored axis details to row metadata")
    restored_top_krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(restored_top_pair), target="hardcore")
    restored_top_prompt = _expect_text("pov_oral_top_view_restore.krea_prompt", restored_top_krea.get("krea_prompt"), 60).lower()
    _expect(
        restored_top_prompt != top_prompt,
        "Krea2 POV Prompt Restore should visibly change the final Krea prompt, not only metadata",
    )
    _expect(
        "nadir-angle standing male pov top-view oral position" in restored_top_prompt,
        "Krea2 POV Prompt Restore should preserve the exact atlas pose while adding detail",
    )
    _expect(
        "eye-level" not in restored_top_prompt,
        f"Krea2 POV Prompt Restore should not reintroduce contradictory eye-level camera wording: {restored_top_prompt}",
    )
    _expect(
        any(str(detail).lower() in restored_top_prompt for detail in restored_details),
        f"Krea2 POV Prompt Restore final prompt did not include sampled restored details: {restored_top_prompt}",
    )
    clothing_only_top_config = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2POVPromptRestore"]().build(
        True,
        False,
        False,
        False,
        True,
        top_variant_config,
    )[0]
    clothing_only_top_pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=3828,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(
            softcore_camera_mode="from_camera_config",
            hardcore_camera_mode="from_camera_config",
            camera_detail="compact",
        ),
        character_cast=_character_cast(pov_man=True),
        hardcore_position_config=clothing_only_top_config,
        location_config=_coworking_location_config(),
        hardcore_camera_config=_orbit_camera(
            horizontal_angle=45,
            vertical_angle=0,
            zoom=7.5,
            subject_focus="action",
        ),
    )
    clothing_only_top_row = clothing_only_top_pair.get("hardcore_row") or {}
    clothing_only_top_axis = clothing_only_top_row.get("item_axis_values") or {}
    clothing_only_axes = clothing_only_top_axis.get("restored_prompt_axes") or []
    clothing_only_details = clothing_only_top_axis.get("restored_prompt_details") or []
    _expect(
        clothing_only_axes == ["clothing_detail"],
        f"Krea2 POV Prompt Restore clothing-only mode should restore clothing_detail, got {clothing_only_axes!r}",
    )
    _expect(
        clothing_only_details,
        "Krea2 POV Prompt Restore clothing-only mode should add a sampled clothing detail",
    )
    clothing_only_top_krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(clothing_only_top_pair), target="hardcore")
    clothing_only_top_prompt = _expect_text(
        "pov_oral_top_view_restore_clothing_only.krea_prompt",
        clothing_only_top_krea.get("krea_prompt"),
        60,
    ).lower()
    clothing_only_prompt_key = _clean_key(clothing_only_top_prompt)
    clothing_only_state = _clean_key(clothing_only_top_pair.get("hardcore_clothing_state"))
    expected_clothing_terms = [
        term
        for term in (
            "body is fully exposed",
            "bare skin unobstructed",
            "pushed aside",
            "partly removed",
            "oral contact unobstructed",
            "belt open and pants lowered below the hips",
        )
        if term in clothing_only_state
    ]
    _expect(
        expected_clothing_terms,
        f"Krea2 POV Prompt Restore clothing-only fixture lost softcore-derived clothing state: {clothing_only_state}",
    )
    _expect(
        any(term in clothing_only_prompt_key for term in expected_clothing_terms),
        f"Krea2 POV Prompt Restore clothing-only final prompt did not follow softcore clothing continuity: {clothing_only_top_prompt}",
    )
    _expect(
        "clothing state:" not in clothing_only_top_prompt,
        f"Krea2 POV Prompt Restore clothing-only final prompt leaked raw clothing label: {clothing_only_top_prompt}",
    )
    _expect(
        "pov foreground clothing cue" not in clothing_only_top_prompt,
        f"Krea2 POV Prompt Restore clothing-only final prompt should not add a second POV clothing/body cue: {clothing_only_top_prompt}",
    )
    _expect(
        not any(str(detail).lower() in clothing_only_top_prompt for detail in clothing_only_details),
        f"Krea2 POV Prompt Restore clothing-only final prompt should not dump raw clothing_detail: {clothing_only_top_prompt}",
    )

    sitting_variant_config = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2POVOralFilter"]().build(
        "replace",
        "",
        include_blowjob_sitting_upright_oral=True,
    )[0]
    sitting_variant_pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=3831,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(
            softcore_camera_mode="from_camera_config",
            hardcore_camera_mode="from_camera_config",
            camera_detail="compact",
        ),
        character_cast=_character_cast(pov_man=True),
        hardcore_position_config=sitting_variant_config,
        location_config=_coworking_location_config(),
        hardcore_camera_config=_orbit_camera(
            horizontal_angle=45,
            vertical_angle=0,
            zoom=7.5,
            subject_focus="action",
        ),
    )
    _expect_pair(sitting_variant_pair, "pov_oral_sitting_variant_filter")
    sitting_variant_row = sitting_variant_pair.get("hardcore_row") or {}
    sitting_variant_config_row = sitting_variant_row.get("hardcore_position_config") or {}
    _expect(
        sitting_variant_config_row.get("krea2_variant_keys") == ["pov_blowjob_sitting_upright_oral"],
        "Sitting oral POV filter lost exact variant key in row metadata",
    )
    _expect(
        "blowjob_sitting" in (sitting_variant_config_row.get("positions") or []),
        "Sitting oral POV filter lost exact sitting route position in row config",
    )
    sitting_axis_values = sitting_variant_row.get("item_axis_values") or {}
    _expect(
        sitting_axis_values.get("krea2_variant_keys") == ["pov_blowjob_sitting_upright_oral"],
        "Sitting oral POV filter exact variant key did not reach formatter axis metadata",
    )
    sitting_variant_krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(sitting_variant_pair), target="hardcore")
    sitting_variant_prompt = _expect_text(
        "pov_oral_sitting_variant_filter.krea_prompt",
        sitting_variant_krea.get("krea_prompt"),
        60,
    ).lower()
    for term in (
        "pov upright sitting oral position",
        "woman sits low between the viewer's open thighs",
        "face lowers close to the exact center contact point",
        "open mouth covers the centered tip",
        "hands wrapped low at the base",
    ):
        _expect(term in sitting_variant_prompt, f"Sitting oral variant prompt missing {term!r}: {sitting_variant_prompt}")
    _expect(
        "pov open-thigh cunnilingus position" not in sitting_variant_prompt,
        f"Sitting oral variant drifted into generic cunnilingus route: {sitting_variant_prompt}",
    )
    _expect(
        "viewer kneels between her legs" not in sitting_variant_prompt,
        f"Sitting oral variant kept generic kneeling-between-legs route: {sitting_variant_prompt}",
    )
    _expect(
        "tongue extended toward genitals" not in sitting_variant_prompt,
        f"Sitting oral variant kept contradictory tongue-extension expression: {sitting_variant_prompt}",
    )
    _expect("eye-level shot" not in sitting_variant_prompt, f"Sitting oral variant kept contradictory eye-level camera text: {sitting_variant_prompt}")

    side_body_item = "side-lying oral position while blowjob with lips wrapped around the viewer's penis"
    side_body_axis = {"position": "side-lying oral position"}
    side_body_role_graph = hardcore_role_oral.build_oral_role_graph(
        "Woman A",
        "Man A",
        side_body_item,
        side_body_axis,
        pov_labels=["Man A"],
    ).lower()
    for term in (
        "adult male viewer's abdomen, navel, pelvis, and near thigh",
        "broad horizontal body surface",
        "woman a enters laterally from the left edge",
        "mouth on the shaft at the male abdomen line",
        "lips touching the shaft at the male abdomen line",
        "mouth-to-shaft contact is the nearest facial detail",
        "adult male viewer's own torso starts at the lower edge",
        "lower-right foreground",
        "camera owner's body",
        "hand around the base under her lips",
    ):
        _expect(term in side_body_role_graph, f"Side-profile oral role graph missing {term!r}: {side_body_role_graph}")

    side_body_pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=3729,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(
            softcore_camera_mode="from_camera_config",
            hardcore_camera_mode="from_camera_config",
            camera_detail="compact",
        ),
        character_cast=_character_cast(pov_man=True),
        hardcore_position_config=_position_filter("oral_only", "oral", ["side_lying"]),
        location_config=_coworking_location_config(),
        hardcore_camera_config=_orbit_camera(
            horizontal_angle=45,
            vertical_angle=0,
            zoom=7.5,
            subject_focus="action",
        ),
    )
    side_body_row = side_body_pair["hardcore_row"]
    side_body_row["item"] = side_body_item
    side_body_row["item_axis_values"] = side_body_axis
    side_body_row["position_key"] = "side_lying"
    side_body_row["position_keys"] = ["side_lying", "blowjob_side"]
    side_body_row["source_role_graph"] = side_body_role_graph
    side_body_row["role_graph"] = side_body_role_graph
    side_body_krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(side_body_pair), target="hardcore")
    side_body_prompt = _expect_text("pov_oral_side_profile_body_line.krea_prompt", side_body_krea.get("krea_prompt"), 80).lower()
    for term in (
        "pov side-profile oral body-line position",
        "male viewer's abdomen, navel, pelvis, and near thigh create a broad horizontal body surface",
        "woman enters laterally from the left edge beside his hip",
        "mouth on the shaft at the male abdomen line",
        "lips touching the shaft at the male abdomen line",
        "mouth-to-shaft contact is the nearest facial detail",
        "adult male viewer's own torso starts at the lower edge",
        "lower-right foreground",
        "camera owner's body",
        "hand around the base under her lips",
    ):
        _expect(term in side_body_prompt, f"Side-profile oral Krea prompt missing {term!r}: {side_body_prompt}")
    _expect("side-phone" not in side_body_prompt, f"Side-profile oral Krea prompt drifted into non-POV side-phone wording: {side_body_prompt}")

    side_body_clothing_config = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2POVOralFilter"]().build(
        "replace",
        "",
        include_blowjob_side_profile_oral=True,
    )[0]
    side_body_clothing_config = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2POVPromptRestore"]().build(
        True,
        False,
        False,
        False,
        True,
        side_body_clothing_config,
    )[0]
    side_body_clothing_pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=3730,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(
            softcore_camera_mode="from_camera_config",
            hardcore_camera_mode="from_camera_config",
            camera_detail="compact",
            hardcore_clothing_continuity="partially_removed",
        ),
        character_cast=_character_cast(pov_man=True),
        hardcore_position_config=side_body_clothing_config,
        location_config=_coworking_location_config(),
        hardcore_camera_config=_orbit_camera(
            horizontal_angle=45,
            vertical_angle=0,
            zoom=7.5,
            subject_focus="action",
        ),
    )
    side_body_problem_clothing = (
        "Clothing state: Woman A's denim shorts are pulled aside or removed below the hips; "
        "button-down shirt tied at the waist, a fitted bralette remain visible from the same outfit; "
        "POV foreground clothing cue: a casual shirt with belt open and pants partly lowered, "
        "appearing as the viewer's hands, forearms, sleeves, or torso edge."
    )
    side_body_clothing_pair["hardcore_clothing_state"] = side_body_problem_clothing
    side_body_clothing_pair["hardcore_row"]["hardcore_clothing_state"] = side_body_problem_clothing
    side_body_clothing_krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(side_body_clothing_pair), target="hardcore")
    side_body_clothing_prompt = _expect_text(
        "pov_oral_side_profile_body_line_restore_clothing.krea_prompt",
        side_body_clothing_krea.get("krea_prompt"),
        80,
    ).lower()
    _expect(
        "pov side-profile oral body-line position" in side_body_clothing_prompt,
        "Side-profile oral clothing restore lost the atlas body-line pose",
    )
    _expect(
        "pov foreground clothing cue" not in side_body_clothing_prompt,
        f"Side-profile oral clothing restore should not add a second POV foreground clothing cue: {side_body_clothing_prompt}",
    )
    _expect(
        "denim shorts" not in side_body_clothing_prompt and "below the hips" not in side_body_clothing_prompt,
        f"Side-profile oral clothing restore should not expose hidden lower-body clothing: {side_body_clothing_prompt}",
    )
    _expect(
        "button-down shirt tied at the waist" in side_body_clothing_prompt and "fitted bralette" in side_body_clothing_prompt,
        f"Side-profile oral clothing restore should preserve visible upper outfit continuity: {side_body_clothing_prompt}",
    )
    _expect(
        "the woman's lower garments are pulled aside out of frame" in side_body_clothing_prompt,
        f"Side-profile oral clothing restore should keep partial-removal state without exposing hidden garments: {side_body_clothing_prompt}",
    )
    _expect(
        "the woman wears the button-down shirt tied at the waist" in side_body_clothing_prompt,
        f"Side-profile oral clothing restore should assign visible clothing to the woman: {side_body_clothing_prompt}",
    )

    sitting_pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=3731,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(
            softcore_camera_mode="from_camera_config",
            hardcore_camera_mode="from_camera_config",
            camera_detail="compact",
        ),
        character_cast=_character_cast(pov_man=True),
        hardcore_position_config=_position_filter("oral_only", "oral", ["reclining_oral"]),
        location_config=_coworking_location_config(),
        hardcore_camera_config=_orbit_camera(
            horizontal_angle=45,
            vertical_angle=0,
            zoom=7.5,
            subject_focus="action",
        ),
    )
    sitting_row = sitting_pair["hardcore_row"]
    sitting_role_graph = (
        "Woman A sits upright between Man A's open thighs in a first-person sitting oral frame; "
        "her face lowers close to the shaft tip while her hands stay low at the base."
    )
    sitting_row["item"] = "upright sitting oral position with her mouth on the viewer's penis"
    sitting_row["item_axis_values"] = {"position": "upright sitting oral position"}
    sitting_row["position_key"] = "reclining_oral"
    sitting_row["position_keys"] = ["reclining_oral", "blowjob_sitting"]
    sitting_row["source_role_graph"] = sitting_role_graph
    sitting_row["role_graph"] = sitting_role_graph
    sitting_krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(sitting_pair), target="hardcore")
    sitting_prompt = _expect_text("pov_oral_sitting_upright.krea_prompt", sitting_krea.get("krea_prompt"), 80).lower()
    for term in (
        "pov upright sitting oral position",
        "woman sits low between his open thighs",
        "face lowered close to the exact center contact point",
        "open mouth covers the tip",
        "hands stay low at the base",
    ):
        _expect(term in sitting_prompt, f"Upright sitting oral Krea prompt missing {term!r}: {sitting_prompt}")
    _expect("pov prone frontal oral position" not in sitting_prompt, f"Upright sitting oral drifted into laying route: {sitting_prompt}")
    _expect("pov kneeling oral position" not in sitting_prompt, f"Upright sitting oral drifted into top-view kneeling route: {sitting_prompt}")


def smoke_pov_climax_target_policy() -> None:
    context = (
        "doggy rear-entry on all fours with hips raised and ass toward the camera; "
        "mouth, face, lips, and tongue appear only in expression detail"
    )
    target = krea_pov_actions.pov_ejaculation_target(context)
    _expect(
        target == "across her ass, thighs, and lower back",
        f"rear-entry climax target should beat face-expression tokens, got {target!r}",
    )
    phrase = krea_pov_actions.pov_action_phrase(
        "Man A ejaculates semen onto Woman A's face and chest; visible semen lands while her mouth stays open",
        ["Man A"],
        role_graph="Woman A is on all fours directly in front of Man A with hips raised while Man A is positioned behind her.",
        hard_item="cumshot with mouth and face expression",
        composition="first-person rear-view frame looking down at the woman's raised ass",
        axis_values={"position": "doggy style position"},
    ).lower()
    _expect("face and chest" not in phrase, f"POV rear-entry climax kept incompatible face/chest target: {phrase}")
    _expect("across her ass, thighs, and lower back" in phrase, f"POV rear-entry climax lost rear target: {phrase}")
    open_thigh_phrase = krea_pov_actions.pov_action_phrase(
        (
            "Woman A lies on her back with thighs open while Man A kneels between her legs "
            "and ejaculates semen across her pussy and thighs; thick visible fluid covers the exposed opening"
        ),
        ["Man A"],
        role_graph=(
            "Woman A lies on her back with thighs open toward Man A after ejaculation, "
            "with semen and clear fluid covering the exposed pussy and inner thighs."
        ),
        hard_item="post-ejaculation open-thigh display with thick semen or fluid around the exposed opening",
        composition="first-person open-thigh aftermath frame with her face and torso visible behind the wet detail",
        axis_values={"position": "reclining with thighs open"},
    ).lower()
    _expect(
        "pov post-ejaculation open-thigh display" in open_thigh_phrase,
        f"POV open-thigh climax should use aftermath display wording: {open_thigh_phrase}",
    )
    _expect(
        "wet aftermath detail is the exact center" in open_thigh_phrase,
        f"POV open-thigh climax lost centered wet aftermath hierarchy: {open_thigh_phrase}",
    )
    _expect(
        "missionary position" not in open_thigh_phrase and "penetrative sex position" not in open_thigh_phrase,
        f"POV open-thigh climax should not read as active penetration: {open_thigh_phrase}",
    )


def smoke_pov_penetration_position_routes() -> None:
    cases = [
        (
            "pov_penetration_missionary",
            "missionary",
            ("woman a lies on her back", "man a is above her between her thighs"),
            ("pov missionary position", "viewer is above her", "penetrates her pussy"),
        ),
        (
            "pov_penetration_missionary_folded",
            "missionary_folded",
            ("woman a lies on her back", "knees folded high toward her chest", "penis thrusts into her pussy"),
            (
                "pov folded missionary high-leg penetration position",
                "large centered shaft rising from the lower center",
                "compact knee block above the contact",
                "penetrates her pussy",
            ),
        ),
        (
            "pov_penetration_cowgirl",
            "cowgirl",
            (
                "woman a straddles man a's hips facing him",
                "man a lies under her",
                "wide horizontal thigh bridge",
                "man a's hands grip the sides of her thighs",
            ),
            (
                "pov frontal cowgirl wide-thigh bridge position",
                "viewer reclines underneath her",
                "wide horizontal thigh bridge",
                "knees planted outside the viewer's hips",
                "viewer hands grip the sides of her thighs",
                "centered contact remains below her belly",
            ),
        ),
        (
            "pov_penetration_cowgirl_alt",
            "cowgirl_alt",
            ("woman a faces man a in a low seated squat", "man a lies flat on his back", "man a supports the underside of her thighs"),
            (
                "pov low cowgirl seated-squat penetration position",
                "viewer lies flat on his back",
                "lens sits low at the viewer's abdomen",
                "high room background behind her upper body",
                "viewer supports the underside of her thighs",
                "knees bent wide and close to the camera",
                "penetrates her pussy",
            ),
        ),
        (
            "pov_penetration_reverse_cowgirl",
            "reverse_cowgirl",
            ("woman a straddles man a's hips facing away", "man a lies under her"),
            (
                "pov reverse cowgirl position",
                "facing away",
                "viewer lies on his back",
                "back, hips, and ass are the nearest largest shapes",
                "viewer thighs frame the lower corners",
                "centered contact sits directly between her thighs below her ass",
            ),
        ),
        (
            "pov_penetration_reverse_cowgirl_alt",
            "reverse_cowgirl_alt",
            ("woman a sits upright facing away", "man a lies under her", "man a's penis thrusts into her pussy"),
            (
                "pov upright reverse cowgirl back-facing penetration position",
                "back stays vertical and readable",
                "viewer hands hold her hips",
                "viewer thighs frame the lower corners",
                "centered contact remains visible below her ass",
            ),
        ),
        (
            "pov_penetration_doggy",
            "doggy",
            ("woman a is on all fours", "man a is positioned behind her"),
            ("top-down pov doggy position", "on all fours", "forearms folded", "penetrates her pussy"),
        ),
        (
            "pov_penetration_edge_supported",
            "edge_supported",
            ("raised edge", "man a kneels between her thighs"),
            ("pov elevated-edge missionary position", "flat elevated support", "hands holding her calves or outer thighs", "penetrates her pussy"),
        ),
        (
            "pov_penetration_lotus",
            "lotus_lap",
            ("woman a sits in man a's lap", "legs around his hips"),
            ("pov lotus position", "woman sits in his lap", "penetrates her pussy"),
        ),
    ]
    for offset, (name, position_key, role_terms, krea_terms) in enumerate(cases, start=3801):
        pair = pb.build_insta_of_pair(
            row_number=1,
            start_index=1,
            seed=offset,
            ethnicity="any",
            figure="random",
            no_plus_women=False,
            no_black=False,
            trigger=Trigger,
            prepend_trigger_to_prompt=True,
            options_json=_insta_options(
                softcore_camera_mode="from_camera_config",
                hardcore_camera_mode="from_camera_config",
                camera_detail="compact",
            ),
            character_cast=_character_cast(pov_man=True),
            hardcore_position_config=_position_filter("penetration_only", "penetrative", [position_key]),
            location_config=_coworking_location_config(),
            hardcore_camera_config=_orbit_camera(
                horizontal_angle=45,
                vertical_angle=0,
                zoom=7.5,
                subject_focus="action",
            ),
        )
        _expect_pair(pair, name)
        hard_row = pair.get("hardcore_row") or {}
        _expect(hard_row.get("action_family") == "penetration", f"{name} action_family should be penetration")
        _expect(hard_row.get("position_family") == "penetrative", f"{name} position_family should be penetrative")
        _expect(position_key in (hard_row.get("position_keys") or []), f"{name} lost position key {position_key!r}")
        role_graph = _expect_text(f"{name}.source_role_graph", hard_row.get("source_role_graph"), 40).lower()
        for term in role_terms:
            _expect(term in role_graph, f"{name} role graph missing {term!r}: {role_graph}")
        krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
        prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 60).lower()
        _expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
        _expect("viewer" in prompt and "pov" in prompt, f"{name} Krea prompt lost POV wording")
        _expect("camera:" not in krea.get("krea_prompt", ""), f"{name} Krea prompt emitted normal third-person camera directive")
        for term in krea_terms:
            _expect(term in prompt, f"{name} Krea prompt missing {term!r}: {prompt}")


def smoke_pov_anal_position_routes() -> None:
    cases = [
        (
            "pov_anal_doggy",
            "doggy",
            ("on all fours", "positioned behind her"),
            ("top-down pov doggy position", "on all fours", "forearms folded", "penetrates her ass"),
        ),
        (
            "pov_anal_bent_over",
            "bent_over",
            ("bent forward", "stands behind her"),
            ("bent forward at the waist", "penetrates her ass"),
        ),
        (
            "pov_anal_face_down",
            "face_down_ass_up",
            ("lies face-down", "ass raised"),
            ("lying face-down", "penetrates her ass"),
        ),
        (
            "pov_anal_standing",
            "standing",
            ("stands braced", "stands behind her"),
            ("pov standing rear-entry position", "viewer stands behind her"),
        ),
        (
            "pov_anal_side_lying",
            "side_lying",
            ("lies on her side", "presses behind her"),
            ("pov side-lying sex position", "viewer is behind her"),
        ),
        (
            "pov_anal_edge_supported",
            "edge_supported",
            ("raised edge", "kneels behind her"),
            ("pov raised-edge penetration position", "viewer kneels between her legs"),
        ),
        (
            "pov_anal_kneeling",
            "kneeling",
            ("kneels forward", "kneels behind her"),
            ("pov kneeling rear-entry position", "viewer kneels behind her"),
        ),
    ]
    for offset, (name, position_key, role_terms, krea_terms) in enumerate(cases, start=3901):
        pair = pb.build_insta_of_pair(
            row_number=1,
            start_index=1,
            seed=offset,
            ethnicity="any",
            figure="random",
            no_plus_women=False,
            no_black=False,
            trigger=Trigger,
            prepend_trigger_to_prompt=True,
            options_json=_insta_options(
                softcore_camera_mode="from_camera_config",
                hardcore_camera_mode="from_camera_config",
                camera_detail="compact",
            ),
            character_cast=_character_cast(pov_man=True),
            hardcore_position_config=_position_filter("anal_only", "anal", [position_key]),
            location_config=_coworking_location_config(),
            hardcore_camera_config=_orbit_camera(
                horizontal_angle=45,
                vertical_angle=0,
                zoom=7.5,
                subject_focus="action",
            ),
        )
        _expect_pair(pair, name)
        hard_row = pair.get("hardcore_row") or {}
        _expect(hard_row.get("position_family") == "anal", f"{name} position_family should be anal")
        _expect(hard_row.get("action_family") == "anal", f"{name} action_family should be anal")
        _expect(position_key in (hard_row.get("position_keys") or []), f"{name} lost position key {position_key!r}")
        role_graph = _expect_text(f"{name}.source_role_graph", hard_row.get("source_role_graph"), 40).lower()
        for term in role_terms:
            _expect(term in role_graph, f"{name} role graph missing {term!r}: {role_graph}")
        krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
        prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 60).lower()
        _expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
        _expect("viewer" in prompt and "first-person" in prompt, f"{name} Krea prompt lost POV wording")
        _expect("camera:" not in krea.get("krea_prompt", ""), f"{name} Krea prompt emitted normal third-person camera directive")
        for term in krea_terms:
            _expect(term in prompt, f"{name} Krea prompt missing {term!r}: {prompt}")


def smoke_double_front_back_route() -> None:
    pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=3911,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(
            hardcore_cast="mixed_group",
            hardcore_men_count=2,
            softcore_camera_mode="from_camera_config",
            hardcore_camera_mode="from_camera_config",
            camera_detail="compact",
        ),
        character_cast=_character_cast_two_men(),
        hardcore_position_config=_anal_double_filter(["front_back"]),
        location_config=_coworking_location_config(),
        hardcore_camera_config=_orbit_camera(
            horizontal_angle=45,
            vertical_angle=0,
            zoom=7.5,
            subject_focus="action",
        ),
    )
    _expect_pair(pair, "double_front_back_route")
    hard_row = pair.get("hardcore_row") or {}
    _expect(hard_row.get("position_family") == "anal", "double route position_family should be anal")
    _expect(hard_row.get("action_family") == "toy_double", "double route action_family should stay toy_double")
    _expect("front_back" in (hard_row.get("position_keys") or []), "double route lost front_back key")
    role_graph = _expect_text("double_front_back_route.source_role_graph", hard_row.get("source_role_graph"), 40).lower()
    _expect("second penetration point from the front" in role_graph, f"double route role graph lost front/back placement: {role_graph}")
    krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(pair), target="hardcore")
    prompt = _expect_text("double_front_back_route.krea_prompt", krea.get("krea_prompt"), 60).lower()
    _expect("metadata" in krea.get("method", ""), "double route Krea did not use metadata")
    _expect("front-and-back" in prompt, "double route Krea lost front/back position wording")
    _expect("second penetration point" in prompt, "double route Krea lost second-contact wording")
    _expect("role graph:" not in prompt and "sexual scene:" not in prompt, "double route Krea leaked raw labels")


def smoke_climax_position_routes() -> None:
    cases = [
        (
            "climax_face_down",
            "face_down_ass_up",
            4001,
            _character_cast(),
            1,
            1,
            ("lies face-down", "lower back and ass"),
            ("face-down", "lower back and ass"),
        ),
        (
            "climax_side_lying",
            "side_lying",
            4042,
            _character_cast(),
            1,
            1,
            ("lies on her side", "thighs and pussy"),
            ("lies on her side", "thighs and pussy"),
        ),
        (
            "climax_lotus_lap",
            "lotus_lap",
            4001,
            _character_cast(),
            1,
            1,
            ("sits in man a's lap", "legs wrapped"),
            ("sits in the man's lap", "legs wrapped"),
        ),
        (
            "climax_open_thighs",
            "open_thighs",
            4001,
            _character_cast(),
            1,
            1,
            ("lies on her back", "thighs open"),
            ("lies on her back", "thighs open"),
        ),
        (
            "climax_front_back",
            "front_back",
            4090,
            _character_cast_two_men(),
            1,
            2,
            ("lies between man a and man b", "man a under her hips"),
            ("lies between man a and man b", "visible semen lands"),
        ),
    ]
    for name, position_key, seed, cast, women_count, men_count, role_terms, krea_terms in cases:
        row = _prompt_row(
            name=name,
            category="Hardcore sexual poses",
            subcategory="Cumshot and climax",
            seed=seed,
            character_cast=cast,
            women_count=women_count,
            men_count=men_count,
            hardcore_position_config=_position_filter("climax_only", "climax", [position_key]),
        )
        _expect_custom_row(row, name)
        _expect(row.get("action_family") == "climax", f"{name} action_family should be climax")
        _expect(row.get("position_family") == "climax", f"{name} position_family should be climax")
        _expect(position_key in (row.get("position_keys") or []), f"{name} lost position key {position_key!r}")
        role_graph = _expect_text(f"{name}.source_role_graph", row.get("source_role_graph"), 40).lower()
        for term in role_terms:
            _expect(term in role_graph, f"{name} role graph missing {term!r}: {role_graph}")
        krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(row), target="single")
        prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 60).lower()
        _expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
        _expect("role graph:" not in prompt and "sexual scene:" not in prompt, f"{name} Krea leaked raw labels")
        for term in krea_terms:
            _expect(term in prompt, f"{name} Krea prompt missing {term!r}: {prompt}")
        if position_key == "side_lying":
            _expect("lower back and ass" not in prompt, f"{name} Krea kept conflicting rear-entry fluid location: {prompt}")
        _expect_formatter_outputs(row, name, target="single")


def smoke_interaction_role_graph_routes() -> None:
    cases = [
        (
            "interaction_manual",
            "Manual stimulation",
            "manual_only",
            "manual",
            "fingering",
            4301,
            _character_cast(),
            1,
            1,
            ("foreground hand is the largest lower-frame object", "open thighs form a v", "wrist enters from the bottom center"),
            ("foreground hand is the largest lower-frame object", "two fingers at her vulva and clit"),
        ),
        (
            "interaction_clothing_transition",
            "Clothing and position transitions",
            "interaction_only",
            "interaction",
            "position_transition",
            4302,
            _character_cast(),
            1,
            1,
            ("mid-transition", "moving clothing aside"),
            ("mid-transition", "guiding the woman's hips"),
        ),
        (
            "interaction_body_worship",
            "Body worship and touching",
            "interaction_only",
            "interaction",
            "body_worship",
            4301,
            _character_cast(),
            1,
            1,
            ("kisses down her body", "hands tracing"),
            ("kisses down her body", "hands tracing"),
        ),
        (
            "interaction_camera_performance",
            "Camera performance",
            "interaction_only",
            "interaction",
            "camera_showing",
            4301,
            _character_cast(),
            1,
            1,
            ("faces the camera", "creator-shot reveal"),
            ("faces the camera", "creator-shot reveal"),
        ),
        (
            "interaction_aftercare",
            "Aftercare and cleanup",
            "interaction_only",
            "interaction",
            "aftercare",
            4301,
            _character_cast(),
            1,
            1,
            ("lie close together after sex", "post-sex cuddle"),
            ("lie close together after sex", "post-sex cuddle"),
        ),
        (
            "interaction_group_coordination",
            "Group coordination",
            "interaction_only",
            "interaction",
            "watching",
            4301,
            _character_cast_two_men(),
            1,
            2,
            ("is centered", "hold and present"),
            ("is centered", "each role clearly visible"),
        ),
    ]
    for name, subcategory, focus, family, position_key, seed, cast, women_count, men_count, role_terms, krea_terms in cases:
        row = _prompt_row(
            name=name,
            category="Hardcore sexual poses",
            subcategory=subcategory,
            seed=seed,
            character_cast=cast,
            women_count=women_count,
            men_count=men_count,
            hardcore_position_config=_position_filter(focus, family, [position_key]),
        )
        _expect_custom_row(row, name)
        expected_action_family = "manual" if family == "manual" else "foreplay"
        _expect(
            row.get("action_family") == expected_action_family,
            f"{name} action_family mismatch: {row.get('action_family')} != {expected_action_family}",
        )
        _expect(row.get("position_family") == family, f"{name} position_family mismatch: {row.get('position_family')}")
        _expect(position_key in (row.get("position_keys") or []), f"{name} lost position key {position_key!r}")
        role_graph = _expect_text(f"{name}.source_role_graph", row.get("source_role_graph"), 40).lower()
        for term in role_terms:
            _expect(term in role_graph, f"{name} role graph missing {term!r}: {role_graph}")
        krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(row), target="single")
        prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 60).lower()
        _expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
        _expect("role graph:" not in prompt and "sexual scene:" not in prompt, f"{name} Krea leaked raw labels")
        for term in krea_terms:
            _expect(term in prompt, f"{name} Krea prompt missing {term!r}: {prompt}")
        _expect_formatter_outputs(row, name, target="single")

    wand_role_graph = hardcore_role_interaction.build_manual_role_graph(
        "Woman A",
        "Man A",
        item_text="toy-assisted manual stimulation with one hand controlling the toy",
        item_axis_values={
            "position": "reclining open-thigh manual position",
            "manual_act": "partner-held vibrator pressed to the clit",
            "manual_detail": "toy and fingers both visible at the contact point",
        },
    )
    wand_role_lower = wand_role_graph.lower()
    for term in (
        "single continuous teal wand-style massager",
        "rounded bulb head presses flat",
        "smooth handle angles in from the bottom right",
        "open thighs and knees form a v",
    ):
        _expect(term in wand_role_lower, f"manual wand role graph missing {term!r}: {wand_role_lower}")
    wand_row = _fixture_hardcore_row(
        subcategory="Manual stimulation",
        subcategory_slug="manual_stimulation",
        item="toy-assisted manual stimulation with one hand controlling the toy, partner-held vibrator pressed to the clit",
        custom_item="Manual stimulation",
        item_axis_values={
            "position": "reclining open-thigh manual position",
            "manual_act": "partner-held vibrator pressed to the clit",
            "manual_detail": "toy and fingers both visible at the contact point",
        },
        scene_text=(
            "coworking lounge with tall windows, warm desks, laptop tables, "
            "glass partition seams, repeated desk rows, plants, and soft shared-office depth"
        ),
        composition="close first-person office-chair toy-contact frame",
        source_composition="close first-person office-chair toy-contact frame",
        role_graph=wand_role_graph,
        source_role_graph=wand_role_graph,
        action_family="manual",
        position_family="manual",
        position_key="wand",
        position_keys=["wand", "toy_contact", "open_thighs"],
    )
    wand_krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(wand_row), target="single")
    wand_prompt = _expect_text("interaction_wand_toy_contact.krea_prompt", wand_krea.get("krea_prompt"), 80).lower()
    for term in (
        "single continuous teal wand-style massager",
        "rounded bulb head presses flat",
        "smooth handle angles in from the bottom right",
        "open thighs and knees form a v",
        "face and torso remain visible behind",
    ):
        _expect(term in wand_prompt, f"manual wand Krea prompt missing {term!r}: {wand_prompt}")

    generated_wand_row = _prompt_row(
        name="interaction_generated_wand_toy_contact",
        category="Hardcore sexual poses",
        subcategory="Manual stimulation",
        seed=46,
        character_cast=_character_cast(pov_man=True),
        women_count=1,
        men_count=1,
        hardcore_position_config=_position_filter("manual_only", "manual", ["fingering"]),
        location_config=_coworking_location_config(),
    )
    generated_wand_krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(generated_wand_row), target="single")
    generated_wand_prompt = _expect_text(
        "interaction_generated_wand_toy_contact.krea_prompt",
        generated_wand_krea.get("krea_prompt"),
        80,
    ).lower()
    for term in (
        "single continuous teal wand-style massager",
        "rounded bulb head presses flat",
        "smooth handle angles in from the bottom right",
        "open thighs and knees form a v",
    ):
        _expect(term in generated_wand_prompt, f"generated manual wand Krea prompt missing {term!r}: {generated_wand_prompt}")

    spread_role_graph = hardcore_role_interaction.build_interaction_role_graph(
        "Woman A",
        "Man A",
        slug="camera_performance",
        item_text="spread open for camera in a reclining camera-presentation position",
        item_axis_values={
            "position": "reclining camera-presentation position",
            "performance_act": "spread open for camera",
            "presentation_detail": "open thighs and knees held wide",
            "hand_detail": "hands holding knees",
        },
    )
    spread_role_lower = spread_role_graph.lower()
    for term in ("knees raised", "broad v-frame", "hands hold her knees", "face and torso remain visible"):
        _expect(term in spread_role_lower, f"Spread presentation role graph missing {term!r}: {spread_role_lower}")
    spread_row = _fixture_hardcore_row(
        subcategory="Camera performance",
        subcategory_slug="camera_performance",
        item="spread open for camera in a reclining camera-presentation position, open thighs and knees held wide, hands holding knees",
        custom_item="Camera performance",
        item_axis_values={
            "position": "reclining camera-presentation position",
            "performance_act": "spread open for camera",
            "presentation_detail": "open thighs and knees held wide",
            "hand_detail": "hands holding knees",
        },
        scene_text=(
            "coworking lounge with tall windows, warm desks, laptop tables, "
            "glass partition seams, repeated desk rows, plants, and soft shared-office depth"
        ),
        composition="close seated camera frame in a coworking lounge",
        source_composition="close seated camera frame in a coworking lounge",
        role_graph=spread_role_graph,
        source_role_graph=spread_role_graph,
        action_family="foreplay",
        position_family="interaction",
        position_key="open_thighs",
        position_keys=["camera_showing", "open_thighs"],
    )
    _expect_custom_row(spread_row, "interaction_spread_open_thigh_presentation")
    _expect(spread_row.get("position_family") == "interaction", "Spread presentation position_family should be interaction")
    _expect("camera_showing" in (spread_row.get("position_keys") or []), "Spread presentation lost camera_showing key")
    _expect("open_thighs" in (spread_row.get("position_keys") or []), "Spread presentation lost open_thighs key")
    spread_krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(spread_row), target="single")
    spread_prompt = _expect_text("interaction_spread_open_thigh_presentation.krea_prompt", spread_krea.get("krea_prompt"), 80).lower()
    _expect("metadata" in spread_krea.get("method", ""), "Spread presentation Krea did not use metadata")
    for term in ("knees raised", "broad v-frame", "hands hold her knees", "office chair seat and chair arms"):
        _expect(term in spread_prompt, f"Spread presentation Krea prompt missing {term!r}: {spread_prompt}")

def smoke_fallback_role_graph_routes() -> None:
    cases = [
        (
            "fallback_solo_woman_manual",
            ("woman",),
            "Manual stimulation",
            "manual_only",
            "manual",
            "fingering",
            4401,
            1,
            0,
            ("reclines with thighs open", "one hand between her legs"),
            ("one hand between her legs", "fingers visibly stimulating"),
        ),
        (
            "fallback_solo_man_climax",
            ("man",),
            "Cumshot and climax",
            "climax_only",
            "climax",
            "open_thighs",
            4401,
            0,
            1,
            ("solo visible ejaculation pose", "semen visible"),
            ("solo visible ejaculation pose", "visible semen on skin"),
        ),
        (
            "fallback_women_only_oral",
            ("woman", "woman"),
            "Oral sex",
            "oral_only",
            "oral",
            "reclining_oral",
            4401,
            2,
            0,
            ("woman a kneels between woman b", "uses tongue and fingers"),
            ("woman a kneels between woman b", "uses tongue and fingers"),
        ),
        (
            "fallback_women_only_threesome",
            ("woman", "woman", "woman"),
            "Threesomes",
            "threesome_only",
            "threesome",
            "front_back",
            4401,
            3,
            0,
            ("uses a strap-on", "gives oral contact"),
            ("uses a strap-on", "gives oral contact"),
        ),
        (
            "fallback_men_only_oral",
            ("man", "man"),
            "Oral sex",
            "oral_only",
            "oral",
            "kneeling",
            4401,
            0,
            2,
            ("man a kneels", "takes man b's penis"),
            ("man a kneels", "takes man b's penis"),
        ),
        (
            "fallback_men_only_threesome",
            ("man", "man", "man"),
            "Threesomes",
            "threesome_only",
            "threesome",
            "front_back",
            4401,
            0,
            3,
            ("penetrates man b anally", "gives oral contact"),
            ("penetrates man b anally", "gives oral contact"),
        ),
        (
            "fallback_mixed_threesome",
            ("woman", "man", "man"),
            "Threesomes",
            "threesome_only",
            "threesome",
            "front_back",
            4401,
            1,
            2,
            ("thrusts his penis into woman a", "uses mouth and hands"),
            ("thrusts his penis into woman a", "uses mouth and hands"),
        ),
    ]
    for name, subjects, subcategory, focus, family, position_key, seed, women_count, men_count, role_terms, krea_terms in cases:
        row = _prompt_row(
            name=name,
            category="Hardcore sexual poses",
            subcategory=subcategory,
            seed=seed,
            character_cast=_character_cast_subjects(subjects),
            women_count=women_count,
            men_count=men_count,
            hardcore_position_config=_position_filter(focus, family, [position_key]),
        )
        _expect_custom_row(row, name)
        _expect(row.get("position_family") == family, f"{name} position_family mismatch: {row.get('position_family')}")
        if family == "threesome":
            _expect(row.get("action_family") == "threesome", f"{name} action_family should be threesome")
        _expect(position_key in (row.get("position_keys") or []), f"{name} lost position key {position_key!r}")
        role_graph = _expect_text(f"{name}.source_role_graph", row.get("source_role_graph"), 30).lower()
        for term in role_terms:
            _expect(term in role_graph, f"{name} role graph missing {term!r}: {role_graph}")
        krea = krea_formatter.format_krea2_prompt("", metadata_json=_json(row), target="single")
        prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 60).lower()
        _expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
        _expect("role graph:" not in prompt and "sexual scene:" not in prompt, f"{name} Krea leaked raw labels")
        for term in krea_terms:
            _expect(term in prompt, f"{name} Krea prompt missing {term!r}: {prompt}")
        if name == "fallback_solo_man_climax":
            _expect("lower back and ass" not in prompt, f"{name} Krea kept conflicting solo male climax detail: {prompt}")
        _expect_formatter_outputs(row, name, target="single")


def smoke_no_expression_fallback() -> None:
    cast = pb.build_character_slot_json(
        subject_type="woman",
        label="A",
        age="25-year-old adult",
        ethnicity="western_european",
        body="slim",
        descriptor_detail="full",
        expression_enabled=False,
    )["character_cast"]
    row = _prompt_row(
        name="hardcore_expression_disabled",
        category="Hardcore sexual poses",
        subcategory="Penetrative sex",
        seed=2301,
        character_cast=cast,
        women_count=1,
        men_count=1,
        hardcore_position_config=_action_filter("penetration_only"),
    )
    _expect_custom_row(row, "hardcore_expression_disabled")
    _expect(not row.get("expression"), "expression should stay disabled without fallback")
    _expect("Facial expressions:" not in row.get("prompt", ""), "disabled expression leaked into prompt")
    _expect_formatter_outputs(row, "hardcore_expression_disabled", target="single")


def smoke_formatter_metadata_fixtures() -> None:
    cases = [
        {
            "name": "fixture_penetration_text_noise",
            "row": _fixture_hardcore_row(),
            "krea_terms": ("penis thrusts",),
            "sdxl_terms": ("penetrative sex", "missionary"),
            "caption_terms": ("penetrative action",),
        },
        {
            "name": "fixture_manual_source_family",
            "row": _fixture_hardcore_row(
                subcategory="Manual stimulation",
                subcategory_slug="manual_stimulation",
                item="wet fingers moving between the thighs, one hand braced on the hip, wet shine on fingers and inner thighs",
                custom_item="Manual stimulation",
                item_axis_values={
                    "position": "kneeling hand-between-thighs position",
                    "manual_act": "wet fingers moving between the thighs",
                },
                scene_text=(
                    "coworking lounge with tall windows, warm desks, laptop tables, "
                    "glass partition seams, repeated desk rows, plants, and soft shared-office depth"
                ),
                composition="close crop on hands and face in a coworking lounge",
                source_composition="close crop on hands and face in a coworking lounge",
                role_graph=(
                    "Woman A reclines with thighs open while Man A's foreground hand is the largest lower-frame object; "
                    "her open thighs form a V around the hand, the wrist enters from the bottom center, "
                    "and two fingers at her vulva and clit make the central manual-contact point while her face and torso remain visible behind the open thighs."
                ),
                source_role_graph=(
                    "Woman A reclines with thighs open while Man A's foreground hand is the largest lower-frame object; "
                    "her open thighs form a V around the hand, the wrist enters from the bottom center, "
                    "and two fingers at her vulva and clit make the central manual-contact point while her face and torso remain visible behind the open thighs."
                ),
                action_family="manual",
                position_family="manual",
                position_key="fingering",
                position_keys=["fingering", "open_thighs"],
            ),
            "krea_terms": ("foreground hand is the largest lower-frame object", "office chair seat and chair arms"),
            "sdxl_terms": ("manual stimulation", "fingering"),
            "caption_terms": ("manual action",),
        },
        {
            "name": "fixture_climax_family",
            "row": _fixture_hardcore_row(
                subcategory="Cumshot and climax",
                subcategory_slug="cumshot_climax",
                item="external cumshot with cum on lower back and ass, explicit semen aftermath visible",
                custom_item="Cumshot and climax",
                item_axis_values={"position": "on all fours with hips raised"},
                composition="low-angle post-orgasm frame",
                source_composition="low-angle post-orgasm frame",
                role_graph=(
                    "Woman A is on all fours with hips raised while Man A is positioned behind her "
                    "and ejaculates semen across her ass, thighs, and lower back."
                ),
                source_role_graph=(
                    "Woman A is on all fours with hips raised while Man A is positioned behind her "
                    "and ejaculates semen across her ass, thighs, and lower back."
                ),
                action_family="climax",
                position_family="climax",
                position_key="doggy",
                position_keys=["doggy"],
            ),
            "krea_terms": ("ejaculates semen",),
            "sdxl_terms": ("climax", "semen"),
            "caption_terms": ("climax action",),
        },
        {
            "name": "fixture_climax_open_thigh_aftermath",
            "row": _fixture_hardcore_row(
                subcategory="Cumshot and climax",
                subcategory_slug="cumshot_climax",
                item=(
                    "post-ejaculation open-thigh display with thick semen and clear fluid "
                    "around the exposed opening, body still after ejaculation"
                ),
                custom_item="Cumshot and climax",
                item_axis_values={"position": "reclining with thighs open"},
                scene_text=(
                    "coworking lounge with tall windows, warm desks, laptop tables, "
                    "glass partition seams, repeated desk rows, plants, and soft shared-office depth"
                ),
                composition="first-person open-thigh aftermath frame in a coworking lounge",
                source_composition="first-person open-thigh aftermath frame in a coworking lounge",
                role_graph=(
                    "Woman A lies on her back with thighs open toward Man A after ejaculation, "
                    "with semen and clear fluid covering the exposed pussy and inner thighs."
                ),
                source_role_graph=(
                    "Woman A lies on her back with thighs open toward Man A after ejaculation, "
                    "with semen and clear fluid covering the exposed pussy and inner thighs."
                ),
                action_family="climax",
                position_family="climax",
                position_key="open_thighs",
                position_keys=["open_thighs"],
                pov_character_labels=["Man A"],
            ),
            "krea_terms": (
                "pov post-ejaculation open-thigh display",
                "wet aftermath detail is the exact center",
                "office chair seat and chair arms",
            ),
            "sdxl_terms": ("climax", "semen"),
            "caption_terms": ("climax action",),
        },
    ]
    for case in cases:
        name = case["name"]
        row = case["row"]
        _expect_custom_row(row, name)
        metadata = _json(row)

        krea = krea_formatter.format_krea2_prompt("", metadata_json=metadata, target="single")
        krea_prompt = _expect_text(f"{name}.krea_prompt", krea.get("krea_prompt"), 40).lower()
        _expect("metadata" in krea.get("method", ""), f"{name}.krea did not use metadata")
        _expect("role graph:" not in krea_prompt and "sexual pose:" not in krea_prompt, f"{name}.krea leaked raw labels")
        for term in case["krea_terms"]:
            _expect(term in krea_prompt, f"{name}.krea missing {term!r}")

        sdxl = sdxl_formatter.format_sdxl_prompt("", metadata_json=metadata, target="single", trigger=SdxlTrigger, prepend_trigger=True)
        sdxl_prompt = _expect_text(f"{name}.sdxl_prompt", sdxl.get("sdxl_prompt"), 40).lower()
        _expect("metadata" in sdxl.get("method", ""), f"{name}.sdxl did not use metadata")
        for term in case["sdxl_terms"]:
            _expect(term in sdxl_prompt, f"{name}.sdxl missing {term!r}")

        caption, method = caption_naturalizer.naturalize_caption("", metadata_json=metadata, trigger=Trigger, include_trigger=True)
        caption_text = _expect_text(f"{name}.caption", caption, 40).lower()
        _expect("metadata" in method, f"{name}.caption did not use metadata")
        for term in case["caption_terms"]:
            _expect(term in caption_text, f"{name}.caption missing {term!r}")

    route_row = _fixture_hardcore_row(
        formatter_hints={
            "all": ["shared route anchor"],
            "krea": ["krea readable anchor"],
            "sdxl": ["sdxl route tag"],
            "caption": ["caption route phrase"],
        }
    )
    _expect_custom_row(route_row, "fixture_formatter_hints")
    metadata = _json(route_row)

    krea = krea_formatter.format_krea2_prompt("", metadata_json=metadata, target="single")
    krea_prompt = _expect_text("fixture_formatter_hints.krea_prompt", krea.get("krea_prompt"), 40).lower()
    _expect("shared route anchor" in krea_prompt, "Krea formatter missed shared formatter hint")
    _expect("krea readable anchor" in krea_prompt, "Krea formatter missed Krea formatter hint")
    _expect("sdxl route tag" not in krea_prompt, "Krea formatter leaked SDXL formatter hint")
    _expect("caption route phrase" not in krea_prompt, "Krea formatter leaked caption formatter hint")

    sdxl = sdxl_formatter.format_sdxl_prompt("", metadata_json=metadata, target="single", trigger=SdxlTrigger, prepend_trigger=True)
    sdxl_prompt = _expect_text("fixture_formatter_hints.sdxl_prompt", sdxl.get("sdxl_prompt"), 40).lower()
    _expect("shared route anchor" in sdxl_prompt, "SDXL formatter missed shared formatter hint")
    _expect("sdxl route tag" in sdxl_prompt, "SDXL formatter missed SDXL formatter hint")
    _expect("krea readable anchor" not in sdxl_prompt, "SDXL formatter leaked Krea formatter hint")
    _expect("caption route phrase" not in sdxl_prompt, "SDXL formatter leaked caption formatter hint")

    caption, method = caption_naturalizer.naturalize_caption("", metadata_json=metadata, trigger=Trigger, include_trigger=True)
    caption_text = _expect_text("fixture_formatter_hints.caption", caption, 40).lower()
    _expect("metadata" in method, "Caption formatter hints fixture did not use metadata")
    _expect("shared route anchor" in caption_text, "Caption naturalizer missed shared formatter hint")
    _expect("caption route phrase" in caption_text, "Caption naturalizer missed caption formatter hint")
    _expect("krea readable anchor" not in caption_text, "Caption naturalizer leaked Krea formatter hint")
    _expect("sdxl route tag" not in caption_text, "Caption naturalizer leaked SDXL formatter hint")

    external_pair = {
        "trigger": Trigger,
        "shared_descriptor": "25-year-old adult woman, slim figure, fair skin, blonde hair, blue eyes",
        "shared_cast_descriptors": [
            "Woman A: 25-year-old adult woman, slim figure, fair skin, blonde hair, blue eyes",
            "Man A: 40-year-old adult man, average figure, tan skin, dark hair",
        ],
        "options": {
            "softcore_cast": "same_as_hardcore",
            "continuity": "same_creator_same_room",
            "softcore_level": "lingerie_tease",
            "hardcore_level": "hardcore",
        },
        "softcore_row": {
            "prompt": f"{Trigger}, embedded soft pair prompt.",
            "caption": f"{Trigger}, embedded soft pair caption.",
            "negative_prompt": "bad hands, bad hands",
            "subject_type": "configured_cast",
            "primary_subject": "woman",
            "item": "red satin lingerie set",
            "softcore_item_prompt_label": "Erotic outfit",
            "pose": "standing together beside a mirror",
            "scene_text": "private studio room with warm light",
            "composition": "mirror teaser composition",
            "expression": "confident soft smile",
            "softcore_partner_styling": {
                "outfits": ["Man A wears a black buttoned shirt and dark trousers"],
                "pose": "standing close with one hand at the waist",
            },
            "camera_config": {
                "camera_mode": "standard",
                "custom_camera_prompt": "front-right quarter view, eye-level shot, medium shot",
            },
            "camera_directive": "Camera: row soft front-right view.",
            "camera_scene_directive": "Row soft mirror camera layout.",
        },
        "hardcore_row": {
            "prompt": f"{Trigger}, embedded hard pair prompt.",
            "caption": f"{Trigger}, embedded hard pair caption.",
            "negative_prompt": "low quality, low quality",
            "subject_type": "configured_cast",
            "primary_subject": "configured_cast",
            "item": "missionary position while full-body penetrative sex",
            "custom_item": "Penetrative sex",
            "item_label": "Sexual pose",
            "item_axis_values": {
                "position": "missionary position",
                "penetration_act": "full-body penetrative sex",
            },
            "scene_text": "private studio room with warm light",
            "composition": "front-facing full-body frame",
            "source_composition": "front-facing full-body frame",
            "role_graph": (
                "Woman A lies on her back with legs open around Man A's hips while Man A is above her "
                "between her thighs; Man A's penis thrusts into her pussy."
            ),
            "source_role_graph": (
                "Woman A lies on her back with legs open around Man A's hips while Man A is above her "
                "between her thighs; Man A's penis thrusts into her pussy."
            ),
            "expression": "focused adult expression",
            "action_family": "penetration",
            "position_family": "penetrative",
            "position_key": "missionary",
            "position_keys": ["missionary"],
            "hardcore_clothing_state": "Woman A's red satin lingerie set is pushed aside at the hips",
            "camera_config": {
                "camera_mode": "standard",
                "custom_camera_prompt": "right side view, eye-level shot, medium shot",
            },
            "camera_directive": "Camera: row hard right-side view.",
            "camera_scene_directive": "Row hard body-aligned camera layout.",
        },
    }
    external_metadata = _json(external_pair)

    krea_pair = krea_formatter.format_krea2_prompt("", metadata_json=external_metadata, target="auto")
    krea_soft = _expect_text("fixture_external_pair.krea_soft", krea_pair.get("krea_softcore_prompt"), 40).lower()
    krea_hard = _expect_text("fixture_external_pair.krea_hard", krea_pair.get("krea_hardcore_prompt"), 40).lower()
    _expect(krea_pair.get("method") == "metadata_json:krea2(insta_of_pair)", "External pair Krea route changed method")
    _expect("black buttoned shirt" in krea_soft, "External pair Krea soft route lost embedded partner styling")
    _expect_no_softcore_noise("fixture_external_pair.krea_soft", krea_pair.get("krea_softcore_prompt"))
    _expect("styled creator-teaser frame" in krea_soft, "External pair Krea soft route lost clean cast-presence wording")
    _expect("red satin lingerie set" in krea_hard, "External pair Krea hard route lost embedded clothing state")
    _expect("row hard right-side view" in krea_hard, "External pair Krea hard route lost embedded camera directive")
    _expect_no_duplicate_comma_items("fixture_external_pair.krea_negative", krea_pair.get("negative_prompt"))

    sdxl_pair = sdxl_formatter.format_sdxl_prompt(
        "",
        metadata_json=external_metadata,
        target="auto",
        trigger=SdxlTrigger,
        prepend_trigger=True,
    )
    sdxl_soft = _expect_text("fixture_external_pair.sdxl_soft", sdxl_pair.get("sdxl_softcore_prompt"), 40).lower()
    sdxl_hard = _expect_text("fixture_external_pair.sdxl_hard", sdxl_pair.get("sdxl_hardcore_prompt"), 40).lower()
    _expect(sdxl_pair.get("method") == "metadata_json:sdxl(insta_of_pair)", "External pair SDXL route changed method")
    _expect_trigger_once("fixture_external_pair.sdxl_soft", sdxl_pair.get("sdxl_softcore_prompt"), SdxlTrigger)
    _expect_trigger_once("fixture_external_pair.sdxl_hard", sdxl_pair.get("sdxl_hardcore_prompt"), SdxlTrigger)
    _expect("black buttoned shirt" in sdxl_soft, "External pair SDXL soft route lost embedded partner styling")
    _expect("1man" in sdxl_soft, "External pair SDXL soft route lost partner count tag")
    _expect("40-year-old adult man" in sdxl_soft, "External pair SDXL soft route lost partner descriptor")
    _expect("softcore teaser" in sdxl_soft, "External pair SDXL soft route lost softcore teaser tag")
    _expect_no_softcore_noise("fixture_external_pair.sdxl_soft", sdxl_pair.get("sdxl_softcore_prompt"))
    _expect("red satin lingerie set" in sdxl_hard, "External pair SDXL hard route lost embedded clothing state")
    _expect("row hard right-side view" in sdxl_hard, "External pair SDXL hard route lost embedded camera directive")
    _expect_no_duplicate_comma_items("fixture_external_pair.sdxl_negative", sdxl_pair.get("negative_prompt"))

    caption, method = caption_naturalizer.naturalize_caption(
        "",
        metadata_json=external_metadata,
        target="auto",
        trigger=Trigger,
        include_trigger=True,
    )
    caption_text = _expect_text("fixture_external_pair.caption", caption, 40).lower()
    _expect(method == "metadata_json:metadata(insta_of_pair)", "External pair caption route changed method")
    _expect_trigger_once("fixture_external_pair.caption", caption, Trigger)
    _expect("black buttoned shirt" in caption_text, "External pair caption route lost embedded partner styling")
    _expect("softcore side" in caption_text, "External pair caption route lost softcore side")
    _expect("hardcore side" in caption_text, "External pair caption route lost hardcore side")
    _expect("softcore version" not in caption_text and "hardcore version" not in caption_text, "External pair caption kept version labels")

    generated = _prompt_row(
        name="fixture_generated_formatter_invariants",
        category="Casual clothes",
        subcategory="Casual clothes / Smart casual",
        seed=4101,
        men_count=0,
        character_cast=_character_cast_subjects(["woman"]),
        camera_config=_orbit_camera(horizontal_angle=45, vertical_angle=0, zoom=5.5),
        location_config=_coworking_location_config(),
    )
    _expect_row_base(generated, "fixture_generated_formatter_invariants")
    _expect(generated.get("source") == "json_category", "Generated formatter invariant should use category JSON route")
    generated_metadata = _json(generated)
    generated_item = _expect_text("fixture_generated_formatter_invariants.item", generated.get("item"), 8)
    item_anchor = " ".join(generated_item.lower().split()[:3])

    krea = krea_formatter.format_krea2_prompt("", metadata_json=generated_metadata, target="single")
    krea_prompt = _expect_text("fixture_generated_formatter_invariants.krea_prompt", krea.get("krea_prompt"), 40).lower()
    _expect("metadata" in krea.get("method", ""), "Generated Krea invariant did not use metadata")
    _expect(item_anchor in krea_prompt, "Generated Krea prompt lost clothing/item metadata")
    _expect("coworking lounge" in krea_prompt, "Generated Krea prompt lost scene metadata")
    _expect("front-right quarter view" in krea_prompt, "Generated Krea prompt lost camera metadata")

    sdxl = sdxl_formatter.format_sdxl_prompt(
        "",
        metadata_json=generated_metadata,
        target="single",
        trigger=SdxlTrigger,
        prepend_trigger=True,
    )
    sdxl_prompt = _expect_text("fixture_generated_formatter_invariants.sdxl_prompt", sdxl.get("sdxl_prompt"), 40).lower()
    _expect("metadata" in sdxl.get("method", ""), "Generated SDXL invariant did not use metadata")
    _expect_trigger_once("fixture_generated_formatter_invariants.sdxl_prompt", sdxl.get("sdxl_prompt"), SdxlTrigger)
    _expect(item_anchor in sdxl_prompt, "Generated SDXL prompt lost clothing/item metadata")
    _expect("coworking lounge" in sdxl_prompt, "Generated SDXL prompt lost scene metadata")
    _expect("front-right quarter view" in sdxl_prompt, "Generated SDXL prompt lost camera metadata")
    for leaked_label in ("camera:", "composition:", "scene:", "setting:", "pose:", "clothing:"):
        _expect(leaked_label not in sdxl_prompt, f"Generated SDXL prompt leaked raw label {leaked_label!r}")

    caption, method = caption_naturalizer.naturalize_caption(
        "",
        metadata_json=generated_metadata,
        trigger=Trigger,
        include_trigger=False,
        caption_profile="training_dense",
    )
    caption_text = _expect_text("fixture_generated_formatter_invariants.caption", caption, 40).lower()
    _expect("metadata" in method, "Generated caption invariant did not use metadata")
    _expect_trigger_once("fixture_generated_formatter_invariants.caption", caption, Trigger)
    _expect(item_anchor in caption_text, "Generated training caption lost clothing/item metadata")
    _expect("coworking lounge" in caption_text, "Generated training caption lost scene metadata")


def _expect_comfy_input_spec(node_name: str, group: str, input_name: str, spec: Any) -> None:
    _expect(isinstance(input_name, str) and input_name, f"{node_name}.{group} has invalid input name {input_name!r}")
    if group == "hidden":
        _expect(
            isinstance(spec, (str, tuple)),
            f"{node_name}.{input_name} hidden input should be a ComfyUI hidden token or tuple",
        )
        return

    _expect(isinstance(spec, tuple) and spec, f"{node_name}.{input_name} visible input has invalid spec")
    type_spec = spec[0]
    _expect(
        isinstance(type_spec, str) or isinstance(type_spec, list),
        f"{node_name}.{input_name} input type should be a socket type or choices list",
    )
    if isinstance(type_spec, list):
        _expect(type_spec, f"{node_name}.{input_name} choice list should not be empty")
    _expect(len(spec) >= 2 and isinstance(spec[1], dict), f"{node_name}.{input_name} visible input options should be a dict")
    _expect("tooltip" in spec[1], f"{node_name}.{input_name} visible input is missing tooltip")


def smoke_node_runtime_contracts() -> None:
    node_names = sorted(sxcp_nodes.NODE_CLASS_MAPPINGS)
    _expect(node_names, "Node registry is empty")
    _expect(
        set(sxcp_nodes.NODE_CLASS_MAPPINGS) == set(sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS),
        "Node class and display registries are out of sync",
    )
    _expect(len(node_names) >= 50, "Node registry unexpectedly small")
    _expect(
        loop_nodes._explicit_loop_schedule("1,3,5", 5) == [1, 3, 5],
        "Loop schedule should parse comma-separated indexes",
    )
    _expect(
        loop_nodes._explicit_loop_schedule("2-4", 5) == [2, 3, 4],
        "Loop schedule should expand inclusive ranges",
    )
    _expect(
        loop_nodes._next_loop_index(4, 10, schedule="4,2") == (2, True),
        "Loop schedule should preserve explicit order",
    )
    _expect(
        loop_nodes._next_loop_index(2, 10, schedule="4,2") == (11, False),
        "Loop schedule should stop after the last scheduled index",
    )

    for node_name in node_names:
        node_class = sxcp_nodes.NODE_CLASS_MAPPINGS[node_name]
        display_name = sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS.get(node_name)
        _expect(isinstance(display_name, str) and display_name, f"{node_name} has no display name")

        input_types = node_class.INPUT_TYPES()
        _expect(isinstance(input_types, dict), f"{node_name}.INPUT_TYPES should return a dict")
        _expect("required" in input_types, f"{node_name}.INPUT_TYPES lost required group")
        for group, inputs in input_types.items():
            _expect(group in {"required", "optional", "hidden"}, f"{node_name}.INPUT_TYPES has unknown group {group!r}")
            _expect(isinstance(inputs, dict), f"{node_name}.{group} inputs should be a dict")
            for input_name, spec in inputs.items():
                _expect_comfy_input_spec(node_name, group, input_name, spec)

        return_types = getattr(node_class, "RETURN_TYPES", None)
        _expect(isinstance(return_types, tuple) and return_types, f"{node_name}.RETURN_TYPES should be a non-empty tuple")
        for output_type in return_types:
            _expect(isinstance(output_type, str) and output_type, f"{node_name}.RETURN_TYPES contains an invalid output type")

        return_names = getattr(node_class, "RETURN_NAMES", None)
        if return_names is not None:
            _expect(isinstance(return_names, tuple), f"{node_name}.RETURN_NAMES should be a tuple when present")
            _expect(len(return_names) == len(return_types), f"{node_name}.RETURN_NAMES length does not match RETURN_TYPES")
            for output_name in return_names:
                _expect(isinstance(output_name, str) and output_name, f"{node_name}.RETURN_NAMES contains an invalid name")

        function_name = getattr(node_class, "FUNCTION", None)
        _expect(isinstance(function_name, str) and function_name, f"{node_name}.FUNCTION should name the executor method")
        _expect(callable(getattr(node_class(), function_name, None)), f"{node_name}.FUNCTION target is not callable")
        category = getattr(node_class, "CATEGORY", None)
        _expect(isinstance(category, str) and category, f"{node_name}.CATEGORY should be a non-empty string")


def smoke_node_utility_registration() -> None:
    required_nodes = [
        "SxCPGlobalSeed",
        "SxCPSeedControl",
        "SxCPSeedLocker",
        "SxCPSDXLBucketSize",
        "SxCPKrea2ResolutionSelector",
    ]
    for node_name in required_nodes:
        _expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
        _expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")
    global_seed_inputs = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPGlobalSeed"].INPUT_TYPES().get("required") or {}
    global_seed_tooltip = global_seed_inputs.get("global_seed", (None, {}))[1].get("tooltip", "")
    _expect("Connect seed to generator seed" in global_seed_tooltip, "Global Seed tooltip should explain generator wiring")

    seed_control = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSeedControl"]
    seed_inputs = seed_control.INPUT_TYPES().get("required") or {}
    _expect("category_seed_mode" in seed_inputs, "Seed Control lost category seed mode input")
    _expect("clothing_seed_mode" in seed_inputs, "Seed Control lost clothing seed mode input")
    _expect("clothing_seed" in seed_inputs, "Seed Control lost clothing seed input")
    _expect("tooltip" in seed_inputs["category_seed_mode"][1], "Seed Control tooltip injection missing")
    _expect(seed_control.RETURN_NAMES == ("seed_config", "summary"), "Seed Control lost visible summary output")
    category_seed_tooltip = node_tooltips._tooltip_for_input("SxCPSeedControl", "category_seed_mode")
    _expect(
        "random rerolls this axis at queue time" in category_seed_tooltip
        and "field value stays unchanged" in category_seed_tooltip,
        "Node tooltip policy lost Seed Control override",
    )
    clothing_seed_tooltip = node_tooltips._tooltip_for_input("SxCPSeedControl", "clothing_seed_mode")
    _expect("clothing/outfit" in clothing_seed_tooltip, "Node tooltip policy lost Seed Control clothing override")
    _expect(
        "Autoscaling switch input" in node_tooltips._tooltip_for_input("SxCPIndexSwitch", "input_12"),
        "Node tooltip policy lost autoscaling input fallback",
    )
    seed_control_config, seed_control_summary = seed_control().build(
        "fixed",
        -1,
        "follow_main",
        -1,
        "random",
        -1,
        "auto",
        123,
        "auto",
        -1,
        "fixed",
        777,
        "follow_main",
        888,
        "auto",
        -1,
        "fixed",
        999,
        "fixed",
        222,
    )
    parsed_seed_control = json.loads(seed_control_config)
    _expect(parsed_seed_control.get("category_seed") == 0, "Seed Control fixed mode did not clamp negative seed")
    _expect(parsed_seed_control.get("subcategory_seed") == -1, "Seed Control follow_main mode should emit -1")
    _expect(int(parsed_seed_control.get("content_seed", -1)) >= 0, "Seed Control random mode did not emit resolved seed")
    _expect(parsed_seed_control.get("clothing_seed") == 222, "Seed Control fixed clothing seed changed")
    _expect(parsed_seed_control.get("person_seed") == 123, "Seed Control auto mode did not preserve explicit value")
    _expect(parsed_seed_control.get("pose_seed") == 777, "Seed Control fixed mode did not preserve positive seed")
    _expect(parsed_seed_control.get("role_seed") == -1, "Seed Control follow_main role did not emit -1")
    _expect(parsed_seed_control.get("composition_seed") == 999, "Seed Control fixed composition seed changed")
    _expect("category=0" in seed_control_summary, "Seed Control summary lost fixed resolved seed")
    _expect("subcategory=follow_main" in seed_control_summary, "Seed Control summary lost follow_main marker")
    _expect(
        f"content={parsed_seed_control['content_seed']}" in seed_control_summary,
        "Seed Control summary lost random resolved seed value",
    )
    legacy_seed_control_config, _legacy_seed_control_summary = seed_control().build(
        "fixed",
        -1,
        "follow_main",
        -1,
        "random",
        -1,
        "auto",
        123,
        "auto",
        456,
        "fixed",
        777,
        "follow_main",
        888,
        "auto",
        321,
        "fixed",
        999,
    )
    legacy_seed_control = json.loads(legacy_seed_control_config)
    _expect(legacy_seed_control.get("clothing_seed") == -1, "Legacy Seed Control call should leave clothing seed unset")
    _expect(legacy_seed_control.get("person_seed") == 123, "Legacy Seed Control call shifted person seed")
    _expect(legacy_seed_control.get("scene_seed") == 456, "Legacy Seed Control call shifted scene seed")
    _expect(legacy_seed_control.get("pose_seed") == 777, "Legacy Seed Control call shifted pose seed")
    _expect(legacy_seed_control.get("role_seed") == -1, "Legacy Seed Control call shifted role seed")
    _expect(legacy_seed_control.get("expression_seed") == 321, "Legacy Seed Control call shifted expression seed")
    _expect(legacy_seed_control.get("composition_seed") == 999, "Legacy Seed Control call shifted composition seed")

    seed_locker = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSeedLocker"]
    locker_inputs = seed_locker.INPUT_TYPES().get("required") or {}
    _expect(
        list(locker_inputs["reroll_axis"][0]) == seed_config.seed_reroll_axis_choices(),
        "Seed Locker reroll choices drifted from seed_config",
    )

    seed, global_seed_config, summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPGlobalSeed"]().build(12345)
    parsed_seed = json.loads(global_seed_config)
    _expect(seed == 12345, "Global Seed did not return the clamped seed")
    _expect(parsed_seed, "Global Seed config should not be empty")
    _expect(all(int(value) == 12345 for value in parsed_seed.values()), "Global Seed config did not lock every axis")
    _expect("all axes locked" in summary, "Global Seed summary changed unexpectedly")

    locker_config, locker_summary = seed_locker().build(12345, "pose", 999)
    parsed_locker = json.loads(locker_config)
    _expect(parsed_locker.get("pose_seed") == 999, "Seed Locker did not apply pose reroll seed")
    _expect("reroll pose" in locker_summary, "Seed Locker summary lost reroll axis")

    bucket_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSDXLBucketSize"]()
    bucket_a = bucket_node.build("portrait", 77, 3, 0)
    bucket_b = bucket_node.build("portrait", 77, 3, 0)
    _expect(bucket_a == bucket_b, "SDXL bucket should be deterministic for fixed seed and row")
    _expect(bucket_a[3] == "portrait", "SDXL bucket ignored orientation filter")
    bucket_seed_config = json.dumps({"camera_seed": 9001})
    bucket_seeded_a = bucket_node.build("landscape", -1, 4, 0, bucket_seed_config)
    bucket_seeded_b = bucket_node.build("landscape", -1, 4, 0, bucket_seed_config)
    _expect(bucket_seeded_a == bucket_seeded_b, "SDXL bucket should honor seed_config aliases deterministically")
    _expect(
        bucket_node._configured_bucket_seed({"camera_seed": "42"}) == 42,
        "SDXL bucket seed should delegate through composition/camera seed aliases",
    )

    krea_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2ResolutionSelector"]()
    krea_width, krea_height, _resolution, aspect_ratio, api_aspect, _api_resolution, *_rest = krea_node.select("1.0MP", "9:16")
    krea_config = json.loads(_rest[-1])
    _expect(krea_height > krea_width, "Krea2 9:16 selector should return portrait dimensions")
    _expect(aspect_ratio == "9:16", "Krea2 selector lost requested aspect ratio")
    _expect(api_aspect == "9:16", "Krea2 selector lost API aspect mapping")
    _expect(krea_config.get("width") == krea_width and krea_config.get("height") == krea_height, "Krea2 config_json dimensions mismatch")


def smoke_server_route_payload_policy() -> None:
    requested, selected, available = index_switch_policy.input_selection(
        0,
        "zero_based",
        "fallback",
        {"input_1": "first"},
    )
    _expect((requested, selected, available) == (1, 1, [1]), "Index switch policy zero-based selection changed")
    _expect(
        index_switch_policy.route_selection(65, "one_based", "wrap") == (65, 1),
        "Index switch policy wrap routing changed",
    )
    _expect(
        index_switch_policy.lazy_inputs(2, "pick_input", "one_based", "fallback", {"input_2": "second"}) == ["input_2"],
        "Index switch policy lazy input selection changed",
    )

    switch = loop_nodes.SxCPIndexSwitch()
    picked = switch.switch(
        2,
        "pick_input",
        "one_based",
        "fallback",
        input_1="first",
        input_2="second",
        fallback="fallback",
    )
    _expect(picked[0] == "second", "Index Switch pick_input did not select the requested input")
    _expect(picked[1] == 2, "Index Switch pick_input selected_index changed")
    _expect("selected=input_2" in picked[2], "Index Switch pick_input status lost selected input")

    routed = switch.switch(3, "route_output", "one_based", "fallback", route_value="routed")
    _expect(routed[0] == "routed", "Index Switch route_output primary value changed")
    _expect(routed[1] == 3, "Index Switch route_output selected_index changed")
    _expect(routed[5] == "routed", "Index Switch route_output did not route value to output_3")

    key = "smoke_route_payload"
    loop_nodes._ACCUMULATOR_STORES[key] = [
        {
            "id": "first",
            "value": "alpha",
            "_sxcp_preview_key": "first-key",
            "prompt": {"api": "prompt"},
            "extra_pnginfo": {"workflow": {"nodes": [{"id": 1, "type": "SmokeNode"}]}},
        },
        {"id": "second", "value": "beta", "_sxcp_preview_key": "second-key"},
    ]
    try:
        listed = server_routes.accumulator_list_payload({"store_key": key, "preview_limit": "0"})
        _expect(listed.get("count") == 2, "Accumulator list payload lost stored entries")
        _expect(listed["entries"][0].get("value") == "alpha", "Accumulator list payload lost value summary")

        retake = server_routes.accumulator_retake_payload({"store_key": key, "preview_key": "first-key"})
        _expect(
            retake.get("workflow", {}).get("nodes", [{}])[0].get("type") == "SmokeNode",
            "Accumulator retake payload lost workflow metadata",
        )
        _expect(retake.get("prompt", {}).get("api") == "prompt", "Accumulator retake payload lost prompt metadata")

        moved = server_routes.accumulator_move_payload({"store_key": key, "entry_id": "second", "target_index": "1"})
        _expect(moved.get("moved") is True, "Accumulator move payload did not report movement")
        _expect(moved.get("from_index") == 2 and moved.get("to_index") == 1, "Accumulator move payload changed indices")
        _expect(moved["entries"][0].get("id") == "second", "Accumulator move payload did not reorder entries")

        deleted = server_routes.accumulator_delete_payload({"store_key": key, "preview_key": "first-key"})
        _expect(deleted.get("removed") == 1, "Accumulator delete payload did not remove by preview key")
        _expect(deleted.get("count") == 1, "Accumulator delete payload count changed")

        cleared = server_routes.accumulator_delete_payload({"store_key": key, "clear": True})
        _expect(cleared.get("removed") == 1 and cleared.get("count") == 0, "Accumulator clear payload changed")
    finally:
        loop_nodes._ACCUMULATOR_STORES.pop(key, None)

    with tempfile.TemporaryDirectory() as tmpdir:
        previous_profile_dir = character_profile.PROFILE_DIR
        character_profile.PROFILE_DIR = Path(tmpdir)
        try:
            profile = character_profile.build_character_profile_json(
                profile_name="route source",
                source="manual",
                subject_type="woman",
                age="28-year-old adult",
                body="slim",
                hair="long black hair",
                save_now=False,
            )
            saved = server_routes.profile_save_cached_payload(
                {"profile_name": "Route Save!*", "profile_json": profile["profile_json"]}
            )
            saved_path = Path(saved.get("saved_path") or "")
            _expect(saved.get("status") == "saved", "Profile save payload did not save")
            _expect(saved.get("profile_name") == "Route_Save", "Profile save payload did not sanitize requested name")
            _expect(saved_path.exists(), "Profile save payload did not write profile file")
        finally:
            character_profile.PROFILE_DIR = previous_profile_dir


def smoke_seed_config_policy() -> None:
    _expect(pb.SEED_AXIS_SALTS is seed_config.SEED_AXIS_SALTS, "prompt_builder seed salts should delegate to seed_config")
    _expect(pb.seed_mode_choices() == seed_config.seed_mode_choices(), "seed mode choices drifted from seed_config")
    _expect(
        pb.seed_reroll_axis_choices() == seed_config.seed_reroll_axis_choices(),
        "seed reroll axis choices drifted from seed_config",
    )
    _expect(pb.normalize_seed_mode("follow main") == "follow_main", "seed mode normalizer should accept spaced labels")
    _expect(pb.normalize_seed_mode("FOLLOW-MAIN") == "follow_main", "seed mode normalizer should accept hyphenated labels")
    _expect(pb.normalize_reroll_axis("content pose") == "content_pose", "reroll axis normalizer should accept spaced labels")
    reroll_choices = pb.seed_reroll_axis_choices()
    for expected_axis in ("clothing", "content_clothing", "clothing_pose"):
        _expect(expected_axis in reroll_choices, f"seed reroll axis choices missing {expected_axis}")
    _expect(pb.normalize_reroll_axis("clothing pose") == "clothing_pose", "reroll axis normalizer should accept clothing pose")
    _expect(pb.normalize_reroll_axis("content clothing") == "content_clothing", "reroll axis normalizer should accept content clothing")

    fixed_config = json.loads(
        pb.build_seed_config_json(
            category_seed=-1,
            content_seed=123,
            pose_seed=456,
            role_seed=789,
            category_seed_mode="fixed",
            content_seed_mode="fixed",
            pose_seed_mode="follow-main",
            role_seed_mode="auto",
        )
    )
    _expect(fixed_config["category_seed"] == 0, "fixed seed mode should clamp negative seeds to zero")
    _expect(fixed_config["content_seed"] == 123, "fixed seed mode should preserve positive seed")
    _expect(fixed_config["pose_seed"] == -1, "follow_main seed mode should emit unlocked axis")
    _expect(fixed_config["role_seed"] == 789, "auto seed mode should preserve numeric seed")

    parsed = pb._parse_seed_config({"item_seed": "44", "pose_seed": "55", "outfit_seed": "66", "bad": "nope"})
    _expect(
        parsed == {"item_seed": 44, "pose_seed": 55, "outfit_seed": 66},
        "seed parser should keep integer-like values only",
    )
    _expect(pb._configured_axis_seed(parsed, "content") == 44, "content axis should honor item_seed alias")
    _expect(pb._configured_axis_seed(parsed, "clothing") == 66, "clothing axis should honor outfit_seed alias")
    _expect(
        pb._configured_axis_seed({"content_seed": 77}, "clothing") == 77,
        "clothing axis should keep content_seed as a legacy fallback",
    )
    _expect(
        pb._configured_axis_seed({"content_seed": 77, "clothing_seed": 88}, "clothing") == 88,
        "clothing_seed should override legacy content_seed fallback",
    )
    _expect(pb._configured_axis_seed(parsed, "role") == 55, "role axis should honor pose seed alias")
    _expect(
        seed_config.configured_seed_from_axes({"camera_seed": "88", "content_seed": "99"}, ("composition", "content")) == 88,
        "seed helper should honor axis aliases in precedence order",
    )
    _expect(
        seed_config.configured_seed_from_axes('{"bad": "json"', ("content",)) is None,
        "seed helper should return no seed for invalid config JSON",
    )

    locked = json.loads(pb.build_seed_lock_config_json(base_seed=100, reroll_axis="content pose", reroll_seed=999))
    _expect(locked["content_seed"] == 999, "content_pose reroll should alter content seed")
    _expect(locked["pose_seed"] == 999 and locked["role_seed"] == 999, "content_pose reroll should alter pose and role seeds")
    _expect(locked["scene_seed"] == 100, "content_pose reroll should leave scene locked")
    clothing_locked = json.loads(pb.build_seed_lock_config_json(base_seed=100, reroll_axis="clothing", reroll_seed=777))
    _expect(clothing_locked["clothing_seed"] == 777, "clothing reroll should alter clothing seed")
    _expect(clothing_locked["content_seed"] == 100, "clothing reroll should leave content locked")
    _expect(clothing_locked["pose_seed"] == 100 and clothing_locked["role_seed"] == 100, "clothing reroll should leave pose and role locked")

    content_clothing_locked = json.loads(
        pb.build_seed_lock_config_json(base_seed=100, reroll_axis="content_clothing", reroll_seed=778)
    )
    _expect(content_clothing_locked["content_seed"] == 778, "content_clothing reroll should alter content seed")
    _expect(content_clothing_locked["clothing_seed"] == 778, "content_clothing reroll should alter clothing seed")
    _expect(content_clothing_locked["pose_seed"] == 100, "content_clothing reroll should leave pose locked")

    clothing_pose_locked = json.loads(pb.build_seed_lock_config_json(base_seed=100, reroll_axis="clothing_pose", reroll_seed=779))
    _expect(clothing_pose_locked["clothing_seed"] == 779, "clothing_pose reroll should alter clothing seed")
    _expect(clothing_pose_locked["pose_seed"] == 779 and clothing_pose_locked["role_seed"] == 779, "clothing_pose reroll should alter pose and role seeds")
    _expect(clothing_pose_locked["content_seed"] == 100, "clothing_pose reroll should leave content locked")

    content_pose_locked = json.loads(pb.build_seed_lock_config_json(base_seed=100, reroll_axis="content_pose", reroll_seed=780))
    _expect(content_pose_locked["clothing_seed"] == 100, "content_pose reroll should not alter clothing seed")
    axis_trace = seed_config.axis_seed_trace({"content_seed": 44, "clothing_seed": 66}, 99, 3, axes=("content", "clothing", "scene"))
    _expect(axis_trace["content"]["source"] == "configured", "Seed axis trace lost configured source")
    _expect(axis_trace["content"]["seed"] == 44, "Seed axis trace lost configured seed")
    _expect(axis_trace["clothing"]["source"] == "configured", "Seed axis trace lost clothing configured source")
    _expect(axis_trace["clothing"]["seed"] == 66, "Seed axis trace lost configured clothing seed")
    _expect(axis_trace["scene"]["source"] == "main", "Seed axis trace lost main source")
    _expect(axis_trace["scene"]["seed"] == 99, "Seed axis trace lost main seed")
    _expect(
        axis_trace["content"]["rng_seed"] == seed_config.row_seed(44, 3, seed_config.SEED_AXIS_SALTS["content"]),
        "Seed axis trace lost content RNG seed",
    )

    rng_a = pb._axis_rng({"content_seed": 123}, "content", 999, 7)
    rng_b = seed_config.axis_rng({"content_seed": 123}, "content", 999, 7)
    _expect(rng_a.random() == rng_b.random(), "prompt_builder axis RNG should delegate to seed_config")
    _expect(pb._row_seed(123, 7, 41) == seed_config.row_seed(123, 7, 41), "row seed wrapper drifted from seed_config")

    deterministic_seed = 3901
    deterministic_config = pb.build_seed_lock_config_json(base_seed=deterministic_seed)
    deterministic_kwargs = {
        "name": "seed_config_policy_deterministic",
        "category": "Hardcore sexual poses",
        "subcategory": "Penetrative sex",
        "seed": deterministic_seed,
        "seed_config": deterministic_config,
        "character_cast": _character_cast(),
        "hardcore_position_config": _action_filter("penetration_only"),
        "location_config": _coworking_location_config(),
    }
    deterministic_a = _prompt_row(**deterministic_kwargs)
    deterministic_b = _prompt_row(**deterministic_kwargs)
    _expect(deterministic_a == deterministic_b, "locked seed config should reproduce identical prompt row output")

    slot_descriptor_keys = ("age_band", "body_phrase", "skin", "hair", "eyes", "figure")

    def slot_seed_cast(slot_seed: int) -> str:
        return pb.build_character_slot_json(
            subject_type="woman",
            label="A",
            slot_seed=slot_seed,
            age="random",
            ethnicity="random",
            figure="random",
            body="random",
            hair_color="random",
            hair_length="random",
            hair_style="random",
            descriptor_detail="full",
        )["character_cast"]

    def slot_seed_row(character_cast: str, reroll_seed: int) -> dict[str, Any]:
        return _prompt_row(
            name=f"seed_config_policy_slot_seed_{reroll_seed}",
            category="Casual clothes",
            subcategory="Casual clothes / Streetwear",
            seed=41001,
            seed_config=pb.build_seed_lock_config_json(
                base_seed=41001,
                reroll_axis="person",
                reroll_seed=reroll_seed,
            ),
            character_cast=character_cast,
            women_count=1,
            men_count=0,
        )

    seeded_slot_cast = slot_seed_cast(50123)
    seeded_slot_a = slot_seed_row(seeded_slot_cast, 60001)
    seeded_slot_b = slot_seed_row(seeded_slot_cast, 60002)
    _expect(
        tuple(seeded_slot_a.get(key) for key in slot_descriptor_keys)
        == tuple(seeded_slot_b.get(key) for key in slot_descriptor_keys),
        "slot_seed should keep random character-slot descriptors stable across person-axis rerolls",
    )
    seeded_trace_a = seeded_slot_a.get("generation_trace") if isinstance(seeded_slot_a.get("generation_trace"), dict) else {}
    seeded_trace_b = seeded_slot_b.get("generation_trace") if isinstance(seeded_slot_b.get("generation_trace"), dict) else {}
    _expect(
        seeded_trace_a.get("seed_axes", {}).get("person", {}).get("seed")
        != seeded_trace_b.get("seed_axes", {}).get("person", {}).get("seed"),
        "slot_seed regression check should actually reroll the person axis",
    )
    seeded_krea_a = krea_formatter.format_krea2_prompt("", metadata_json=_json(seeded_slot_a), target="single")
    seeded_krea_b = krea_formatter.format_krea2_prompt("", metadata_json=_json(seeded_slot_b), target="single")
    _expect(
        seeded_krea_a.get("krea_prompt") == seeded_krea_b.get("krea_prompt"),
        "slot_seed should keep final Krea prompt stable across person-axis rerolls",
    )

    unseeded_slot_cast = slot_seed_cast(-1)
    unseeded_slot_a = slot_seed_row(unseeded_slot_cast, 60001)
    unseeded_slot_b = slot_seed_row(unseeded_slot_cast, 60002)
    _expect(
        tuple(unseeded_slot_a.get(key) for key in slot_descriptor_keys)
        != tuple(unseeded_slot_b.get(key) for key in slot_descriptor_keys),
        "unseeded random character slot should still follow person-axis rerolls",
    )

    pose_changed = False
    for reroll_seed in range(deterministic_seed + 1, deterministic_seed + 10):
        pose_reroll = _prompt_row(
            name="seed_config_policy_pose_reroll",
            category="Hardcore sexual poses",
            subcategory="Penetrative sex",
            seed=deterministic_seed,
            seed_config=pb.build_seed_lock_config_json(
                base_seed=deterministic_seed,
                reroll_axis="pose",
                reroll_seed=reroll_seed,
            ),
            character_cast=_character_cast(),
            hardcore_position_config=_action_filter("penetration_only"),
            location_config=_coworking_location_config(),
        )
        _expect(
            pose_reroll.get("cast_descriptor_text") == deterministic_a.get("cast_descriptor_text"),
            "pose reroll should keep locked cast descriptors stable",
        )
        _expect(
            pose_reroll.get("scene_text") == deterministic_a.get("scene_text"),
            "pose reroll should keep locked scene stable",
        )
        if (
            pose_reroll.get("position_key") != deterministic_a.get("position_key")
            or pose_reroll.get("source_role_graph") != deterministic_a.get("source_role_graph")
            or pose_reroll.get("item") != deterministic_a.get("item")
        ):
            pose_changed = True
            break
    _expect(pose_changed, "pose reroll should change pose/action metadata while cast and scene stay locked")


def smoke_prompt_route_simulation_policy() -> None:
    report = prompt_route_simulation.run_simulation(seed=3901, include_prompts=False)
    summary = report.get("summary") or {}
    quality = report.get("quality") or {}
    _expect(summary.get("cases") == 16, "Prompt route simulation case count changed unexpectedly")
    _expect(summary.get("coverage_checks") == 2, "Prompt route simulation lost family coverage checks")
    _expect(summary.get("axis_checks") == 6, "Prompt route simulation lost axis check coverage")
    _expect(summary.get("pair_seed_checks") == 7, "Prompt route simulation lost pair seed check coverage")
    _expect(summary.get("issues") == 0, f"Prompt route simulation reported issues: {report.get('issues')}")
    _expect(quality.get("route_cases") == 16, "Prompt route simulation quality summary lost route case count")
    _expect(quality.get("route_issues") == 0, f"Prompt route simulation quality reported route issues: {quality}")
    _expect(quality.get("check_issues") == 0, f"Prompt route simulation quality reported check issues: {quality}")
    _expect((quality.get("targets") or {}).get("single", {}).get("cases") == 10, "Prompt route simulation quality lost single target count")
    _expect((quality.get("targets") or {}).get("softcore", {}).get("cases") == 3, "Prompt route simulation quality lost softcore target count")
    _expect((quality.get("targets") or {}).get("hardcore", {}).get("cases") == 3, "Prompt route simulation quality lost hardcore target count")
    _expect(not quality.get("issue_buckets"), "Prompt route simulation quality should have no issue buckets on clean baseline")
    _expect(not quality.get("weakest_cases"), "Prompt route simulation quality should have no weak cases on clean baseline")
    cases = {case.get("name"): case for case in report.get("cases") or []}
    for route_name in (
        "hardcore.single.oral",
        "hardcore.single.manual",
        "hardcore.single.outercourse",
        "hardcore.single.foreplay",
        "hardcore.single.interaction",
        "hardcore.single.anal",
        "hardcore.single.threesome",
        "hardcore.single.group",
        "hardcore.single.climax",
        "insta_pair.penetration.hardcore",
        "insta_pair.pov_ballsucking.hardcore",
    ):
        _expect(route_name in cases, f"Prompt route simulation lost route family case {route_name}")
    coverage_checks = {check.get("name"): check for check in report.get("coverage_checks") or []}
    action_coverage = coverage_checks.get("route_coverage.action_families") or {}
    position_coverage = coverage_checks.get("route_coverage.position_families") or {}
    _expect(not action_coverage.get("issues"), f"Prompt route simulation action coverage failed: {action_coverage}")
    _expect(not position_coverage.get("issues"), f"Prompt route simulation position coverage failed: {position_coverage}")
    expected_actions = (
        set(hardcore_action_metadata.HARDCORE_ACTION_FAMILY_CHOICES)
        - prompt_route_simulation.ROUTE_SIM_ACTION_FAMILY_EXCLUSIONS
    )
    expected_positions = (
        set(hardcore_position_config.hardcore_position_family_choices())
        - prompt_route_simulation.ROUTE_SIM_POSITION_FAMILY_EXCLUSIONS
    )
    _expect(set(action_coverage.get("observed") or []) == expected_actions, "Prompt route simulation action coverage drifted")
    _expect(set(position_coverage.get("observed") or []) == expected_positions, "Prompt route simulation position coverage drifted")
    pov_hard = cases.get("insta_pair.pov_outercourse.hardcore") or {}
    pov_summary = pov_hard.get("summary") or {}
    _expect(
        pov_summary.get("position_key") == "penis_licking",
        "Prompt route simulation should catch selected outercourse position as primary position_key",
    )
    _expect(
        "penis_licking" in (pov_summary.get("position_keys") or []),
        "Prompt route simulation lost selected outercourse key from position_keys",
    )
    ballsucking_hard = cases.get("insta_pair.pov_ballsucking.hardcore") or {}
    ballsucking_summary = ballsucking_hard.get("summary") or {}
    _expect(
        ballsucking_summary.get("position_key") == "testicle_sucking",
        "Prompt route simulation should include a dedicated ballsucking/testicle POV route",
    )
    _expect(
        "testicle_sucking" in (ballsucking_summary.get("position_keys") or []),
        "Prompt route simulation ballsucking route lost selected testicle_sucking key",
    )
    axis_checks = {check.get("name"): check for check in report.get("axis_checks") or []}
    for check_name in (
        "seed_axis.locked_determinism",
        "seed_axis.person_reroll",
        "seed_axis.scene_reroll",
        "seed_axis.pose_reroll",
        "seed_axis.expression_reroll",
        "seed_axis.composition_reroll",
    ):
        check = axis_checks.get(check_name) or {}
        _expect(check, f"Prompt route simulation lost seed-axis check {check_name}")
        _expect(not check.get("issues"), f"Prompt route simulation seed-axis check reported issues: {check_name}")
    _expect(axis_checks["seed_axis.locked_determinism"].get("changed") is False, "Locked determinism check should not be a reroll")
    for check_name in (
        "seed_axis.person_reroll",
        "seed_axis.scene_reroll",
        "seed_axis.pose_reroll",
        "seed_axis.expression_reroll",
        "seed_axis.composition_reroll",
    ):
        _expect(axis_checks[check_name].get("changed") is True, f"{check_name} should prove its axis can reroll")
    pair_seed_checks = {check.get("name"): check for check in report.get("pair_seed_checks") or []}
    for check_name in (
        "pair_seed.locked_determinism",
        "pair_seed.person_reroll",
        "pair_seed.scene_reroll",
        "pair_seed.content_reroll",
        "pair_seed.pose_reroll",
        "pair_seed.expression_reroll",
        "pair_seed.composition_reroll",
    ):
        check = pair_seed_checks.get(check_name) or {}
        _expect(check, f"Prompt route simulation lost pair seed check {check_name}")
        _expect(not check.get("issues"), f"Prompt route simulation pair seed check reported issues: {check_name}")
    _expect(
        pair_seed_checks["pair_seed.locked_determinism"].get("changed") is False,
        "Pair locked determinism check should not be a reroll",
    )
    for check_name in (
        "pair_seed.person_reroll",
        "pair_seed.scene_reroll",
        "pair_seed.content_reroll",
        "pair_seed.pose_reroll",
        "pair_seed.expression_reroll",
        "pair_seed.composition_reroll",
    ):
        _expect(pair_seed_checks[check_name].get("changed") is True, f"{check_name} should prove its axis can reroll")
    _expect(
        pair_seed_checks["pair_seed.content_reroll"].get("changed") is True,
        "Pair content reroll should prove soft outfit/content can reroll while hard action stays locked",
    )
    _expect(
        pair_seed_checks["pair_seed.pose_reroll"].get("changed") is True,
        "Pair pose reroll should prove soft pose or hard action can reroll while cast/scene axes stay locked",
    )
    climax_regression = prompt_route_simulation.run_simulation(seed=42052, include_prompts=False)
    _expect(
        climax_regression.get("summary", {}).get("issues") == 0,
        f"Prompt route simulation climax regression reported issues: {climax_regression.get('issues')}",
    )
    sweep = prompt_route_simulation.run_simulation_sweep(seed=3901, count=3, seed_step=101, include_prompts=False)
    sweep_summary = sweep.get("summary") or {}
    sweep_quality = sweep.get("quality") 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") == 48, "Prompt route simulation sweep case count changed")
    _expect(sweep_summary.get("issues") == 0, f"Prompt route simulation sweep reported issues: {sweep.get('issues')}")
    _expect(sweep_quality.get("route_cases") == 48, "Prompt route simulation sweep quality lost route case count")
    _expect(sweep_quality.get("route_issues") == 0, f"Prompt route simulation sweep quality reported route issues: {sweep_quality}")
    _expect(sweep_quality.get("check_issues") == 0, f"Prompt route simulation sweep quality reported check issues: {sweep_quality}")
    _expect((sweep_quality.get("targets") or {}).get("single", {}).get("cases") == 30, "Prompt route simulation sweep quality lost single target count")
    _expect((sweep_quality.get("targets") or {}).get("softcore", {}).get("cases") == 9, "Prompt route simulation sweep quality lost softcore target count")
    _expect((sweep_quality.get("targets") or {}).get("hardcore", {}).get("cases") == 9, "Prompt route simulation sweep quality lost hardcore target count")


def smoke_sxcp_mcp_client_cli_policy() -> None:
    helper_path = ROOT / "tools" / "sxcp_mcp_client.py"
    eval_loop_doc = (ROOT / "docs" / "sxcp-eval-loop.md").read_text(encoding="utf-8")
    _expect("## MCP Helper Command" in eval_loop_doc, "SxCP eval loop doc lost MCP helper command memory")
    _expect(
        "/media/p5/miniforge3/bin/python tools/sxcp_mcp_client.py call-tool comfy_push" in eval_loop_doc,
        "SxCP eval loop doc lost approved MCP push helper command",
    )
    _expect("sxcp_eval_out" in eval_loop_doc, "SxCP eval loop doc lost output channel name")
    top_help = subprocess.run(
        [sys.executable, str(helper_path), "--help"],
        cwd=ROOT,
        capture_output=True,
        text=True,
        check=False,
    )
    _expect(top_help.returncode == 0, f"sxcp MCP client --help failed: {top_help.stderr}")
    help_text = top_help.stdout
    _expect("list-tools" in help_text, "sxcp MCP client help lost list-tools command")
    _expect("call-tool" in help_text, "sxcp MCP client help lost call-tool command")
    _expect("--bridge-url" in help_text, "sxcp MCP client help lost bridge URL option")
    call_help = subprocess.run(
        [sys.executable, str(helper_path), "call-tool", "--help"],
        cwd=ROOT,
        capture_output=True,
        text=True,
        check=False,
    )
    _expect(call_help.returncode == 0, f"sxcp MCP client call-tool --help failed: {call_help.stderr}")
    _expect("--arguments-json" in call_help.stdout, "sxcp MCP client help lost JSON argument option")


def smoke_watch_prompt_image_folder_cli_policy() -> None:
    helper_path = ROOT / "tools" / "watch_prompt_image_folder.sh"
    _expect(helper_path.exists(), "watch prompt/image folder helper is missing")
    _expect(helper_path.stat().st_mode & 0o111, "watch prompt/image folder helper must be executable")

    help_result = subprocess.run(
        [str(helper_path), "--help"],
        cwd=ROOT,
        capture_output=True,
        text=True,
        check=False,
    )
    _expect(help_result.returncode == 0, f"watch helper --help failed: {help_result.stderr}")
    for term in ("--folder", "--target", "--notes", "--once", "--dry-run", "tmux send-keys"):
        _expect(term in help_result.stdout, f"watch helper help lost {term!r}")

    with tempfile.TemporaryDirectory() as temp_dir:
        folder = Path(temp_dir)
        prompt_path = folder / "atlas_case_001.txt"
        image_path = folder / "atlas_case_001.png"
        state_path = folder / "seen.state"
        notes_path = folder / "prompt-learning.md"
        prompt_path.write_text("nadir-angle standing male POV test prompt\n", encoding="utf-8")
        image_path.write_bytes(b"fake-png")

        first_scan = subprocess.run(
            [
                str(helper_path),
                "--folder",
                str(folder),
                "--target",
                "codex:1.0",
                "--notes",
                str(notes_path),
                "--state",
                str(state_path),
                "--once",
                "--dry-run",
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        _expect(first_scan.returncode == 0, f"watch helper dry-run scan failed: {first_scan.stderr}")
        _expect("tmux send-keys -t codex:1.0" in first_scan.stdout, "watch helper dry-run did not render tmux target")
        _expect(str(prompt_path) in first_scan.stdout, "watch helper notification lost prompt path")
        _expect(str(image_path) in first_scan.stdout, "watch helper notification lost image path")
        _expect(str(notes_path) in first_scan.stdout, "watch helper notification lost notes path")
        _expect("atlas_case_001.txt" in state_path.read_text(encoding="utf-8"), "watch helper did not record notified prompt")

        second_scan = subprocess.run(
            [
                str(helper_path),
                "--folder",
                str(folder),
                "--target",
                "codex:1.0",
                "--notes",
                str(notes_path),
                "--state",
                str(state_path),
                "--once",
                "--dry-run",
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        _expect(second_scan.returncode == 0, f"watch helper repeated dry-run scan failed: {second_scan.stderr}")
        _expect("no new prompt/image pairs" in second_scan.stdout, "watch helper should dedupe already-notified pairs")

        jpeg_prompt = folder / "atlas_case_002.prompt"
        jpeg_image = folder / "atlas_case_002.jpg"
        jpeg_prompt.write_text("side-profile oral prompt\n", encoding="utf-8")
        jpeg_image.write_bytes(b"fake-jpg")
        third_scan = subprocess.run(
            [
                str(helper_path),
                "--folder",
                str(folder),
                "--target",
                "codex:1.0",
                "--notes",
                str(notes_path),
                "--state",
                str(state_path),
                "--once",
                "--dry-run",
            ],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        _expect(third_scan.returncode == 0, f"watch helper jpeg dry-run scan failed: {third_scan.stderr}")
        _expect(str(jpeg_prompt) in third_scan.stdout and str(jpeg_image) in third_scan.stdout, "watch helper lost prompt/JPEG pairing")


def smoke_sxcp_prompt_batch_cli_policy() -> None:
    helper_path = ROOT / "tools" / "sxcp_prompt_batch.py"
    eval_loop_doc = (ROOT / "docs" / "sxcp-eval-loop.md").read_text(encoding="utf-8")
    methodology = (ROOT / "docs" / "krea2-ab-methodology.md").read_text(encoding="utf-8")
    _expect("tools/sxcp_prompt_batch.py" in eval_loop_doc, "SxCP eval loop doc lost prompt batch helper")
    _expect("print-push-commands" in eval_loop_doc, "SxCP eval loop doc lost prompt batch command rendering")
    _expect("print-result-template" in eval_loop_doc, "SxCP eval loop doc lost prompt batch result template")
    _expect("run-batch" in eval_loop_doc, "SxCP eval loop doc lost prompt batch runner")
    _expect("validate-results" in eval_loop_doc, "SxCP eval loop doc lost prompt batch result validation")
    _expect("print-eval-entry-draft" in eval_loop_doc, "SxCP eval loop doc lost prompt batch eval-entry draft")
    _expect("--allow-geometry-only" in eval_loop_doc, "SxCP eval loop doc lost geometry-only draft guard")
    _expect("tools/sxcp_prompt_batch.py" in methodology, "Krea2 A/B methodology lost prompt batch helper memory")
    _expect("run-batch" in methodology, "Krea2 A/B methodology lost prompt batch runner")
    _expect("validate-results" in methodology, "Krea2 A/B methodology lost prompt batch result validation")
    _expect("print-eval-entry-draft" in methodology, "Krea2 A/B methodology lost prompt batch eval-entry draft memory")
    _expect("--allow-geometry-only" in methodology, "Krea2 A/B methodology lost geometry-only draft guard")
    batch = {
        "seed": 123456789,
        "channel_out": "sxcp_eval_out",
        "channel_in": "sxcp_eval_in",
        "subject_id": "same_woman_001",
        "variant_key": "pov_footjob_frontal_sole_stroke",
        "source_entry_id": "pov_footjob_frontal_sole_stroke_00001",
        "source_stem": "pov_footjob_frontal_sole_stroke_00001_",
        "selection": {"selection_seed": 202, "seed_slot": "atlas_cue_seed"},
        "probes": [
            {
                "id": "subject_first_axis",
                "prompt_order": "subject_first",
                "text": "A 24-year-old adult woman with long wavy brunette hair. Subject-first controlled pose wording.",
                "variant_key": "pov_footjob_frontal_sole_stroke",
                "source_entry_id": "pov_footjob_frontal_sole_stroke_00001",
                "source_stem": "pov_footjob_frontal_sole_stroke_00001_",
                "cue_axes": {"foot_position": "soles_more_forward"},
                "seed_metadata": {"atlas_cue_seed": 202},
                "evidence": {"seed": 123456789, "image_path": "/tmp/subject_first_axis.png"},
                "matrix_evidence": {
                    "stable": True,
                    "selection_seed": 202,
                    "sampler_seeds": [123456789],
                    "jobs": [{"id": "job_1"}],
                },
                "selection": {"selection_seed": 202, "seed_slot": "atlas_cue_seed"},
                "prompt_source": {"kind": "append_cues", "append_cues": ["soles farther forward"]},
                "notes": "metadata should survive helper loading",
            },
            {
                "id": "geometry_only_axis",
                "prompt_order": "geometry_only",
                "text": "Geometry-only floor-plane wording for rough pose-axis discovery.",
            },
        ],
    }
    with tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".json", delete=False) as handle:
        json.dump(batch, handle)
        batch_path = Path(handle.name)
    try:
        loaded_batch = sxcp_prompt_batch.load_batch(batch_path)
        loaded_probe = loaded_batch.get("probes", [{}])[0]
        _expect(
            loaded_batch.get("variant_key") == "pov_footjob_frontal_sole_stroke"
            and loaded_batch.get("selection", {}).get("selection_seed") == 202,
            f"sxcp prompt batch loader should preserve atlas batch metadata: {loaded_batch}",
        )
        _expect(
            loaded_probe.get("matrix_evidence", {}).get("stable") is True
            and loaded_probe.get("cue_axes", {}).get("foot_position") == "soles_more_forward"
            and loaded_probe.get("seed_metadata", {}).get("atlas_cue_seed") == 202,
            f"sxcp prompt batch loader should preserve atlas probe metadata: {loaded_probe}",
        )
        commands = subprocess.run(
            [sys.executable, str(helper_path), "print-push-commands", "--batch-json", str(batch_path)],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        _expect(commands.returncode == 0, f"sxcp prompt batch command rendering failed: {commands.stderr}")
        _expect("tools/sxcp_mcp_client.py call-tool comfy_push" in commands.stdout, "sxcp prompt batch lost MCP push helper command")
        _expect('"channel":"sxcp_eval_out"' in commands.stdout, "sxcp prompt batch lost positive output channel")
        _expect('"seed":123456789' in commands.stdout, "sxcp prompt batch lost fixed sampler seed")
        _expect("subject_first_axis" in commands.stdout and "geometry_only_axis" in commands.stdout, "sxcp prompt batch lost probe ids")
        _expect("geometry-only" in commands.stdout, "sxcp prompt batch lost geometry-only caveat")
        _expect("sxcp_eval_negative_out" not in commands.stdout, "sxcp prompt batch should not emit negative output channel")

        template = subprocess.run(
            [sys.executable, str(helper_path), "print-result-template", "--batch-json", str(batch_path)],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        _expect(template.returncode == 0, f"sxcp prompt batch result template failed: {template.stderr}")
        result_template = json.loads(template.stdout)
        _expect(result_template.get("seed") == 123456789, "sxcp prompt batch result template lost seed")
        _expect(result_template.get("channel_in") == "sxcp_eval_in", "sxcp prompt batch result template lost input channel")
        probes = result_template.get("probes") or []
        _expect([probe.get("id") for probe in probes] == ["subject_first_axis", "geometry_only_axis"], "sxcp prompt batch result template lost probe order")
        _expect(all("image_path" in probe and "turn" in probe for probe in probes), "sxcp prompt batch result template lost image presence fields")

        result_template["probes"][0].update(
            {
                "turn": 101,
                "image_path": "/media/unraid/comfyui/output/agent_bridge/img_subject_first.png",
                "returned_seed": 123456789,
            }
        )
        result_template["probes"][1].update(
            {
                "turn": 102,
                "image_path": "/media/unraid/comfyui/output/agent_bridge/img_geometry_only.png",
                "returned_seed": 123456789,
            }
        )
        with tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".json", delete=False) as result_handle:
            json.dump(result_template, result_handle)
            result_path = Path(result_handle.name)
        try:
            validated_results = subprocess.run(
                [
                    sys.executable,
                    str(helper_path),
                    "validate-results",
                    "--batch-json",
                    str(batch_path),
                    "--result-json",
                    str(result_path),
                ],
                cwd=ROOT,
                capture_output=True,
                text=True,
                check=False,
            )
            _expect(validated_results.returncode == 0, f"sxcp prompt batch result validation failed: {validated_results.stderr}")
            _expect("validated results: 2 probes, seed 123456789" in validated_results.stdout, "sxcp prompt batch result validation lost summary")

            draft = subprocess.run(
                [
                    sys.executable,
                    str(helper_path),
                    "print-eval-entry-draft",
                    "--batch-json",
                    str(batch_path),
                    "--result-json",
                    str(result_path),
                    "--variant-key",
                    "pov_missionary_open_leg_penetration",
                    "--entry-id",
                    "missionary-open-123456789-subject-first-axis",
                    "--baseline-image",
                    "/media/unraid/comfyui/output/agent_bridge/img_baseline.png",
                    "--candidate-id",
                    "subject_first_axis",
                    "--source",
                    "sxcp_eval_mcp_batch_smoke",
                ],
                cwd=ROOT,
                capture_output=True,
                text=True,
                check=False,
            )
            _expect(draft.returncode == 0, f"sxcp prompt batch eval-entry draft failed: {draft.stderr}")
            entry = json.loads(draft.stdout)
            _expect(entry.get("id") == "missionary-open-123456789-subject-first-axis", "sxcp prompt batch eval draft lost entry id")
            _expect(entry.get("variant_key") == "pov_missionary_open_leg_penetration", "sxcp prompt batch eval draft lost variant key")
            _expect(entry.get("seed") == 123456789, "sxcp prompt batch eval draft lost seed")
            _expect(entry.get("baseline_image") == "/media/unraid/comfyui/output/agent_bridge/img_baseline.png", "sxcp prompt batch eval draft lost baseline image")
            _expect(entry.get("candidate_image") == "/media/unraid/comfyui/output/agent_bridge/img_subject_first.png", "sxcp prompt batch eval draft lost selected candidate image")
            _expect(entry.get("result") == "inconclusive" and entry.get("decision") == "needs_more_tests", "sxcp prompt batch eval draft should default to inconclusive needs_more_tests")
            _expect("subject_first_axis" in entry.get("candidate_prompt_summary", ""), "sxcp prompt batch eval draft lost candidate probe id")
            _expect("subject_first" in entry.get("observation", ""), "sxcp prompt batch eval draft lost prompt-order note")
            errors = krea2_eval_log.validate_entry(entry, catalog_keys=set(krea2_pose_variant_catalog.variant_keys()))
            _expect(errors == [], f"sxcp prompt batch eval draft should validate: {errors}")

            geometry_draft = subprocess.run(
                [
                    sys.executable,
                    str(helper_path),
                    "print-eval-entry-draft",
                    "--batch-json",
                    str(batch_path),
                    "--result-json",
                    str(result_path),
                    "--variant-key",
                    "pov_missionary_open_leg_penetration",
                    "--baseline-image",
                    "/media/unraid/comfyui/output/agent_bridge/img_baseline.png",
                    "--candidate-id",
                    "geometry_only_axis",
                ],
                cwd=ROOT,
                capture_output=True,
                text=True,
                check=False,
            )
            _expect(geometry_draft.returncode != 0, "sxcp prompt batch eval draft should reject geometry-only candidates by default")
            _expect("geometry_only" in geometry_draft.stderr, "sxcp prompt batch geometry-only rejection should name prompt order")

            allowed_geometry_draft = subprocess.run(
                [
                    sys.executable,
                    str(helper_path),
                    "print-eval-entry-draft",
                    "--batch-json",
                    str(batch_path),
                    "--result-json",
                    str(result_path),
                    "--variant-key",
                    "pov_missionary_open_leg_penetration",
                    "--baseline-image",
                    "/media/unraid/comfyui/output/agent_bridge/img_baseline.png",
                    "--candidate-id",
                    "geometry_only_axis",
                    "--allow-geometry-only",
                ],
                cwd=ROOT,
                capture_output=True,
                text=True,
                check=False,
            )
            _expect(allowed_geometry_draft.returncode == 0, f"sxcp prompt batch should allow explicit geometry-only drafts: {allowed_geometry_draft.stderr}")
            allowed_entry = json.loads(allowed_geometry_draft.stdout)
            _expect("not treat as subject/look-controlled evidence" in allowed_entry.get("observation", ""), "sxcp prompt batch allowed geometry draft lost caveat")

            mock_pulls = [
                {
                    "turn": 201,
                    "image_path": "/media/unraid/comfyui/output/agent_bridge/img_subject_first_run.png",
                    "seed": 123456789,
                },
                {
                    "turn": 201,
                    "image_path": "/media/unraid/comfyui/output/agent_bridge/img_stale_run.png",
                    "seed": 123456789,
                },
                {
                    "turn": 202,
                    "image_path": "/media/unraid/comfyui/output/agent_bridge/img_geometry_run.png",
                    "seed": 123456789,
                },
            ]
            with tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".json", delete=False) as mock_handle:
                json.dump(mock_pulls, mock_handle)
                mock_path = Path(mock_handle.name)
            with tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".json", delete=False) as run_result_handle:
                run_result_path = Path(run_result_handle.name)
            run_result_path.unlink(missing_ok=True)
            try:
                runner = subprocess.run(
                    [
                        sys.executable,
                        str(helper_path),
                        "run-batch",
                        "--batch-json",
                        str(batch_path),
                        "--result-json",
                        str(run_result_path),
                        "--mock-pulls-json",
                        str(mock_path),
                        "--previous-turn",
                        "200",
                        "--max-polls",
                        "3",
                    ],
                    cwd=ROOT,
                    capture_output=True,
                    text=True,
                    check=False,
                )
                _expect(runner.returncode == 0, f"sxcp prompt batch runner failed: {runner.stderr}")
                _expect("recorded results: 2 probes, seed 123456789" in runner.stdout, "sxcp prompt batch runner lost result summary")
                run_results = json.loads(run_result_path.read_text(encoding="utf-8"))
                _expect(
                    [probe.get("turn") for probe in run_results.get("probes") or []] == [201, 202],
                    "sxcp prompt batch runner should skip stale turns and preserve batch order",
                )
                _expect(
                    [probe.get("image_path") for probe in run_results.get("probes") or []]
                    == [
                        "/media/unraid/comfyui/output/agent_bridge/img_subject_first_run.png",
                        "/media/unraid/comfyui/output/agent_bridge/img_geometry_run.png",
                    ],
                    "sxcp prompt batch runner lost selected image paths",
                )
                validate_runner_results = subprocess.run(
                    [
                        sys.executable,
                        str(helper_path),
                        "validate-results",
                        "--batch-json",
                        str(batch_path),
                        "--result-json",
                        str(run_result_path),
                    ],
                    cwd=ROOT,
                    capture_output=True,
                    text=True,
                    check=False,
                )
                _expect(validate_runner_results.returncode == 0, f"sxcp prompt batch runner wrote invalid results: {validate_runner_results.stderr}")
            finally:
                mock_path.unlink(missing_ok=True)
                run_result_path.unlink(missing_ok=True)

            bad_result = json.loads(json.dumps(result_template))
            bad_result["probes"][1]["returned_seed"] = 987654321
            with tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".json", delete=False) as bad_result_handle:
                json.dump(bad_result, bad_result_handle)
                bad_result_path = Path(bad_result_handle.name)
            try:
                bad_validated_results = subprocess.run(
                    [
                        sys.executable,
                        str(helper_path),
                        "validate-results",
                        "--batch-json",
                        str(batch_path),
                        "--result-json",
                        str(bad_result_path),
                    ],
                    cwd=ROOT,
                    capture_output=True,
                    text=True,
                    check=False,
                )
                _expect(bad_validated_results.returncode != 0, "sxcp prompt batch result validation should reject returned seed mismatch")
                _expect("returned_seed" in bad_validated_results.stderr, "sxcp prompt batch result validation should name returned_seed mismatch")
            finally:
                bad_result_path.unlink(missing_ok=True)

            bad_result = json.loads(json.dumps(result_template))
            bad_result["probes"][1]["id"] = "unexpected_axis"
            with tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".json", delete=False) as bad_result_handle:
                json.dump(bad_result, bad_result_handle)
                bad_result_path = Path(bad_result_handle.name)
            try:
                bad_validated_results = subprocess.run(
                    [
                        sys.executable,
                        str(helper_path),
                        "validate-results",
                        "--batch-json",
                        str(batch_path),
                        "--result-json",
                        str(bad_result_path),
                    ],
                    cwd=ROOT,
                    capture_output=True,
                    text=True,
                    check=False,
                )
                _expect(bad_validated_results.returncode != 0, "sxcp prompt batch result validation should reject probe order mismatch")
                _expect("probe ids" in bad_validated_results.stderr, "sxcp prompt batch result validation should name probe ids mismatch")
            finally:
                bad_result_path.unlink(missing_ok=True)
        finally:
            result_path.unlink(missing_ok=True)
    finally:
        batch_path.unlink(missing_ok=True)

    bad_batch = dict(batch)
    bad_batch["channel_out"] = "sxcp_eval_negative_out"
    with tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".json", delete=False) as handle:
        json.dump(bad_batch, handle)
        bad_batch_path = Path(handle.name)
    try:
        bad = subprocess.run(
            [sys.executable, str(helper_path), "validate", "--batch-json", str(bad_batch_path)],
            cwd=ROOT,
            capture_output=True,
            text=True,
            check=False,
        )
        _expect(bad.returncode != 0, "sxcp prompt batch should reject negative output channel")
        _expect("sxcp_eval_negative_out" in bad.stderr, "sxcp prompt batch rejection should name the negative channel")
    finally:
        bad_batch_path.unlink(missing_ok=True)


def smoke_node_camera_registration() -> None:
    required_nodes = [
        "SxCPCameraControl",
        "SxCPCameraOrbitControl",
        "SxCPQwenCameraTranslator",
    ]
    for node_name in required_nodes:
        _expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
        _expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")

    camera_control = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCameraControl"]
    camera_inputs = camera_control.INPUT_TYPES().get("required") or {}
    _expect("camera_mode" in camera_inputs, "Camera Control lost camera_mode input")
    _expect("tooltip" in camera_inputs["camera_mode"][1], "Camera Control tooltip injection missing")
    camera_config = camera_control().build(
        "handheld_selfie",
        "three_quarter_body",
        "high_angle",
        "smartphone_wide",
        "arm_length",
        "vertical_story",
        "phone_visible",
        "locked",
        "compact",
    )[0]
    parsed_camera = json.loads(camera_config)
    _expect(parsed_camera.get("camera_mode") == "handheld_selfie", "Camera Control lost camera_mode")
    _expect(parsed_camera.get("phone_visibility") == "phone_visible", "Camera Control lost phone visibility")

    orbit_config, orbit_prompt, orbit_info = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCameraOrbitControl"]().build(
        True,
        "standard",
        45,
        0,
        5.5,
        "from_zoom",
        "auto",
        "auto",
        "auto",
        "auto",
        "soft_hint",
        "compact",
        True,
    )
    parsed_orbit = json.loads(orbit_config)
    _expect(parsed_orbit.get("camera_source") == "orbit", "Orbit camera lost source metadata")
    _expect("front-right quarter view" in orbit_prompt, "Orbit camera prompt lost direction")
    _expect(json.loads(orbit_info).get("orbit_azimuth") == 45, "Orbit info JSON lost azimuth")

    qwen_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPQwenCameraTranslator"]
    qwen_inputs = qwen_node.INPUT_TYPES()
    _expect("camera_info" in (qwen_inputs.get("optional") or {}), "Qwen translator lost camera_info optional input")
    qwen_config, qwen_prompt, qwen_info = qwen_node().build(
        "<sks> front-right quarter view eye-level shot medium shot",
        True,
        "standard",
        "auto",
        "auto",
        "auto",
        "auto",
        "soft_hint",
        "compact",
        False,
        True,
    )
    parsed_qwen = json.loads(qwen_config)
    _expect(parsed_qwen.get("camera_source") == "qwen_multiangle_prompt", "Qwen translator lost source metadata")
    _expect(parsed_qwen.get("phone_visibility") == "auto", "Qwen translator should suppress phone visibility by default")
    _expect("front-right quarter view" in qwen_prompt, "Qwen camera prompt lost direction")
    _expect(json.loads(qwen_info).get("qwen_prompt", "").startswith("<sks>"), "Qwen info JSON lost original prompt")


def smoke_node_route_config_registration() -> None:
    required_nodes = [
        "SxCPCategoryPreset",
        "SxCPLocationPool",
        "SxCPCompositionPool",
        "SxCPLocationTheme",
        "SxCPStylePool",
        "SxCPCastControl",
        "SxCPCastBias",
    ]
    for node_name in required_nodes:
        _expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
        _expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")

    category_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCategoryPreset"]
    category_inputs = category_node.INPUT_TYPES().get("required") or {}
    _expect("preset" in category_inputs, "Category Preset lost preset input")
    _expect("tooltip" in category_inputs["preset"][1], "Category Preset tooltip injection missing")
    category_config, category, subcategory = category_node().build("auto_weighted", "random")
    parsed_category = json.loads(category_config)
    _expect(category == parsed_category.get("category") == "auto_weighted", "Category Preset output category mismatch")
    _expect(subcategory == "random", "Category Preset output subcategory mismatch")

    location_config, location_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPLocationPool"]().build(
        True,
        "replace",
        "custom_only",
        "classical library stacks with brass lamps",
    )
    parsed_location = json.loads(location_config)
    _expect(parsed_location.get("scene_entries"), "Location Pool did not keep custom location")
    _expect("locations=1" in location_summary, "Location Pool summary lost custom count")

    composition_config, composition_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCompositionPool"]().build(
        True,
        "replace",
        "no_outfit_check",
        "long aisle composition with shelves repeating behind the subject",
    )
    parsed_composition = json.loads(composition_config)
    _expect(parsed_composition.get("composition_entries"), "Composition Pool did not keep composition entries")
    _expect("compositions=" in composition_summary, "Composition Pool summary lost composition count")

    theme_location, theme_composition, theme_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPLocationTheme"]().build(
        True,
        "replace",
        "semi_public_affair",
        "",
        "",
    )
    _expect(json.loads(theme_location).get("scene_entries"), "Location Theme did not output locations")
    _expect(json.loads(theme_composition).get("composition_entries"), "Location Theme did not output compositions")
    _expect("semi_public_affair" in theme_summary, "Location Theme summary lost theme name")

    style_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPStylePool"]
    style_inputs = style_node.INPUT_TYPES().get("required") or {}
    _expect("preset" in style_inputs, "Style Pool lost preset input")
    _expect("tooltip" in style_inputs["preset"][1], "Style Pool tooltip injection missing")
    style_config, style_summary = style_node().build(
        True,
        "replace",
        "comic_pinup_colored_pencil",
        "",
        "",
        "",
    )
    parsed_style = json.loads(style_config)
    _expect(parsed_style.get("schema") == "sxcp_style_config_v1", "Style Pool emitted wrong schema")
    _expect("comic pin-up" in parsed_style.get("style", ""), "Style Pool lost comic preset style")
    _expect("comic pin-up" in style_summary, "Style Pool summary lost preset label")

    cast_config, women_count, men_count, cast_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCastControl"]().build(
        "mixed_couple",
        1,
        1,
    )
    parsed_cast = json.loads(cast_config)
    _expect((women_count, men_count) == (parsed_cast.get("women_count"), parsed_cast.get("men_count")), "Cast Control count outputs mismatch")
    _expect("1 women, 1 men" in cast_summary, "Cast Control summary changed unexpectedly")

    cast_bias = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCastBias"]()
    bias_a = cast_bias.build(123, 2, "0.7,0.3", 1, "0.4,0.6", 0, "force_one_woman")
    bias_b = cast_bias.build(123, 2, "0.7,0.3", 1, "0.4,0.6", 0, "force_one_woman")
    _expect(bias_a == bias_b, "Cast Bias should be deterministic for fixed seed and row")
    _expect(bias_a[1] + bias_a[2] >= 1, "Cast Bias empty behavior allowed empty cast")
    _expect("weighted cast:" in bias_a[3], "Cast Bias summary lost weighted cast label")
    bias_seed_config = json.dumps({"category": 702})
    seeded_bias_a = cast_bias.build(-1, 2, "0.7,0.3", 1, "0.4,0.6", 0, "force_one_woman", bias_seed_config)
    seeded_bias_b = cast_bias.build(-1, 2, "0.7,0.3", 1, "0.4,0.6", 0, "force_one_woman", bias_seed_config)
    _expect(seeded_bias_a == seeded_bias_b, "Cast Bias should honor seed_config aliases deterministically")
    _expect(cast_bias._configured_cast_seed({"category": "702"}) == 702, "Cast Bias seed should delegate through category aliases")


def smoke_node_character_registration() -> None:
    required_nodes = [
        "SxCPHairLength",
        "SxCPHairColor",
        "SxCPHairStyle",
        "SxCPCharacterAgeRange",
        "SxCPCharacterBodyPool",
        "SxCPWomanBodyPool",
        "SxCPManBodyPool",
        "SxCPEyeColorPool",
        "SxCPCharacterClothing",
        "SxCPCharacterManualDetails",
        "SxCPWomanSlot",
        "SxCPManSlot",
        "SxCPCharacterSlot",
        "SxCPCharacterProfileSave",
        "SxCPCharacterProfileLoad",
    ]
    for node_name in required_nodes:
        _expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
        _expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")

    woman_slot_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPWomanSlot"]
    woman_slot_input_types = woman_slot_node.INPUT_TYPES()
    woman_slot_inputs = woman_slot_input_types.get("required") or {}
    _expect("slot_seed" in woman_slot_inputs, "Woman Slot lost slot_seed input")
    _expect("tooltip" in woman_slot_inputs["slot_seed"][1], "Woman Slot tooltip injection missing")
    woman_slot_optional = woman_slot_input_types.get("optional") or {}
    cast_tooltip = woman_slot_optional.get("character_cast", (None, {}))[1].get("tooltip", "")
    _expect("incoming cast from the previous slot" in cast_tooltip, "Woman Slot character_cast tooltip should explain chain order")
    man_slot_required = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPManSlot"].INPUT_TYPES().get("required") or {}
    presence_tooltip = man_slot_required.get("presence_mode", (None, {}))[1].get("tooltip", "")
    _expect("first-person viewer" in presence_tooltip, "Man Slot presence tooltip should explain POV behavior")

    hair_config, hair_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPHairColor"]().build(
        "replace_axis",
        "",
        include_blonde=True,
    )
    parsed_hair = json.loads(hair_config)
    _expect(parsed_hair.get("colors") == ["blonde"], "Hair Color did not output selected blonde pool")
    _expect("colors=blonde" in hair_summary, "Hair Color summary changed unexpectedly")

    age_config, age_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCharacterAgeRange"]().build(
        "replace_axis",
        25,
        27,
    )
    parsed_age = json.loads(age_config)
    _expect(parsed_age.get("ages") == ["25-year-old adult", "26-year-old adult", "27-year-old adult"], "Age Range output changed")
    _expect("25-year-old adult" in age_summary, "Age Range summary lost selected ages")

    body_config, _body_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPWomanBodyPool"]().build(
        "replace_axis",
        "",
        include_curvy=True,
    )
    _expect(json.loads(body_config).get("bodies") == ["curvy"], "Woman Body Pool did not output selected body")

    eye_config, _eye_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPEyeColorPool"]().build(
        "replace_axis",
        "",
        include_blue=True,
    )
    _expect(json.loads(eye_config).get("eyes") == ["blue"], "Eye Color Pool did not output selected eye color")

    clothing_config, clothing_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCharacterClothing"]().build(
        "replace_axis",
        "lingerie_tease",
        "fully_nude",
        "",
        "",
    )
    parsed_clothing = json.loads(clothing_config)
    _expect(parsed_clothing.get("softcore_outfits"), "Character Clothing lost softcore outfit pool")
    _expect(parsed_clothing.get("hardcore_clothing") == ["fully nude"], "Character Clothing lost hardcore clothing state")
    _expect("soft_outfits=" in clothing_summary, "Character Clothing summary lost outfit count")

    manual_config, manual_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCharacterManualDetails"]().build(
        "merge_nonempty",
        "31-year-old adult",
        "curvy",
        "custom body phrase",
        "warm skin",
        "short blonde hair",
        "blue eyes",
        "red dress",
        "fully nude",
    )
    parsed_manual = json.loads(manual_config)
    _expect(parsed_manual.get("manual_age") == "31-year-old adult", "Manual Details lost manual_age")
    _expect(parsed_manual.get("softcore_outfit") == "red dress", "Manual Details lost softcore outfit")
    _expect("manual_age=31-year-old adult" in manual_summary, "Manual Details summary changed unexpectedly")

    cast, slot, slot_summary, slot_status = woman_slot_node().build(
        True,
        "A",
        123,
        "25-year-old adult",
        "western_european",
        "balanced",
        "curvy",
        "full",
        True,
        0.5,
        0.4,
        0.8,
        "",
        "",
        "",
        hair_config,
        "",
    )
    parsed_slot = json.loads(slot)
    _expect(parsed_slot.get("subject_type") == "woman", "Woman Slot output lost subject type")
    _expect(parsed_slot.get("slot_seed") == 123, "Woman Slot output lost slot seed")
    _expect("Woman A" in slot_summary, "Woman Slot summary lost label")
    _expect("1 slot(s)" in slot_status, "Woman Slot status lost cast count")
    _expect(json.loads(cast).get("slots"), "Woman Slot did not output chained cast JSON")

    man_cast, man_slot, _man_summary, _man_status = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPManSlot"]().build(
        True,
        "A",
        124,
        "40-year-old adult",
        "western_european",
        "average",
        "compact",
        True,
        0.3,
        "pov",
        0.2,
        0.7,
        cast,
        "",
        "",
        "",
        "",
    )
    _expect(json.loads(man_slot).get("presence_mode") == "pov", "Man Slot output lost POV presence mode")
    _expect(len(json.loads(man_cast).get("slots") or []) == 2, "Man Slot did not append to incoming cast")

    save_result = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCharacterProfileSave"]().build(
        "smoke_profile",
        "character_slot",
        "woman",
        "",
        "",
        "",
        "",
        "",
        "",
        "",
        False,
        character_slot=slot,
    )
    saved_profile = save_result["result"][0]
    _expect(save_result["result"][2] == "smoke_profile", "Profile Save lost profile name")
    _expect(save_result["result"][4] == "not_saved", "Profile Save should not write when save_now is false")
    loaded_profile = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCharacterProfileLoad"]().build(
        True,
        "manual",
        "",
        False,
        False,
        fallback_profile_json=saved_profile,
    )
    _expect(loaded_profile[4] == "fallback", "Profile Load should consume fallback profile JSON")
    _expect(json.loads(loaded_profile[0]).get("profile_type") == "character", "Profile Load returned wrong profile type")


def smoke_node_hardcore_position_registration() -> None:
    required_nodes = [
        "SxCPHardcorePositionPool",
        "SxCPHardcoreActionFilter",
        "SxCPKrea2PoseVariant",
        "SxCPKrea2POVPenetrationFilter",
        "SxCPKrea2POVOralFilter",
        "SxCPKrea2POVOutercourseFilter",
        "SxCPKrea2POVManualFilter",
        "SxCPKrea2POVToyFilter",
        "SxCPKrea2POVClimaxFilter",
        "SxCPKrea2POVInteractionFilter",
        "SxCPKrea2POVPromptRestore",
        "SxCPKrea2VariantEvidence",
    ]
    for node_name in required_nodes:
        _expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
        _expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")

    position_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPHardcorePositionPool"]
    position_inputs = position_node.INPUT_TYPES().get("required") or {}
    _expect("family" in position_inputs, "Hardcore Position Pool lost family input")
    _expect("tooltip" in position_inputs["family"][1], "Hardcore Position Pool tooltip injection missing")
    pool_config, pool_summary = position_node().build(
        "replace",
        "oral",
        "",
        include_boobjob=True,
        include_handjob=True,
    )
    parsed_pool = json.loads(pool_config)
    _expect(parsed_pool.get("family") == "oral", "Hardcore Position Pool lost selected family")
    _expect(parsed_pool.get("positions") == ["boobjob", "handjob"], "Hardcore Position Pool lost selected positions")
    _expect("positions=boobjob,handjob" in pool_summary, "Hardcore Position Pool summary changed unexpectedly")

    filter_config, filter_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPHardcoreActionFilter"]().build(
        "outercourse_only",
        False,
        False,
        False,
        True,
        True,
        True,
        False,
        True,
        False,
        False,
        pool_config,
    )
    parsed_filter = json.loads(filter_config)
    _expect(parsed_filter.get("family") == "outercourse", "Hardcore Action Filter did not apply focus family")
    _expect(parsed_filter.get("positions") == ["boobjob", "handjob"], "Hardcore Action Filter lost incoming positions")
    _expect(parsed_filter.get("allow_penetration") is False, "Hardcore Action Filter did not block penetration")
    _expect(parsed_filter.get("allow_outercourse") is True, "Hardcore Action Filter should allow outercourse")
    _expect("blocked=" in filter_summary, "Hardcore Action Filter summary lost blocked-gate details")

    variant_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2PoseVariant"]
    variant_inputs = variant_node.INPUT_TYPES().get("required") or {}
    _expect("variant_key" in variant_inputs, "Krea2 Pose Variant lost variant selector")
    _expect("tooltip" in variant_inputs["variant_key"][1], "Krea2 Pose Variant tooltip injection missing")
    _expect("pov_boobjob_upright_cleavage" in variant_inputs["variant_key"][0], "Krea2 Pose Variant lost boobjob option")
    variant_config, variant_key, prompt_cues, avoid_cues, variant_summary, variant_json = variant_node().build(
        "pov_boobjob_upright_cleavage",
        "replace",
        "",
    )
    parsed_variant_config = json.loads(variant_config)
    parsed_variant = json.loads(variant_json)
    _expect(variant_key == "pov_boobjob_upright_cleavage", "Krea2 Pose Variant returned wrong key")
    _expect(parsed_variant_config.get("positions") == ["boobjob"], "Krea2 Pose Variant did not map to boobjob position config")
    _expect(parsed_variant.get("status") == "proven", "Krea2 Pose Variant lost status metadata")
    _expect("pressed-together breasts" in prompt_cues, "Krea2 Pose Variant lost prompt cues output")
    _expect("torso bent forward" in avoid_cues, "Krea2 Pose Variant lost avoid cues output")
    _expect("variant=pov_boobjob_upright_cleavage" in variant_summary, "Krea2 Pose Variant summary lost key")

    oral_filter = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2POVOralFilter"]
    synthetic_variant = {
        "key": "pov_synthetic_node_seeded_oral",
        "family": "oral",
        "status": "candidate",
        "action_family": "oral",
        "position_keys": ["top_down_oral"],
        "canonical_geometry": "synthetic seeded oral atlas geometry",
        "prompt_cues": ["synthetic baseline oral atlas cue"],
        "prompt_variant_cues": [
            ["synthetic alternate oral atlas cue"],
            {"prompt_cues": ["synthetic second oral atlas cue"]},
        ],
        "avoid_cues": ["synthetic avoid cue"],
    }
    original_get_variant = krea2_pose_variant_catalog.get_variant
    original_variants = krea2_pose_variant_catalog.variants
    try:
        def fake_get_variant(key: str, **kwargs):
            if key == "pov_synthetic_node_seeded_oral":
                return dict(synthetic_variant)
            return original_get_variant(key, **kwargs)

        def fake_variants(*, status=None, family=None, action_family=None, path=None):
            if action_family == "oral":
                return [dict(synthetic_variant)]
            return original_variants(status=status, family=family, action_family=action_family, path=path)

        krea2_pose_variant_catalog.get_variant = fake_get_variant
        krea2_pose_variant_catalog.variants = fake_variants

        seeded_config_a, _seeded_key_a, _seeded_cues_a, _seeded_avoid_a, seeded_summary_a, _seeded_json_a = (
            variant_node().build(
                "pov_synthetic_node_seeded_oral",
                "replace",
                "",
                atlas_cue_seed=901,
            )
        )
        seeded_config_b, _seeded_key_b, _seeded_cues_b, _seeded_avoid_b, seeded_summary_b, _seeded_json_b = (
            variant_node().build(
                "pov_synthetic_node_seeded_oral",
                "replace",
                "",
                atlas_cue_seed=901,
            )
        )
        parsed_seeded_a = json.loads(seeded_config_a)
        parsed_seeded_b = json.loads(seeded_config_b)
        seeded_index = parsed_seeded_a.get("krea2_prompt_variant_indices", {}).get("pov_synthetic_node_seeded_oral")
        _expect(
            parsed_seeded_a.get("krea2_variant_keys") == ["pov_synthetic_node_seeded_oral"],
            "Krea2 Pose Variant should write selected atlas variant metadata",
        )
        _expect(
            isinstance(seeded_index, int) and 0 <= seeded_index < 3,
            f"Krea2 Pose Variant should store a seeded prompt cue index, got {seeded_index!r}",
        )
        _expect(
            parsed_seeded_b.get("krea2_prompt_variant_indices", {}).get("pov_synthetic_node_seeded_oral") == seeded_index,
            "Krea2 Pose Variant atlas cue seed should be deterministic for the same seed",
        )
        _expect(
            parsed_seeded_a.get("krea2_prompt_variant_seed") == 901,
            "Krea2 Pose Variant should record atlas cue seed provenance",
        )
        _expect(
            "cue_seed=901" in seeded_summary_a and "cue_indices=pov_synthetic_node_seeded_oral:" in seeded_summary_a,
            "Krea2 Pose Variant summary should expose atlas cue seed and selected cue index",
        )

        include_synthetic = _atlas_variant_include_key("pov_synthetic_node_seeded_oral")
        synthetic_filter_config, synthetic_filter_keys, _synthetic_positions, _synthetic_notes, synthetic_filter_summary, _ = (
            oral_filter().build(
                "replace",
                "",
                atlas_cue_seed=902,
                **{include_synthetic: True},
            )
        )
        parsed_filter_seeded = json.loads(synthetic_filter_config)
        _expect(
            synthetic_filter_keys == "pov_synthetic_node_seeded_oral",
            "Krea2 POV filter should select the synthetic seeded variant",
        )
        _expect(
            parsed_filter_seeded.get("krea2_prompt_variant_seed") == 902,
            "Krea2 POV filter should record atlas cue seed provenance",
        )
        _expect(
            "cue_seed=902" in synthetic_filter_summary,
            "Krea2 POV filter summary should expose atlas cue seed",
        )
    finally:
        krea2_pose_variant_catalog.get_variant = original_get_variant
        krea2_pose_variant_catalog.variants = original_variants

    penetration_filter = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2POVPenetrationFilter"]
    penetration_inputs = penetration_filter.INPUT_TYPES().get("required") or {}
    _expect("include_doggy_top_down_rear_entry" in penetration_inputs, "POV Penetration Filter lost doggy atlas checkbox")
    _expect(
        "include_blowjob_side_profile_oral" not in penetration_inputs,
        "POV Penetration Filter should not expose oral atlas checkboxes",
    )
    oral_inputs = oral_filter.INPUT_TYPES().get("required") or {}
    _expect("include_blowjob_side_profile_oral" in oral_inputs, "POV Oral Filter lost blowjob side-profile checkbox")
    _expect(
        "include_doggy_top_down_rear_entry" not in oral_inputs,
        "POV Oral Filter should not expose penetration atlas checkboxes",
    )
    top_config, top_keys, top_positions, top_notes, top_summary, _top_variants_json = oral_filter().build(
        "replace",
        "",
        include_blowjob_top_down_vertical_shaft=True,
    )
    parsed_top_config = json.loads(top_config)
    _expect(top_keys == "pov_blowjob_top_down_vertical_shaft", "POV Oral Filter returned wrong exact top-view variant key")
    _expect(
        parsed_top_config.get("krea2_variant_keys") == ["pov_blowjob_top_down_vertical_shaft"],
        "POV Oral Filter lost exact top-view variant metadata",
    )
    _expect("kneeling" in top_positions, "POV Oral Filter lost top-view route position")
    _expect(
        "nadir-angle" not in top_notes and "viewer looks almost straight down" not in top_notes,
        "POV Oral Filter should not emit raw atlas prompt prose as a connectable text output",
    )
    _expect("variants=pov_blowjob_top_down_vertical_shaft" in top_summary, "POV Oral Filter summary lost top-view variant key")

    sitting_config, sitting_keys, sitting_positions, _sitting_notes, sitting_summary, _sitting_variants_json = oral_filter().build(
        "replace",
        "",
        include_blowjob_sitting_upright_oral=True,
    )
    parsed_sitting_config = json.loads(sitting_config)
    _expect(sitting_keys == "pov_blowjob_sitting_upright_oral", "POV Oral Filter returned wrong exact sitting oral variant key")
    _expect(
        parsed_sitting_config.get("krea2_variant_keys") == ["pov_blowjob_sitting_upright_oral"],
        "POV Oral Filter lost exact sitting oral variant metadata",
    )
    _expect(
        "blowjob_sitting" in (parsed_sitting_config.get("positions") or []),
        "POV Oral Filter dropped sitting oral route position from config",
    )
    _expect("blowjob_sitting" in sitting_positions.split(","), "POV Oral Filter dropped sitting oral route position summary")
    _expect("variants=pov_blowjob_sitting_upright_oral" in sitting_summary, "POV Oral Filter summary lost sitting oral variant key")

    doggy_config, doggy_keys, doggy_positions, doggy_notes, doggy_summary, doggy_variants_json = penetration_filter().build(
        "replace",
        "",
        include_doggy_top_down_rear_entry=True,
    )
    parsed_doggy_config = json.loads(doggy_config)
    _expect(parsed_doggy_config.get("family") == "penetrative", "POV Penetration Filter should constrain single-family output")
    _expect(parsed_doggy_config.get("positions") == ["doggy"], "POV Penetration Filter did not map doggy variant to position key")
    _expect(
        parsed_doggy_config.get("krea2_variant_keys") == ["pov_doggy_top_down_rear_entry"],
        "POV Penetration Filter lost exact selected variant metadata",
    )
    _expect(doggy_keys == "pov_doggy_top_down_rear_entry", "POV Penetration Filter returned wrong selected variant keys")
    _expect(doggy_positions == "doggy", "POV Penetration Filter returned wrong selected positions")
    _expect("pov_doggy_top_down_rear_entry" in doggy_notes and "top-down POV" not in doggy_notes, "POV Penetration Filter should emit compact variant notes, not raw prompt cues")
    _expect("variants=pov_doggy_top_down_rear_entry" in doggy_summary, "POV Penetration Filter summary lost selected variant")
    _expect(json.loads(doggy_variants_json)[0].get("key") == "pov_doggy_top_down_rear_entry", "POV Penetration Filter returned wrong variant JSON")

    mixed_config, mixed_keys, mixed_positions, _mixed_cues, mixed_summary, _mixed_variants_json = oral_filter().build(
        "add",
        doggy_config,
        include_blowjob_side_profile_oral=True,
    )
    parsed_mixed_config = json.loads(mixed_config)
    _expect(parsed_mixed_config.get("family") == "any", "Chained POV filters should use any family for mixed categories")
    _expect(
        parsed_mixed_config.get("positions") == ["doggy", "side_lying", "reclining_oral", "penis_licking"],
        "Chained POV filters did not preserve doggy and blowjob position keys",
    )
    _expect(
        parsed_mixed_config.get("krea2_variant_keys") == [
            "pov_doggy_top_down_rear_entry",
            "pov_blowjob_side_profile_oral",
        ],
        "Chained POV filters lost exact selected variant metadata",
    )
    _expect(
        mixed_keys == "pov_doggy_top_down_rear_entry,pov_blowjob_side_profile_oral",
        "Chained POV filters returned wrong selected variant key list",
    )
    _expect("doggy" in mixed_positions and "penis_licking" in mixed_positions, "Chained POV filters returned wrong positions summary")
    _expect("family=any" in mixed_summary, "Chained POV filters summary should show mixed family")

    restore_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2POVPromptRestore"]
    restore_inputs = restore_node.INPUT_TYPES().get("required") or {}
    restore_optional = restore_node.INPUT_TYPES().get("optional") or {}
    _expect(
        "hardcore_position_config" not in restore_inputs,
        "Krea2 POV Prompt Restore should not require an incoming position config connection",
    )
    _expect(
        "hardcore_position_config" in restore_optional,
        "Krea2 POV Prompt Restore should expose incoming position config as an optional connection",
    )
    for key in (
        "restore_clothing_detail",
        "restore_face_expression_detail",
        "restore_body_touch_detail",
        "restore_camera_presentation_detail",
        "relax_non_pose_axis_conflicts",
    ):
        _expect(key in restore_inputs, f"Krea2 POV Prompt Restore lost input {key}")
    standalone_restore_config, standalone_restore_summary = restore_node().build(
        True,
        True,
        False,
        False,
        True,
        "",
    )
    parsed_standalone_restore = json.loads(standalone_restore_config)
    _expect(
        parsed_standalone_restore.get("restore_prompt_axes") == [
            "clothing_detail",
            "face_detail",
            "expression_detail",
            "mouth_detail",
            "reaction_detail",
        ],
        "Krea2 POV Prompt Restore should emit restore axes without an incoming connection",
    )
    _expect(
        "restore_axes=clothing_detail,face_detail" in standalone_restore_summary,
        "Krea2 POV Prompt Restore standalone summary lost restored axes",
    )
    pre_restore_config, _pre_restore_summary = restore_node().build(
        True,
        True,
        False,
        False,
        True,
        "",
    )
    pre_restored_top_config, _pre_restored_top_keys, _pre_restored_top_positions, _, _, _ = oral_filter().build(
        "replace",
        pre_restore_config,
        include_blowjob_top_down_vertical_shaft=True,
    )
    parsed_pre_restored_top = json.loads(pre_restored_top_config)
    _expect(
        parsed_pre_restored_top.get("krea2_variant_keys") == ["pov_blowjob_top_down_vertical_shaft"],
        "Restore-before-filter should keep the selected top-view atlas variant metadata",
    )
    _expect(
        parsed_pre_restored_top.get("restore_prompt_axes") == [
            "clothing_detail",
            "face_detail",
            "expression_detail",
            "mouth_detail",
            "reaction_detail",
        ],
        "Restore-before-filter should preserve restore axes through replace-mode pose filtering",
    )
    _expect(
        parsed_pre_restored_top.get("relax_non_pose_axis_conflicts") is True,
        "Restore-before-filter should preserve non-pose conflict relaxation through replace-mode pose filtering",
    )
    restored_config, restored_summary = restore_node().build(
        True,
        True,
        False,
        False,
        True,
        doggy_config,
    )
    parsed_restored_config = json.loads(restored_config)
    _expect(
        parsed_restored_config.get("krea2_variant_keys") == ["pov_doggy_top_down_rear_entry"],
        "Krea2 POV Prompt Restore should preserve selected atlas variant metadata",
    )
    _expect(parsed_restored_config.get("positions") == ["doggy"], "Krea2 POV Prompt Restore should preserve selected pose lock")
    _expect(parsed_restored_config.get("family") == "penetrative", "Krea2 POV Prompt Restore should preserve atlas family lock")
    _expect(
        parsed_restored_config.get("restore_prompt_axes") == [
            "clothing_detail",
            "face_detail",
            "expression_detail",
            "mouth_detail",
            "reaction_detail",
        ],
        "Krea2 POV Prompt Restore should record requested prompt axes",
    )
    _expect(
        parsed_restored_config.get("relax_non_pose_axis_conflicts") is True,
        "Krea2 POV Prompt Restore should enable non-pose conflict relaxation",
    )
    _expect("restore_axes=clothing_detail,face_detail" in restored_summary, "Krea2 POV Prompt Restore summary lost restored axes")
    parsed_restored = hardcore_position_config.parse_hardcore_position_config(parsed_restored_config)
    conflicting_clothing = {"text": "shirt pulled open while standing", "position_keys": ["standing"]}
    neutral_clothing = {"text": "shirt pulled open at the hips"}
    restored_clothing = hardcore_position_config.filter_hardcore_axis(
        "clothing_detail",
        [conflicting_clothing, neutral_clothing],
        parsed_restored,
    )
    _expect(
        restored_clothing == [conflicting_clothing, neutral_clothing],
        "Krea2 POV Prompt Restore should allow restored clothing detail through non-pose conflict pruning",
    )
    unrestored_hand = hardcore_position_config.filter_hardcore_axis(
        "hand_detail",
        [conflicting_clothing, neutral_clothing],
        parsed_restored,
    )
    _expect(
        unrestored_hand == [neutral_clothing],
        "Krea2 POV Prompt Restore should not relax unrequested non-pose axes",
    )
    restored_position = hardcore_position_config.filter_hardcore_axis(
        "position",
        [
            {"text": "standing sex position", "position_keys": ["standing"]},
            {"text": "doggy style position", "position_keys": ["doggy"]},
        ],
        parsed_restored,
    )
    _expect(
        restored_position == [{"text": "doggy style position", "position_keys": ["doggy"]}],
        "Krea2 POV Prompt Restore should keep position axes locked to the atlas pose",
    )

    evidence_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2VariantEvidence"]
    evidence_inputs = evidence_node.INPUT_TYPES().get("required") or {}
    _expect("variant_key" in evidence_inputs, "Krea2 Variant Evidence lost variant selector")
    _expect("tooltip" in evidence_inputs["variant_key"][1], "Krea2 Variant Evidence tooltip injection missing")
    (
        evidence_summary,
        baseline_image,
        candidate_image,
        evidence_json,
        evidence_seed,
        evidence_decision,
    ) = evidence_node().build(
        "pov_boobjob_upright_cleavage",
        "accepted",
        "",
    )
    parsed_evidence = json.loads(evidence_json)
    _expect(evidence_seed == 7302, "Krea2 Variant Evidence returned wrong fixed seed")
    _expect(evidence_decision == "generator_patch", "Krea2 Variant Evidence returned wrong decision")
    _expect("boobjob-7302" in evidence_summary, "Krea2 Variant Evidence summary lost entry id")
    _expect(baseline_image.endswith(".png") and candidate_image.endswith(".png"), "Krea2 Variant Evidence lost image paths")
    _expect(parsed_evidence.get("variant_key") == "pov_boobjob_upright_cleavage", "Krea2 Variant Evidence returned wrong JSON")


def smoke_node_formatter_registration() -> None:
    required_nodes = [
        "SxCPCaptionNaturalizer",
        "SxCPKrea2Formatter",
        "SxCPSDXLFormatter",
    ]
    for node_name in required_nodes:
        _expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
        _expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")

    krea_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPKrea2Formatter"]
    krea_inputs = krea_node.INPUT_TYPES().get("required") or {}
    _expect("source_text" in krea_inputs, "Krea2 Formatter lost source_text input")
    _expect("tooltip" in krea_inputs["source_text"][1], "Krea2 Formatter tooltip injection missing")
    krea_optional = krea_node.INPUT_TYPES().get("optional") or {}
    _expect("source_text_input" in krea_optional, "Krea2 Formatter lost connectable source_text_input")
    _expect("metadata_json" in krea_optional, "Krea2 Formatter lost metadata_json input")
    _expect(krea_optional["metadata_json"][1].get("forceInput") is True, "Krea2 Formatter metadata_json should be connectable")
    _expect(krea_optional["negative_prompt"][1].get("forceInput") is True, "Krea2 Formatter negative_prompt should be connectable")

    caption, caption_method, caption_trace_json = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCaptionNaturalizer"]().build(
        "A woman standing by a window, best quality",
        "caption_or_prompt",
        "manual_controls",
        "concise",
        "drop_style_tail",
        "sxcppnl7",
        True,
    )
    _expect_text("node_formatter.caption", caption, 20)
    _expect(caption.startswith("sxcppnl7"), "Caption Naturalizer did not prepend trigger")
    _expect("text(" in caption_method, "Caption Naturalizer method changed unexpectedly")
    caption_trace = json.loads(_expect_text("node_formatter.caption_trace", caption_trace_json, 20))
    _expect(caption_trace.get("formatter") == "caption", "Caption Naturalizer trace lost formatter")
    _expect(caption_trace.get("branch") == "text", "Caption Naturalizer trace lost text branch")
    caption_inputs = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCaptionNaturalizer"].INPUT_TYPES().get("required") or {}
    _expect("caption_profile" in caption_inputs, "Caption Naturalizer lost caption_profile input")
    _expect("target" in caption_inputs, "Caption Naturalizer lost target input")
    _expect(caption_inputs["target"][0] == formatter_target.target_choices(), "Caption Naturalizer target choices drifted")
    _expect(caption_inputs["detail_level"][0] == formatter_detail.detail_level_choices(), "Caption Naturalizer detail choices drifted")
    _expect(caption_inputs["style_policy"][0] == caption_policy.style_policy_choices(), "Caption Naturalizer style choices drifted")
    _expect("tooltip" in caption_inputs["caption_profile"][1], "Caption profile tooltip injection missing")
    _expect(krea_inputs["target"][0] == formatter_target.target_choices(), "Krea2 Formatter target choices drifted")
    _expect(krea_inputs["detail_level"][0] == formatter_detail.detail_level_choices(), "Krea2 Formatter detail choices drifted")
    _expect(krea_inputs["style_mode"][0] == krea_format_route.style_mode_choices(), "Krea2 Formatter style choices drifted")

    krea_output = krea_node().build(
        "sxcppnl7 A woman standing by a window",
        "prompt",
        "single",
        "concise",
        "preserve",
        True,
    )
    _expect_text("node_formatter.krea_prompt", krea_output[0], 20)
    _expect("sxcppnl7" in krea_output[0], "Krea2 Formatter did not preserve trigger when enabled")
    _expect(krea_output[6].startswith("text("), "Krea2 Formatter method changed unexpectedly")
    krea_trace = json.loads(_expect_text("node_formatter.krea_trace", krea_output[7], 20))
    _expect(krea_trace.get("formatter") == "krea2", "Krea2 Formatter trace lost formatter")
    _expect(krea_trace.get("branch") == "fallback", "Krea2 Formatter trace lost fallback branch")

    sdxl_output = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSDXLFormatter"]().build(
        "A woman standing by a window",
        "prompt",
        "single",
        "manual_controls",
        "flat_vector_pony",
        "pony_high",
        "mythp0rt",
        True,
        False,
        1.29,
    )
    _expect_text("node_formatter.sdxl_prompt", sdxl_output[0], 40)
    _expect_trigger_once("node_formatter.sdxl_prompt", sdxl_output[0], "mythp0rt")
    _expect_text("node_formatter.sdxl_negative", sdxl_output[1], 20)
    _expect(sdxl_output[6].startswith("text("), "SDXL Formatter method changed unexpectedly")
    sdxl_trace = json.loads(_expect_text("node_formatter.sdxl_trace", sdxl_output[7], 20))
    _expect(sdxl_trace.get("formatter") == "sdxl", "SDXL Formatter trace lost formatter")
    _expect(sdxl_trace.get("branch") == "fallback", "SDXL Formatter trace lost fallback branch")
    sdxl_inputs = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSDXLFormatter"].INPUT_TYPES().get("required") or {}
    _expect("formatter_profile" in sdxl_inputs, "SDXL Formatter lost formatter_profile input")
    _expect(sdxl_inputs["target"][0] == formatter_target.target_choices(), "SDXL Formatter target choices drifted")
    _expect("tooltip" in sdxl_inputs["formatter_profile"][1], "SDXL formatter_profile tooltip injection missing")

    pair = pb.build_insta_of_pair(
        row_number=1,
        start_index=1,
        seed=4311,
        ethnicity="any",
        figure="random",
        no_plus_women=False,
        no_black=False,
        trigger=Trigger,
        prepend_trigger_to_prompt=True,
        options_json=_insta_options(),
        character_cast=_character_cast(),
        hardcore_position_config=_action_filter("penetration_only"),
    )
    pair_metadata = _json(pair)

    expected_krea_pair = krea_formatter.format_krea2_prompt(
        "",
        metadata_json=pair_metadata,
        input_hint="metadata_json",
        target="hardcore",
        detail_level="balanced",
        style_mode="preserve",
        extra_positive="node krea pair marker",
        extra_negative="node krea pair negative",
    )
    node_krea_pair = krea_node().build(
        "",
        "metadata_json",
        "hardcore",
        "balanced",
        "preserve",
        False,
        metadata_json=pair_metadata,
        extra_positive="node krea pair marker",
        extra_negative="node krea pair negative",
    )
    _expect(
        node_krea_pair
        == (
            expected_krea_pair["krea_prompt"],
            expected_krea_pair["negative_prompt"],
            expected_krea_pair["krea_softcore_prompt"],
            expected_krea_pair["krea_hardcore_prompt"],
            expected_krea_pair["softcore_negative_prompt"],
            expected_krea_pair["hardcore_negative_prompt"],
            expected_krea_pair["method"],
            expected_krea_pair["route_trace_json"],
        ),
        "Krea2 Formatter node output drifted from public metadata formatter",
    )

    expected_sdxl_pair = sdxl_formatter.format_sdxl_prompt(
        "",
        metadata_json=pair_metadata,
        input_hint="metadata_json",
        target="hardcore",
        formatter_profile="manual_controls",
        style_preset="flat_vector_pony",
        quality_preset="pony_high",
        trigger=SdxlTrigger,
        prepend_trigger=True,
        preserve_trigger=False,
        nude_weight=1.29,
        extra_positive="node sdxl pair marker",
        extra_negative="node sdxl pair negative",
    )
    node_sdxl_pair = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPSDXLFormatter"]().build(
        "",
        "metadata_json",
        "hardcore",
        "manual_controls",
        "flat_vector_pony",
        "pony_high",
        SdxlTrigger,
        True,
        False,
        1.29,
        metadata_json=pair_metadata,
        extra_positive="node sdxl pair marker",
        extra_negative="node sdxl pair negative",
    )
    _expect(
        node_sdxl_pair
        == (
            expected_sdxl_pair["sdxl_prompt"],
            expected_sdxl_pair["negative_prompt"],
            expected_sdxl_pair["sdxl_softcore_prompt"],
            expected_sdxl_pair["sdxl_hardcore_prompt"],
            expected_sdxl_pair["softcore_negative_prompt"],
            expected_sdxl_pair["hardcore_negative_prompt"],
            expected_sdxl_pair["method"],
            expected_sdxl_pair["route_trace_json"],
        ),
        "SDXL Formatter node output drifted from public metadata formatter",
    )

    expected_caption_pair = caption_naturalizer.naturalize_caption_with_trace(
        "",
        metadata_json=pair_metadata,
        input_hint="metadata_json",
        target="hardcore",
        trigger=Trigger,
        include_trigger=True,
        detail_level="balanced",
        style_policy="drop_style_tail",
        caption_profile="training_dense",
    )
    node_caption_pair = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPCaptionNaturalizer"]().build(
        "",
        "metadata_json",
        "training_dense",
        "balanced",
        "drop_style_tail",
        Trigger,
        True,
        "hardcore",
        metadata_json=pair_metadata,
    )
    _expect(
        node_caption_pair == expected_caption_pair,
        "Caption Naturalizer node output drifted from public metadata formatter",
    )


def smoke_node_insta_registration() -> None:
    required_nodes = [
        "SxCPInstaOFOptions",
        "SxCPInstaOFPromptPair",
    ]
    for node_name in required_nodes:
        _expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
        _expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")

    options_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPInstaOFOptions"]
    options_inputs = options_node.INPUT_TYPES().get("required") or {}
    _expect("hardcore_detail_density" in options_inputs, "Insta/OF Options lost hardcore_detail_density input")
    _expect("tooltip" in options_inputs["hardcore_detail_density"][1], "Insta/OF Options tooltip injection missing")
    options_json = options_node().build(
        "same_as_hardcore",
        "couple",
        1,
        1,
        "lingerie_tease",
        "hardcore",
        True,
        True,
        0.45,
        0.85,
        "hybrid",
        "same_creator_same_room",
        "explicit_nude",
        "standard",
        "standard",
        "compact",
        "balanced",
    )[0]
    parsed_options = json.loads(options_json)
    _expect(parsed_options.get("softcore_cast") == "same_as_hardcore", "Insta/OF Options lost softcore cast")
    _expect(parsed_options.get("hardcore_cast") == "couple", "Insta/OF Options lost hardcore cast")
    _expect(parsed_options.get("hardcore_clothing_continuity") == "explicit_nude", "Insta/OF Options lost clothing continuity")

    pair_output = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPInstaOFPromptPair"]().build(
        1,
        41,
        123,
        "any",
        "random",
        Trigger,
        True,
        options_json=options_json,
    )
    _expect_text("node_insta.softcore_prompt", pair_output[0], 20)
    _expect_text("node_insta.hardcore_prompt", pair_output[1], 20)
    pair = json.loads(pair_output[7])
    _expect_pair(pair, "node_insta_pair")
    _expect(pair.get("options", {}).get("hardcore_cast") == "couple", "Insta/OF Prompt Pair lost options metadata")


def smoke_node_scene_chain_registration() -> None:
    required_nodes = [
        "SxCPSceneLayerSeedOptions",
        "SxCPSceneCastOptions",
        "SxCPSceneCharacterOptions",
        "SxCPSceneWardrobeOptions",
        "SxCPSceneLocationLayoutOptions",
        "SxCPSceneSetDressingOptions",
        "SxCPSceneBlockingOptions",
        "SxCPSceneActionOptions",
        "SxCPScenePerformanceOptions",
        "SxCPSceneCameraOptions",
        "SxCPSceneCompositionOptions",
        "SxCPSceneLightingOptions",
        "SxCPSceneBranchOptions",
        "SxCPSceneStart",
        "SxCPSceneCast",
        "SxCPSceneCharacter",
        "SxCPSceneWardrobe",
        "SxCPSceneLocation",
        "SxCPSceneSetDressing",
        "SxCPSceneBlocking",
        "SxCPSceneAction",
        "SxCPScenePerformance",
        "SxCPSceneCamera",
        "SxCPSceneComposition",
        "SxCPSceneLighting",
        "SxCPSceneBranchPair",
        "SxCPSoftcoreBranchOptions",
        "SxCPHardcoreBranchOptions",
        "SxCPSceneOutput",
        "SxCPScenePairOutput",
        "SxCPChoiceBoard",
    ]
    for node_name in required_nodes:
        _expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
        _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,
        777,
        "raw",
        "woman",
        "random",
        "balanced",
        Trigger,
        True,
    )
    _expect("scene v" in start_summary, "Scene Start summary changed unexpectedly")
    parsed_scene = json.loads(scene)
    _expect(parsed_scene.get("schema") == "sxcp_scene_v2", "Scene Start did not emit v2 schema")

    cast_options = nodes["SxCPSceneCastOptions"]().build("replace", "mixed_couple", 1, 1, "woman_a", "none")[0]
    scene, _cast_config, _cast_summary, _cast_metadata = nodes["SxCPSceneCast"]().build(
        scene,
        "mixed_couple",
        1,
        1,
        "woman_a",
        "none",
        cast_options=cast_options,
    )
    character_options = nodes["SxCPSceneCharacterOptions"]().build(
        "replace",
        "medium",
        "visible",
        "enabled",
        0.5,
        -1,
        -1,
        "controlled slot performance note",
    )[0]
    scene, character_cast, _slot, _summary, _metadata = nodes["SxCPSceneCharacter"]().build(
        scene,
        True,
        "woman",
        "A",
        -1,
        "25-year-old adult",
        "random",
        "random",
        "random",
        "medium",
        True,
        0.5,
        "visible",
        -1,
        -1,
        character_options=character_options,
    )
    scene, character_cast, _slot, _summary, _metadata = nodes["SxCPSceneCharacter"]().build(
        scene,
        True,
        "man",
        "A",
        -1,
        "40-year-old adult",
        "random",
        "random",
        "average",
        "compact",
        True,
        0.5,
        "visible",
        -1,
        -1,
    )
    seed_fixture_scene = scene
    wardrobe_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build("wardrobe", "fixed", 9981, "content", "same_for_all_rows", "replace_layer")[0]
    wardrobe_options = nodes["SxCPSceneWardrobeOptions"]().build(
        "replace",
        "woman",
        "A",
        "full",
        "explicit_nude",
        True,
        "simple black dress",
        "",
        "thin necklace",
        "",
    )[0]
    scene, character_cast, _wardrobe_summary, _wardrobe_metadata = nodes["SxCPSceneWardrobe"]().build(
        scene,
        True,
        "woman",
        "A",
        "full",
        "simple black dress",
        "fully nude",
        "",
        wardrobe_options=wardrobe_options,
        seed_options=wardrobe_seed_options,
    )
    slots = json.loads(character_cast).get("slots") or []
    woman_slot = next(slot for slot in slots if slot.get("subject_type") == "woman")
    _expect(woman_slot.get("softcore_outfit") == "simple black dress", "Scene Wardrobe did not update softcore outfit")
    _expect(woman_slot.get("hardcore_clothing") == "fully nude", "Scene Wardrobe did not update hardcore clothing")
    _expect(json.loads(scene).get("seed_trace", {}).get("wardrobe", {}).get("seed") == 9981, "Scene Wardrobe seed options did not write seed trace")

    location_options = nodes["SxCPSceneLocationLayoutOptions"]().build(
        "replace",
        "mirror edge",
        "soft curtain layer",
        "repeating lamp reflections",
        "private",
        "private",
        "warm mirror-room geometry",
    )[0]
    scene = nodes["SxCPSceneLocation"]().build(
        scene,
        True,
        "replace",
        "custom_only",
        "quiet studio room with a large mirror",
        "",
        location_options=location_options,
    )[0]
    set_options = nodes["SxCPSceneSetDressingOptions"]().build("replace", "mirror edge", "soft curtains", "small lamp", "warm fabric texture", "")[0]
    scene = nodes["SxCPSceneSetDressing"]().build(scene, True, "mirror edge", "soft curtains", "small lamp", "", set_options=set_options)[0]
    blocking_options = nodes["SxCPSceneBlockingOptions"]().build(
        "replace",
        "standing",
        "woman near mirror",
        "man behind her",
        "three_quarter",
        "foreground",
        "standing close",
        "",
    )[0]
    scene = nodes["SxCPSceneBlocking"]().build(scene, True, "standing", "woman near mirror", "man behind her", "", blocking_options=blocking_options)[0]
    action_options = nodes["SxCPSceneActionOptions"]().build("replace", "softcore", "softcore_tease", "no_change", "quiet pose transition")[0]
    scene = nodes["SxCPSceneAction"]().build(scene, True, "regular", "no_change", "", action_options=action_options)[0]
    performance_options = nodes["SxCPScenePerformanceOptions"]().build("replace", "enabled", "fixed", 0.4, "camera", "on_body", "posed", "controlled eye contact")[0]
    scene = nodes["SxCPScenePerformance"]().build(scene, True, "fixed", 0.4, "controlled eye contact", performance_options=performance_options)[0]
    camera_options = nodes["SxCPSceneCameraOptions"]().build("replace", "from_camera_config", True, "mirror-aware camera note")[0]
    scene = nodes["SxCPSceneCamera"]().build(
        scene,
        True,
        "standard",
        "three_quarter",
        "eye_level",
        "auto",
        "auto",
        "auto",
        "auto",
        "strong",
        "compact",
        "",
        camera_options=camera_options,
    )[0]
    composition_options = nodes["SxCPSceneCompositionOptions"]().build("replace", "body", "three_quarter", "clear", "mirror-aware three-quarter frame")[0]
    scene = nodes["SxCPSceneComposition"]().build(scene, True, "replace", "no_outfit_check", "", "", composition_options=composition_options)[0]
    lighting_options = nodes["SxCPSceneLightingOptions"]().build("replace", "practical_lamps", "soft", "medium", "warm", "evening", "")[0]
    scene = nodes["SxCPSceneLighting"]().build(scene, True, "practical_lamps", "soft", "medium", "warm", "", lighting_options=lighting_options)[0]

    output = nodes["SxCPSceneOutput"]().build(scene)
    _expect_text("node_scene_chain.prompt", output[0], 40)
    _expect_trigger_once("node_scene_chain.prompt", output[0], Trigger)
    row = json.loads(output[3])
    _expect(row.get("scene_chain", {}).get("schema") == "sxcp_scene_v2", "Scene Output lost scene_chain metadata")

    branch_options = nodes["SxCPSceneBranchOptions"]().build("replace", "both", "same_creator_same_room", "hybrid", "shared branch note")[0]
    branch_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build("hardcore_branch", "fixed", 7799, "pose", "same_for_all_rows", "replace_layer")[0]
    soft_scene, hard_scene, _branch_summary, _branch_metadata = nodes["SxCPSceneBranchPair"]().build(
        scene,
        "same_creator_same_room",
        "hybrid",
        branch_options=branch_options,
        seed_options=branch_seed_options,
    )
    soft_scene = nodes["SxCPSoftcoreBranchOptions"]().build(
        soft_scene,
        "same_as_hardcore",
        "lingerie_tease",
        True,
        0.45,
        "from_camera_config",
        "compact",
        "",
        branch_options=branch_options,
    )[0]
    hard_scene = nodes["SxCPHardcoreBranchOptions"]().build(
        hard_scene,
        "couple",
        1,
        1,
        "hardcore",
        True,
        0.85,
        "explicit_nude",
        "from_camera_config",
        "compact",
        "balanced",
        "",
        branch_options=branch_options,
        seed_options=branch_seed_options,
    )[0]
    pair_output = nodes["SxCPScenePairOutput"]().build(soft_scene, hard_scene)
    _expect_text("node_scene_chain.softcore_prompt", pair_output[0], 40)
    _expect_text("node_scene_chain.hardcore_prompt", pair_output[1], 40)
    pair = json.loads(pair_output[7])
    _expect_pair(pair, "node_scene_chain_pair")
    _expect(pair.get("options", {}).get("hardcore_cast") == "couple", "Scene Pair Output lost hardcore branch options")
    _expect("scene_chain" in pair, "Scene Pair Output lost scene_chain metadata")
    _expect(
        pair.get("scene_chain", {}).get("hardcore", {}).get("seed_trace", {}).get("hardcore.hardcore_branch", {}).get("seed") == 7799,
        "Scene branch seed options did not write hardcore branch seed trace",
    )
    hard_seed_config = pair.get("hardcore_row", {}).get("seed_config") if isinstance(pair.get("hardcore_row"), dict) else {}
    _expect(hard_seed_config.get("pose_seed") == 7799, "Scene Pair Output did not pass hardcore branch pose seed to generator")
    _expect(hard_seed_config.get("role_seed") == 7799, "Scene Pair Output did not pass hardcore branch role seed to generator")
    hard_trace_axes = pair.get("hardcore_row", {}).get("generation_trace", {}).get("seed_axes", {}) if isinstance(pair.get("hardcore_row"), dict) else {}
    _expect(
        hard_trace_axes.get("pose", {}).get("seed") == 7799,
        "Scene Pair Output generation trace did not use hardcore branch pose seed",
    )
    soft_content_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build(
        "softcore_branch",
        "fixed",
        6679,
        "content",
        "same_for_all_rows",
        "replace_layer",
    )[0]
    seeded_soft_scene, seeded_hard_scene, _summary, _metadata = nodes["SxCPSceneBranchPair"]().build(
        seed_fixture_scene,
        "same_creator_same_room",
        "hybrid",
        branch_options=branch_options,
    )
    seeded_soft_scene = nodes["SxCPSoftcoreBranchOptions"]().build(
        seeded_soft_scene,
        "same_as_hardcore",
        "lingerie_tease",
        True,
        0.45,
        "from_camera_config",
        "compact",
        "",
        branch_options=branch_options,
        seed_options=soft_content_seed_options,
    )[0]
    default_hardcore_continuity = (
        nodes["SxCPHardcoreBranchOptions"].INPUT_TYPES()
        .get("required", {})
        .get("hardcore_clothing_continuity", (None, {}))[1]
        .get("default")
    )
    _expect(
        default_hardcore_continuity == "partially_removed",
        "Hardcore Branch Options default should inherit softcore outfit continuity",
    )
    seeded_hard_scene = nodes["SxCPHardcoreBranchOptions"]().build(
        seeded_hard_scene,
        "couple",
        1,
        1,
        "hardcore",
        True,
        0.85,
        default_hardcore_continuity,
        "from_camera_config",
        "compact",
        "balanced",
        "",
        branch_options=branch_options,
    )[0]
    seeded_pair = json.loads(nodes["SxCPScenePairOutput"]().build(seeded_soft_scene, seeded_hard_scene)[7])
    seeded_soft_item = str(seeded_pair.get("softcore_row", {}).get("item") or "").lower()
    seeded_hard_clothing = str(seeded_pair.get("hardcore_clothing_state") or "").lower()
    seeded_hard_prompt = str(seeded_pair.get("hardcore_prompt") or "").lower()
    _expect(
        "black lace lingerie" in seeded_soft_item,
        "Scene softcore branch content seed fixture no longer selects the expected outfit",
    )
    _expect(
        "black lace lingerie" in seeded_hard_clothing,
        "Hardcore prompt did not inherit the softcore-branch seeded woman outfit",
    )
    _expect(
        "black lace lingerie" in seeded_hard_prompt,
        "Hardcore prompt text did not include the softcore-branch seeded woman outfit",
    )
    content_pose_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build(
        "hardcore_branch",
        "fixed",
        8899,
        "content_pose",
        "same_for_all_rows",
        "replace_layer",
    )[0]
    soft_scene_content, hard_scene_content, _branch_summary, _branch_metadata = nodes["SxCPSceneBranchPair"]().build(
        scene,
        "same_creator_same_room",
        "hybrid",
        branch_options=branch_options,
        seed_options=content_pose_seed_options,
    )
    soft_scene_content = nodes["SxCPSoftcoreBranchOptions"]().build(
        soft_scene_content,
        "same_as_hardcore",
        "lingerie_tease",
        True,
        0.45,
        "from_camera_config",
        "compact",
        "",
        branch_options=branch_options,
    )[0]
    hard_scene_content = nodes["SxCPHardcoreBranchOptions"]().build(
        hard_scene_content,
        "couple",
        1,
        1,
        "hardcore",
        True,
        0.85,
        "explicit_nude",
        "from_camera_config",
        "compact",
        "balanced",
        "",
        branch_options=branch_options,
        seed_options=content_pose_seed_options,
    )[0]
    content_pair = json.loads(nodes["SxCPScenePairOutput"]().build(soft_scene_content, hard_scene_content)[7])
    content_soft_seed_config = (
        content_pair.get("softcore_row", {}).get("seed_config")
        if isinstance(content_pair.get("softcore_row"), dict)
        else {}
    )
    content_hard_seed_config = (
        content_pair.get("hardcore_row", {}).get("seed_config")
        if isinstance(content_pair.get("hardcore_row"), dict)
        else {}
    )
    _expect(
        content_soft_seed_config.get("content_seed") != 8899,
        "Hardcore branch content_pose reroll leaked into softcore clothing seed",
    )
    _expect(
        content_hard_seed_config.get("content_seed") == 8899,
        "Hardcore branch content_pose reroll did not reach hardcore content seed",
    )
    _expect(
        content_hard_seed_config.get("pose_seed") == 8899 and content_hard_seed_config.get("role_seed") == 8899,
        "Hardcore branch content_pose reroll did not reach hardcore pose and role seeds",
    )

    def _soft_pose_pair(soft_pose_seed: int) -> dict[str, Any]:
        soft_pose_seed_options = nodes["SxCPSceneLayerSeedOptions"]().build(
            "softcore_branch",
            "fixed",
            soft_pose_seed,
            "pose",
            "same_for_all_rows",
            "replace_layer",
        )[0]
        soft_scene_pose, hard_scene_pose, _summary, _metadata = nodes["SxCPSceneBranchPair"]().build(
            scene,
            "same_creator_same_room",
            "hybrid",
            branch_options=branch_options,
            seed_options=soft_pose_seed_options,
        )
        soft_scene_pose = nodes["SxCPSoftcoreBranchOptions"]().build(
            soft_scene_pose,
            "same_as_hardcore",
            "lingerie_tease",
            True,
            0.45,
            "from_camera_config",
            "compact",
            "",
            branch_options=branch_options,
            seed_options=soft_pose_seed_options,
        )[0]
        hard_scene_pose = nodes["SxCPHardcoreBranchOptions"]().build(
            hard_scene_pose,
            "couple",
            1,
            1,
            "hardcore",
            True,
            0.85,
            "explicit_nude",
            "from_camera_config",
            "compact",
            "balanced",
            "",
            branch_options=branch_options,
        )[0]
        return json.loads(nodes["SxCPScenePairOutput"]().build(soft_scene_pose, hard_scene_pose)[7])

    soft_pose_pairs = [_soft_pose_pair(seed) for seed in (6677, 6678, 6679, 6680)]
    soft_pose_items = {pair.get("softcore_row", {}).get("item") for pair in soft_pose_pairs}
    soft_pose_values = {pair.get("softcore_row", {}).get("pose") for pair in soft_pose_pairs}
    _expect(len(soft_pose_items) == 1, "Softcore branch pose reroll should not change softcore outfit/content")
    _expect(len(soft_pose_values) > 1, "Softcore branch pose reroll did not change softcore pose")
    for expected_seed, pose_pair in zip((6677, 6678, 6679, 6680), soft_pose_pairs):
        soft_seed_config = pose_pair.get("softcore_row", {}).get("seed_config") if isinstance(pose_pair.get("softcore_row"), dict) else {}
        hard_seed_config = pose_pair.get("hardcore_row", {}).get("seed_config") if isinstance(pose_pair.get("hardcore_row"), dict) else {}
        _expect(
            soft_seed_config.get("pose_seed") == expected_seed,
            "Softcore branch pose seed did not reach softcore generator seed config",
        )
        _expect(
            hard_seed_config.get("pose_seed") != expected_seed,
            "Softcore branch pose seed leaked into hardcore generator seed config",
        )
    choice_board_node = nodes["SxCPChoiceBoard"]()
    choice_board_output = choice_board_node.build(
        json.dumps(content_pair),
        "hardcore_position_current",
        "",
        "",
        "auto",
        "auto",
        "Woman A",
        "",
        "",
    )
    choice_result = choice_board_output["result"]
    position_config = json.loads(choice_result[2])
    board_json = json.loads(choice_result[4])
    _expect(position_config.get("enabled"), "Choice Board did not emit active hardcore position config")
    _expect(board_json.get("choices"), "Choice Board did not expose resolved choice rows")
    location_board_output = choice_board_node.build(
        json.dumps(content_pair),
        "none",
        "fixed private test location with repeated room anchors",
        "",
        "auto",
        "auto",
        "Woman A",
        "",
        "",
    )
    location_config = json.loads(location_board_output["result"][0])
    _expect(
        location_config.get("scene_entries", [{}])[0].get("prompt") == "fixed private test location with repeated room anchors",
        "Choice Board did not convert location override into a replace location config",
    )


def smoke_node_builder_registration() -> None:
    required_nodes = [
        "SxCPPromptBuilder",
        "SxCPPromptBuilderFromConfigs",
    ]
    for node_name in required_nodes:
        _expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
        _expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")

    builder_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPPromptBuilder"]
    builder_inputs = builder_node.INPUT_TYPES().get("required") or {}
    _expect("category" in builder_inputs, "Prompt Builder lost category input")
    _expect("tooltip" in builder_inputs["category"][1], "Prompt Builder tooltip injection missing")
    direct_output = builder_node().build(
        "woman",
        "random",
        1,
        41,
        123,
        "full",
        "any",
        "standard",
        True,
        0.5,
        0.0,
        "random",
        1,
        0,
        -1,
        -1,
        Trigger,
        True,
    )
    direct_row = json.loads(direct_output[3])
    _expect_row_base(direct_row, "node_builder.direct_row")
    _expect(direct_output[0] == direct_row.get("prompt"), "Prompt Builder prompt output drifted from metadata")
    _expect(direct_output[4] == direct_row.get("main_category"), "Prompt Builder category output drifted from metadata")
    direct_trace = direct_row.get("generation_trace")
    _expect(isinstance(direct_trace, dict), "Prompt Builder metadata lost generation_trace")
    _expect(direct_trace.get("branch") == "built_in", "Prompt Builder metadata generation_trace lost branch")
    _expect(direct_trace.get("seed_axes", {}).get("content", {}).get("source") == "main", "Prompt Builder metadata trace lost content seed source")
    _expect_trigger_once("node_builder.direct_prompt", direct_output[0], Trigger)

    config_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPPromptBuilderFromConfigs"]
    config_inputs = config_node.INPUT_TYPES()
    _expect("category_config" in (config_inputs.get("optional") or {}), "Prompt Builder From Configs lost category_config input")
    config_output = config_node().build(
        1,
        41,
        123,
        category_config=pb.build_category_config_json("woman", "random"),
        cast_config=pb.build_cast_config_json("solo_woman", 1, 0),
        generation_profile=pb.build_generation_profile_json(profile="balanced"),
    )
    config_row = json.loads(config_output[3])
    _expect_row_base(config_row, "node_builder.config_row")
    _expect(config_output[0] == config_row.get("prompt"), "Prompt Builder From Configs prompt output drifted from metadata")
    _expect(config_output[4] == config_row.get("main_category"), "Prompt Builder From Configs category output drifted from metadata")
    config_trace = config_row.get("generation_trace")
    _expect(isinstance(config_trace, dict), "Prompt Builder From Configs metadata lost generation_trace")
    _expect(config_trace.get("builder") == "prompt_builder", "Prompt Builder From Configs trace lost builder label")
    _expect_text("node_builder.config_caption", config_output[2], 20)


def smoke_node_profile_filter_registration() -> None:
    required_nodes = [
        "SxCPGenerationProfile",
        "SxCPAdvancedFilters",
        "SxCPEthnicityList",
    ]
    for node_name in required_nodes:
        _expect(node_name in sxcp_nodes.NODE_CLASS_MAPPINGS, f"{node_name} missing from node registry")
        _expect(node_name in sxcp_nodes.NODE_DISPLAY_NAME_MAPPINGS, f"{node_name} missing from display registry")

    profile_node = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPGenerationProfile"]
    profile_inputs = profile_node.INPUT_TYPES().get("required") or {}
    _expect("profile" in profile_inputs, "Generation Profile lost profile input")
    _expect("tooltip" in profile_inputs["profile"][1], "Generation Profile tooltip injection missing")
    profile_config, profile_summary = profile_node().build(
        profile="balanced",
        clothing_override="profile_default",
        poses_override="profile_default",
        expression_enabled=True,
        expression_intensity_mode="fixed",
        expression_intensity=0.5,
        backside_bias=-1,
        minimal_clothing_ratio=-1,
        standard_pose_ratio=-1,
        trigger_policy="profile_default",
    )
    parsed_profile = json.loads(profile_config)
    _expect(parsed_profile.get("profile") == "balanced", "Generation Profile output lost profile")
    _expect(parsed_profile.get("expression_intensity") == 0.5, "Generation Profile output lost fixed expression intensity")
    _expect("balanced:" in profile_summary, "Generation Profile summary changed unexpectedly")

    filter_config = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPAdvancedFilters"]().build(
        include_european=True,
        include_mediterranean_mena=False,
        include_latina=False,
        include_east_asian=False,
        include_southeast_asian=False,
        include_south_asian=False,
        include_black_african=True,
        include_indigenous=False,
        include_mixed=False,
        include_plus_size=False,
        figure="curvy",
    )[0]
    parsed_filter = json.loads(filter_config)
    _expect(parsed_filter.get("figure") == "curvy", "Advanced Filters lost figure")
    _expect(parsed_filter.get("ethnicity_includes") == ["european", "black_african"], "Advanced Filters ethnicity includes changed")
    _expect(parsed_filter.get("no_plus_women") is True, "Advanced Filters should set no_plus_women when plus size is excluded")

    ethnicity, ethnicity_filter_config, ethnicity_summary = sxcp_nodes.NODE_CLASS_MAPPINGS["SxCPEthnicityList"]().build(
        include_european=False,
        include_mediterranean_mena=False,
        include_latina=False,
        include_east_asian=False,
        include_southeast_asian=False,
        include_south_asian=False,
        include_black_african=False,
        include_indigenous=False,
        include_mixed=False,
        include_asian=False,
        include_white_asian=False,
        include_western_european=False,
        include_french_european=True,
        include_germanic_european=False,
        include_nordic_european=False,
        include_celtic_european=False,
        include_slavic_european=False,
        include_baltic_european=False,
        include_alpine_european=False,
        include_balkan_european=False,
        include_greek_mediterranean=False,
        include_italian_mediterranean=False,
        include_iberian_mediterranean=False,
        strict_excludes=True,
    )
    parsed_ethnicity_filter = json.loads(ethnicity_filter_config)
    _expect("french_european" in ethnicity, "Ethnicity List output lost selected regional ethnicity")
    _expect(parsed_ethnicity_filter.get("ethnicity_includes") == ["french_european"], "Ethnicity List filter output changed")
    _expect("ethnicity list:" in ethnicity_summary, "Ethnicity List summary changed unexpectedly")


SMOKE_CASES: list[tuple[str, Callable[[], None]]] = [
    ("builtin_single_woman", smoke_builtin_single),
    ("builtin_couple_metadata", smoke_builtin_couple_metadata),
    ("camera_scene_single", smoke_camera_scene_single),
    ("scene_camera_adapter_pov_profile_policy", smoke_scene_camera_adapter_pov_profile_policy),
    ("row_camera_policy", smoke_row_camera_policy),
    ("config_route_location_theme", smoke_config_route_location_theme),
    ("builder_prompt_route_policy", smoke_builder_prompt_route_policy),
    ("builder_config_route_policy", smoke_builder_config_route_policy),
    ("krea_normal_row_routes", smoke_krea_normal_row_routes),
    ("krea_action_details_policy", smoke_krea_action_details_policy),
    ("outercourse_action_policy", smoke_outercourse_action_policy),
    ("item_axis_policy", smoke_item_axis_policy),
    ("krea_row_fields_policy", smoke_krea_row_fields_policy),
    ("location_config_policy", smoke_location_config_policy),
    ("row_location_policy", smoke_row_location_policy),
    ("row_expression_policy", smoke_row_expression_policy),
    ("row_prompt_axes_policy", smoke_row_prompt_axes_policy),
    ("row_item_policy", smoke_row_item_policy),
    ("row_category_route_policy", smoke_row_category_route_policy),
    ("row_generation_policy", smoke_row_generation_policy),
    ("category_extensions_policy", smoke_category_extensions_policy),
    ("category_cast_config_policy", smoke_category_cast_config_policy),
    ("row_subject_route_policy", smoke_row_subject_route_policy),
    ("generation_profile_config_policy", smoke_generation_profile_config_policy),
    ("filter_config_policy", smoke_filter_config_policy),
    ("character_config_policy", smoke_character_config_policy),
    ("character_profile_policy", smoke_character_profile_policy),
    ("row_normalization_policy", smoke_row_normalization_policy),
    ("prompt_hygiene_policy", smoke_prompt_hygiene_policy),
    ("row_rendering_policy", smoke_row_rendering_policy),
    ("row_role_graph_policy", smoke_row_role_graph_policy),
    ("row_assembly_policy", smoke_row_assembly_policy),
    ("formatter_input_policy", smoke_formatter_input_policy),
    ("formatter_target_policy", smoke_formatter_target_policy),
    ("formatter_detail_policy", smoke_formatter_detail_policy),
    ("krea_format_route_policy", smoke_krea_format_route_policy),
    ("formatter_cast_policy", smoke_formatter_cast_policy),
    ("caption_policy", smoke_caption_policy),
    ("caption_format_route_policy", smoke_caption_format_route_policy),
    ("caption_text_policy", smoke_caption_text_policy),
    ("caption_metadata_routes", smoke_caption_metadata_routes),
    ("sdxl_presets_policy", smoke_sdxl_presets_policy),
    ("sdxl_format_route_policy", smoke_sdxl_format_route_policy),
    ("sdxl_tag_policy", smoke_sdxl_tag_policy),
    ("sdxl_tag_routes", smoke_sdxl_tag_routes),
    ("hardcore_position_config_policy", smoke_hardcore_position_config_policy),
    ("row_route_metadata_policy", smoke_row_route_metadata_policy),
    ("category_library_route", smoke_category_library_route),
    ("category_subcategory_matrix", smoke_category_subcategory_matrix),
    ("hardcore_category_routes", smoke_hardcore_category_routes),
    ("krea_close_foreplay_route", smoke_krea_close_foreplay_route),
    ("pair_options_policy", smoke_pair_options_policy),
    ("pair_route_policy", smoke_pair_route_policy),
    ("pair_builder_policy", smoke_pair_builder_policy),
    ("insta_pair_same_cast", smoke_insta_pair),
    ("krea_pair_clothing_state", smoke_krea_pair_clothing_state),
    ("krea_anal_axis_compatibility", smoke_krea_anal_axis_compatibility),
    ("insta_pair_pov_man", smoke_insta_pair_pov),
    ("insta_pair_camera_split", smoke_insta_pair_camera_split),
    ("pov_camera_scene", smoke_pov_camera_scene),
    ("krea2_pov_pose_variant_catalog", smoke_krea2_pov_pose_variant_catalog),
    ("krea2_pov_atlas_variant_prompt_routes", smoke_krea2_pov_atlas_variant_prompt_routes),
    ("krea2_pose_variant_catalog_policy", smoke_krea2_pose_variant_catalog_policy),
    ("krea2_eval_log_policy", smoke_krea2_eval_log_policy),
    ("krea2_prompt_guide_policy", smoke_krea2_prompt_guide_policy),
    ("krea2_tuning_report_policy", smoke_krea2_tuning_report_policy),
    ("krea2_atlas_refine_manifest_policy", smoke_krea2_atlas_refine_manifest_policy),
    ("krea_pov_penetration_route", smoke_krea_pov_penetration_route),
    ("pov_outercourse_position_routes", smoke_pov_outercourse_position_routes),
    ("pov_oral_position_routes", smoke_pov_oral_position_routes),
    ("pov_climax_target_policy", smoke_pov_climax_target_policy),
    ("pov_penetration_position_routes", smoke_pov_penetration_position_routes),
    ("pov_anal_position_routes", smoke_pov_anal_position_routes),
    ("double_front_back_route", smoke_double_front_back_route),
    ("climax_position_routes", smoke_climax_position_routes),
    ("interaction_role_graph_routes", smoke_interaction_role_graph_routes),
    ("fallback_role_graph_routes", smoke_fallback_role_graph_routes),
    ("expression_disabled", smoke_no_expression_fallback),
    ("formatter_metadata_fixtures", smoke_formatter_metadata_fixtures),
    ("node_runtime_contracts", smoke_node_runtime_contracts),
    ("node_utility_registration", smoke_node_utility_registration),
    ("server_route_payload_policy", smoke_server_route_payload_policy),
    ("seed_config_policy", smoke_seed_config_policy),
    ("prompt_route_simulation_policy", smoke_prompt_route_simulation_policy),
    ("sxcp_mcp_client_cli_policy", smoke_sxcp_mcp_client_cli_policy),
    ("watch_prompt_image_folder_cli_policy", smoke_watch_prompt_image_folder_cli_policy),
    ("sxcp_prompt_batch_cli_policy", smoke_sxcp_prompt_batch_cli_policy),
    ("node_camera_registration", smoke_node_camera_registration),
    ("node_route_config_registration", smoke_node_route_config_registration),
    ("node_character_registration", smoke_node_character_registration),
    ("node_hardcore_position_registration", smoke_node_hardcore_position_registration),
    ("node_formatter_registration", smoke_node_formatter_registration),
    ("node_insta_registration", smoke_node_insta_registration),
    ("node_scene_chain_registration", smoke_node_scene_chain_registration),
    ("node_builder_registration", smoke_node_builder_registration),
    ("node_profile_filter_registration", smoke_node_profile_filter_registration),
]


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "--list",
        action="store_true",
        help="List available smoke case names and exit.",
    )
    parser.add_argument(
        "--case",
        choices=[name for name, _func in SMOKE_CASES],
        action="append",
        help="Run only the named smoke case. Can be passed multiple times.",
    )
    parser.add_argument(
        "--quiet",
        action="store_true",
        help="Suppress passing case lines and print one success summary.",
    )
    args = parser.parse_args(argv)
    if args.list:
        for name, _func in SMOKE_CASES:
            print(name)
        return 0
    selected = set(args.case or [])
    report = SmokeReport(verbose=not args.quiet)
    for name, func in SMOKE_CASES:
        if selected and name not in selected:
            continue
        try:
            func()
        except Exception as exc:  # noqa: BLE001 - report all smoke failures uniformly.
            report.fail(name, str(exc))
        else:
            report.ok(name)
    if args.quiet and not report.failed:
        print(f"OK: smoke passed ({len(report.passed)} cases).")
    else:
        print(f"\nSummary: {len(report.passed)} passed, {len(report.failed)} failed")
    if report.failed:
        return 1
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
