diff --git a/examples/visual/pallas-species-strikes.branch-and-fold.v0.visual.json b/examples/visual/pallas-species-strikes.branch-and-fold.v0.visual.json index f6daa0d..6f55e3b 100644 --- a/examples/visual/pallas-species-strikes.branch-and-fold.v0.visual.json +++ b/examples/visual/pallas-species-strikes.branch-and-fold.v0.visual.json @@ -5,57 +5,6 @@ "source_training_annotation_ref": "examples/ink/pallas-species-strikes.branch-and-fold.v0.training.json", "scene_id": "pallas-species-strikes-branch-and-fold-v0", "purpose": "Visual scene plan, style cue, character refs, and image prompt controls for the Pallas Species Strikes branch-and-fold IF fixture.", - "visual_art_direction": { - "base_scene_prompt": "Inside an asteroid cavity maintenance yard in the Pallas Bloom Cluster: Service Ring Kappa is a curved industrial lane built against raw asteroid rock, with ribbed metal wall supports, scuffed deck plates, amber work lights, pipe-and-cable manifold panels, dry-operation cephalopod harness bays with humidity collars, oxygenation loops, pressure cuffs, limiter sockets, and many-armed tool rails, dry-side anchor tracks with hooks and safety tethers, engineered seal technicians in gray suits, industrial haze, worn yellow-white safety paint, and lived-in future infrastructure.", - "modification_prompts": [ - { - "state": "formal_stoppage", - "trigger": "formal_stoppage == true", - "prompt": "Add workers halted in coordinated stillness at the manifold, with AU supervisor lights turning cold blue in the background." - }, - { - "state": "bypass_ready", - "trigger": "bypass_ready == true", - "prompt": "Add a portable crawl-access bypass bridge glowing with temporary pressure lines between Teth's harness and the seal assembly." - }, - { - "state": "security_pressure_high", - "trigger": "security_pressure >= 4", - "prompt": "Add security drones at the far hatch, small and sterile against the crowded maintenance ring." - }, - { - "state": "worker_injury", - "trigger": "worker_injury > 0", - "prompt": "Add a baseline rigger with a pressure-lash injury being steadied by coworkers, with emergency sealant and blood-dark coolant marks near the anchor rail." - }, - { - "state": "media_visibility_high", - "trigger": "media_visibility >= 2", - "prompt": "Add an outside-feed recorder light reflected on Lio's slate, suggesting the Kappa Refusal has escaped the yard's internal board." - }, - { - "state": "baseline_solidarity_high", - "trigger": "baseline_solidarity >= 3", - "prompt": "Show baseline riggers standing with anchor hooks at the front of the safety line beside engineered seal technicians instead of across from them." - }, - { - "state": "nara_path_marked", - "trigger": "nara_path_marked == true", - "prompt": "Show Lio's slate displaying a marked crawl path with a false seam highlighted while Nara keeps both hands visible near the manifold." - }, - { - "state": "sealed_evidence_custody", - "trigger": "branch-04-trade-bypass-for-evidence OR branch-04-seal-evidence-without-bypass selected", - "prompt": "Add a sealed incident packet icon split across AU safety, the yard board, and an outside Awakened Labor Front mirror on the slate display." - }, - { - "state": "local_aftermath", - "trigger": "return_fold reached", - "prompt": "Quiet the scene into a post-shift maintenance ring: Orrin's cracked mug in the break rack, Teth patching a harness cuff, and workers repeating the manifold touch without open celebration." - } - ], - "legacy_summary_note": "Compatibility summary only. Use visual_scene_plan for illustrated replay and per-section image generation." - }, "visual_scene_plan": { "purpose": "Illustrated Ink replay segmentation for website and imagegen use. Each entry maps a click-through Ink section or branch location to an imagegen-ready visual anchor.", "segmentation_rule": "Distinct places, focal areas, object closeups, arrivals, legal/authority reframes, red-alert beats, branch locations, and aftermath states receive visual_scene_id anchors. If a new actor or institution changes the room's power geometry, stage the pre-arrival beat first, then give the arrival/reframe its own click-through and visual transition. Pivotal one-sentence beats may stand alone.", diff --git a/scripts/count_visual_images.py b/scripts/count_visual_images.py index 97cef55..6136cd1 100644 --- a/scripts/count_visual_images.py +++ b/scripts/count_visual_images.py @@ -22,11 +22,11 @@ def plural(count: int, singular: str, plural_form: str | None = None) -> str: def count_visual_plan(path: Path) -> dict[str, Any]: data = load_json(path) + if "visual_art_direction" in data: + raise ValueError(f"{path} uses legacy visual_art_direction; count visual_scene_plan instead") scene_plan = data.get("visual_scene_plan", {}) scenes = scene_plan.get("scenes", []) global_branch_modifiers = scene_plan.get("global_branch_image_modifiers", []) - legacy_modifiers = data.get("visual_art_direction", {}).get("modification_prompts", []) - scene_variant_counts = [] for scene in scenes: modifiers = scene.get("state_image_modifiers", []) @@ -42,7 +42,6 @@ def count_visual_plan(path: Path) -> dict[str, Any]: base_images = len(scenes) scene_variants = sum(item["scene_variants"] for item in scene_variant_counts) global_variants = len(global_branch_modifiers) - legacy_variants = len(legacy_modifiers) return { "path": path, @@ -50,9 +49,8 @@ def count_visual_plan(path: Path) -> dict[str, Any]: "base_images": base_images, "scene_variants": scene_variants, "global_branch_variants": global_variants, - "legacy_summary_variants": legacy_variants, - "total_variants": scene_variants + global_variants + legacy_variants, - "max_exhaustive_renders": base_images + scene_variants + global_variants + legacy_variants, + "total_variants": scene_variants + global_variants, + "max_exhaustive_renders": base_images + scene_variants + global_variants, "scene_variant_counts": scene_variant_counts, } @@ -62,8 +60,7 @@ def format_report(results: list[dict[str, Any]], verbose: bool) -> str: total_base = sum(item["base_images"] for item in results) total_scene_variants = sum(item["scene_variants"] for item in results) total_global_variants = sum(item["global_branch_variants"] for item in results) - total_legacy_variants = sum(item["legacy_summary_variants"] for item in results) - total_variants = total_scene_variants + total_global_variants + total_legacy_variants + total_variants = total_scene_variants + total_global_variants lines.append("Ghostlight visual image inventory") lines.append("") @@ -74,7 +71,6 @@ def format_report(results: list[dict[str, Any]], verbose: bool) -> str: lines.append(f" variants: {item['total_variants']}") lines.append(f" scene-local variants: {item['scene_variants']}") lines.append(f" global branch variants: {item['global_branch_variants']}") - lines.append(f" legacy summary variants: {item['legacy_summary_variants']}") lines.append(f" max exhaustive renders: {item['max_exhaustive_renders']}") if verbose: lines.append(" scene breakdown:") @@ -90,7 +86,6 @@ def format_report(results: list[dict[str, Any]], verbose: bool) -> str: lines.append(f"- variants: {total_variants}") lines.append(f" scene-local variants: {total_scene_variants}") lines.append(f" global branch variants: {total_global_variants}") - lines.append(f" legacy summary variants: {total_legacy_variants}") lines.append(f"- max exhaustive renders: {total_base + total_variants}") return "\n".join(lines) diff --git a/state/evidence.jsonl b/state/evidence.jsonl index 49f57d8..d3cdfd8 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -53,3 +53,4 @@ {"ts": "2026-05-03T10:05:39+00:00", "type": "fixture", "status": "ok", "note": "Revised Pallas fixture prose and visual sidecar to make Teth Inkwise carry AU cephalopod dry-operation exploitation through visible discomfort, restrained stoicism, and worn harness details without turning the scene into exposition."} {"ts": "2026-05-03T10:12:17+00:00", "type": "review", "status": "ok", "note": "Tightened Pallas visual segmentation doctrine and fixture structure: scene-changing arrivals and institutional reframes now require click-through visual transitions; split Nara refusal from Ilya arrival and AU classification from BioElevate legal reframe."} {"ts": "2026-05-03T10:20:18+00:00", "type": "architecture", "status": "ok", "note": "Split illustrated IF visual planning out of the Pallas Ink training annotation into a separate .visual.json artifact, with validator support for visual_plan_ref so training sidecars stay focused on branch semantics and mutation surfaces."} +{"ts": "2026-05-03T10:26:26+00:00", "type": "architecture", "status": "ok", "note": "Removed legacy visual_art_direction/modification_prompts from the Pallas visual plan path. Canonical image variants now come from visual_scene_plan.scenes[].state_image_modifiers and visual_scene_plan.global_branch_image_modifiers only; the counter and validator reject the legacy layer."} diff --git a/tools/validate_ink_examples.py b/tools/validate_ink_examples.py index ba724ea..8ee18c8 100644 --- a/tools/validate_ink_examples.py +++ b/tools/validate_ink_examples.py @@ -250,7 +250,6 @@ def validate_ink_visual_plan(path: Path, ink_path: Path, annotation_path: Path) "source_training_annotation_ref", "scene_id", "purpose", - "visual_art_direction", "visual_scene_plan", "visual_character_refs", ], @@ -259,11 +258,7 @@ def validate_ink_visual_plan(path: Path, ink_path: Path, annotation_path: Path) require(data["schema_version"] == "ghostlight.ink_visual_plan.v0", f"{path}.schema_version is wrong") require((ROOT / data["ink_ref"]).resolve() == ink_path.resolve(), f"{path}.ink_ref does not match {ink_path}") require((ROOT / data["source_training_annotation_ref"]).resolve() == annotation_path.resolve(), f"{path}.source_training_annotation_ref does not match {annotation_path}") - - art = data["visual_art_direction"] - require_keys(art, ["base_scene_prompt", "modification_prompts"], f"{path}.visual_art_direction") - require(isinstance(art["base_scene_prompt"], str) and art["base_scene_prompt"].strip(), f"{path}.visual_art_direction.base_scene_prompt must be non-empty") - require(isinstance(art["modification_prompts"], list), f"{path}.visual_art_direction.modification_prompts must be an array") + require("visual_art_direction" not in data, f"{path}.visual_art_direction is legacy cruft; use visual_scene_plan instead") plan = data["visual_scene_plan"] require_keys(plan, ["global_style_cue", "segmentation_rule", "scenes", "global_branch_image_modifiers", "prompt_assembly_rule"], f"{path}.visual_scene_plan")