# Nine-Case Visual Reliability Remediation Roadmap

Date: 2026-05-14

Purpose: consolidate the earlier 9 visual failure cases into a practical,
step-by-step remediation roadmap. This document re-checks the current code and
prompt structure and connects each case to the correct SOT, current gaps, and
implementation direction.

Related documents:

- `docs/visual-reliability-audit/2026-05-14-semantic-string-routing-debt-map.md`
- `docs/visual-reliability-audit/2026-05-14-prompt-hygiene-v1-spec.md`

This is still a design/remediation roadmap. It is not an implementation plan for
all cases at once.

## 1. Core Principle

The system should not handle "photo", "dead body", "boat", "mirror",
"close-up", or "doorway" as hardcoded case names. Those are surface examples.

The replacement model is:

> LLM-produced structured SOT describes the visual requirement. Code consumes
> closed enums/ids/contracts and validates consistency. Code must not infer
> open-world visual meaning from arbitrary prose.

Correct abstraction:

- `visual_identity`: preserve identity of a repeated visible entity.
- `information_surface`: show or hide the information-bearing side of a
  surface.
- `representation_binding`: bind a represented subject inside a carrier surface
  such as photo, mirror, screen, projection.
- `appearance_continuity`: preserve a cross-shot invariant appearance/posture
  intersection.
- `spatial_relation`: enforce frame-space placement/relation.
- `visibility_requirement`: enforce who/what must or must not appear.

Incorrect abstraction:

- `photo_front`
- `dead_body_pose`
- `boat_identity`
- `mirror_face_fix`
- `close_up_no_other_person`

Those names merely repackage hardcoded cases.

## 2. Current System Anchors

### 2.1 Existing strong anchors

RenderPromptCard now has the right general container shape:

- Top-level fields include `render_strategy`, `id_policy`,
  `background_binding`, `continuity_elements_used`, `asset_requirements`, and
  `render_contracts` in `backend/app/core/steps/render_prompt_card.py:538-543`.
- Area B introduced `render_contracts` with `visual_identity` as the first
  dimension in `render_prompt_card.py:1791-1821`.
- `required_refs_from_render_contracts()` derives prop refs from
  `visual_identity/preserve/use_entity_reference` in
  `render_prompt_card.py:2008-2064`.
- `build_asset_requirements()` states prop required refs are now
  render-contract derived, not noun-list derived, at
  `render_prompt_card.py:2088-2195`.

This is the right direction. Future cases should add requirements to a general
contract system only when needed, not add new case-specific code.

### 2.2 Existing partial anchors

- `frame_spatial_contract` is an opt-in frame-space SOT helper:
  `backend/app/core/frame_spatial_contract.py:1-6`.
- `shot_staging` schema has `frame_spatial_contract` and screen/depth/gesture
  fields: `prompts/_base/shot_staging/10.202605141617/schema.json:50-96`.
- `scene_detail` requires every variation to echo applied frame spatial
  constraint ids:
  `prompts/_base/scene_detail/23.202605141758/detail_schema.json:53`.
- `background_binding.owned_objects` is the owned background object contract:
  `prompts/_base/background_prompt/6.202605091200/schema.json:19-26`.
- `entity_metadata` has prop `visual_identity.reference_required`:
  `backend/app/core/entity_metadata.py:1-8`, `:34-37`, `:158-200`.
- `visual_world_rules` already owns broad phenomenon types:
  `prompts/_base/visual_world_rules/6.202605021400/system.md:51`.

### 2.3 Existing weak anchors

- `framing_scale` is still derived from camera_direction regex:
  `backend/app/core/steps/render_prompt_card.py:560-566`.
- close framing reference skip still relies on `_CLOSE_FRAMING_RE`:
  `backend/app/services/scene_generation_coordinator.py:76-81`.
- `shot_visibility` still uses gaze/offscreen/framing lexicons:
  `backend/app/modules/pipeline/shot_visibility.py:36-94`, `:108-132`.
- `ref_contract_validator` still classifies "from the reference" by token
  windows:
  `backend/app/core/ref_contract_validator.py:47-64`, `:156-190`, `:425-445`.
- `visible_entities_validator` has face/close-up regex and trigger phrase
  substring checks:
  `backend/app/core/visible_entities_validator.py:139-145`, `:212-218`.
- `t2i_review` still applies blind prompt substring replacement:
  `backend/app/modules/pipeline/t2i_review.py:383-403`, `:407-451`.

These are not all equal priority, but they explain why the 9 cases keep
reappearing.

## 3. The 9 Cases

### Case 1. Character pose/state continuity

User problem:

- A character's posture or physical state must stay consistent across shots.
- Examples include dead, unconscious, injured, restrained, slumped, kneeling,
  sitting, or a very specific repeated pose.

Current SOT:

- `shot_staging.character_angles[].body_pose` exists as natural language:
  `prompts/_base/shot_staging/10.202605141617/schema.json:24`.
- `shot_staging.character_angles[].gaze_target` has special values such as
  `unconscious`, `dead`, `severely_injured`:
  `schema.json:25`.
- `scene_reference_service.detect_state_variant_sids()` detects state variants
  from staging gaze_target:
  `backend/app/services/scene_reference_service.py:866-911`.
- `semantic_contract_router` uses `gaze_target` and
  `character_state` fixed elements:
  `backend/app/modules/semantic_contract_router.py:78-128`.
- `scene_reference_service` excludes state-variant characters from removal
  hints:
  `backend/app/services/scene_reference_service.py:714-774`.

Current gap:

- Strong only for the existing state-variant set, mostly
  `dead/unconscious/severely_injured`.
- General pose continuity is still free-text `body_pose`, not structured
  cross-shot invariant.
- Previous-shot fallback and state-variant refs can overlap; this may be
  defensive layering, but it needs case review before changing.

Remediation direction:

1. Do not reintroduce `immobilized_character` into `keep_elements`.
2. Add a future `appearance_continuity` render-contract requirement for
   cross-shot invariant posture/state.
3. Let the LLM emit:
   - target entity id
   - invariant summary
   - allowed variation
   - duration/scope
4. Code should not parse `slumped`, `kneeling`, `dead`, `sitting` from prose.
5. Existing state-variant refs remain the first concrete consumer; broader
   pose-state refs should be a separate area.

Suggested area:

- `area-character-pose-state-reference`

Do not do:

- Do not solve this by adding pose noun regex.
- Do not route character state through previous-shot `keep_elements`.

### Case 2. Vehicle / large prop / repeated object identity

User problem:

- Important non-human objects such as vehicles, boats, large machines, or
  recurring props need visual identity consistency.

Current SOT:

- Entity metadata shape includes prop `visual_identity.reference_required`:
  `backend/app/core/entity_metadata.py:34-37`, `:158-200`.
- Entity extractor prompt requires prop visual identity metadata:
  `prompts/_base/entity_extractor_v2/9.202605130226/system.md:124-131`.
- Render contracts are generated from visible prop plus
  `visual_identity.reference_required`:
  `backend/app/core/steps/render_prompt_card.py:1809-1866`.
- `required_refs_from_render_contracts()` turns the contract into required prop
  refs:
  `render_prompt_card.py:2008-2064`.

Current gap:

- B-min covers prop visual identity only.
- A large object embedded as a background-owned object may not be addressable as
  `P##`.
- The system must avoid case naming such as `boat` or `vehicle`; the actual rule
  is "repeated visible object with identity must become addressable".

Remediation direction:

1. Keep Area B's render-contract path.
2. Do not add vehicle/boat noun filters.
3. Add an "addressability" decision: if a background-owned or location-owned
   object must preserve identity across shots, it must be promoted or linked to
   an addressable entity.
4. Continue deriving required refs from render contracts, not from prop names.
5. Add tests proving noun-list removal remains true.

Suggested area:

- `area-large-prop-vehicle-identity`

Do not do:

- Do not add `vehicle`, `boat`, `car`, `bicycle` lists to code.
- Do not bypass render_contracts and push metadata bool directly into
  required_refs.

### Case 3. Home/interior/furniture/device continuity

User problem:

- Rooms, doors, furniture, appliances, windows, lighting, and interior layout
  need continuity.

Current SOT:

- Background prompt requires `objects_owned_by_background`:
  `prompts/_base/background_prompt/6.202605091200/schema.json:19-26`.
- Background prompt system says floor plan is layout source only and
  `objects_owned_by_background` prevents scene_detail redraw:
  `prompts/_base/background_prompt/6.202605091200/system.md:14-20`.
- Background chain planning creates nodes based on shared wall/door/window/
  furniture requirements:
  `prompts/_base/background_chain_planning/4.202604291315/system.md:44`.
- Child backgrounds carry shared visual anchors:
  `background_chain_planning/system.md:82-87`.
- RenderPromptCard has `background_binding.owned_objects`:
  `backend/app/core/steps/render_prompt_card.py:1331-1467`.

Current gap:

- Owned objects are mostly common noun lists, not placed/anchored objects.
- Some spatial continuity depends on chain background image consistency rather
  than structured object-anchor arbitration.
- State changes in furniture/device positions can still be under-specified.

Remediation direction:

1. Do not replace background-owned objects with prop refs wholesale.
2. Keep background-owned object lists for redraw prevention.
3. Add a future anchor-placement layer only for objects whose position/state is
   shot-critical.
4. Use frame_spatial_contract for per-shot screen placement only when needed.
5. Use render_contracts only when the object needs identity/reference behavior.

Suggested area:

- `area-background-anchor-arbitration`

Do not do:

- Do not make every furniture item an entity.
- Do not make every shot emit frame_spatial_contract.

### Case 4. Documents / phones / photos / frames / maps shown backwards

User problem:

- Information-bearing surfaces can be shown from the wrong side or orientation.

Current SOT:

- `shot_staging.key_bg_elements[].directionality_class` exists:
  `prompts/_base/shot_staging/10.202605141617/schema.json:40-46`.
- `orientation` requires natural-language details for content/reflective/
  transparent surfaces:
  `schema.json:38`.
- Scene_detail prompt says `directionality_class` is the source for
  reproduction-surface logic:
  `prompts/_base/scene_detail/23.202605141758/system.md:51`.
- RenderPromptCard has `id_policy.reproduction_surface_rule`:
  `backend/app/core/steps/render_prompt_card.py:1192-1250`.

Current gap:

- `directionality_class` is a low-level observation, not the final requirement.
- `content_surface` does not always mean "show front"; sometimes the shot may
  intentionally show the back side.
- `perception_mode` is still string compared in code:
  `backend/app/core/steps/render_prompt_card.py:1292`.

Remediation direction:

1. Keep `directionality_class` as low-level LLM SOT.
2. Add `information_surface` render-contract requirement only when the shot
   intent requires the information-bearing side to be visible.
3. Separate:
   - surface type
   - required side/orientation
   - represented subject identity
4. Convert `perception_mode` to enum or feed representation_binding contracts.

Suggested area:

- `area-information-surface`

Do not do:

- Do not add noun lists for photo/document/phone/map.
- Do not automatically force front side for every `content_surface`.

### Case 5. Spatial direction / doors / movement / screen-space relation

User problem:

- T2I/I2I often fails to understand spatial directions, door position,
  movement direction, and pointing/gaze direction unless described in screen
  coordinates.

Current SOT:

- `frame_spatial_contract` is optional/null and max-3 constraints:
  `prompts/_base/shot_staging/10.202605141617/schema.json:50-96`.
- The prompt says most shots should keep it null and emit it only when spatial
  misunderstanding can break the image:
  `prompts/_base/shot_staging/10.202605141617/system.md:210-235`.
- Scene_detail must apply it in every variation when present:
  `prompts/_base/scene_detail/23.202605141758/system.md:163-199`.
- Echo field is per variation:
  `prompts/_base/scene_detail/23.202605141758/detail_schema.json:53`.
- Helper validates and prepares contract:
  `backend/app/core/frame_spatial_contract.py:1-6`.

Current gap:

- This is structurally the strongest case today.
- But it is opt-in and one-shot/frame-space only.
- It must not become a universal router for pose, identity, surface, or
  cross-shot continuity.

Remediation direction:

1. Keep it opt-in.
2. Keep constraints minimal.
3. Use it for screen-space placement/direction only.
4. Do not expand it to solve every other case.
5. Add canary/image-level verification later; current contract closure is not
   equivalent to image-level proof.

Suggested area:

- Already mostly handled by frame_spatial_contract.
- Follow-up only if canary shows missed triggers.

Do not do:

- Do not emit it for every shot.
- Do not add multi-reason complexity unless there is a proven failure cluster.

### Case 6. Face/body displacement or identity appearing in wrong surface/place

User problem:

- Faces or body parts appear in the wrong place, on a surface, or as a phantom
  extra body.

Current SOT:

- RenderPromptCard `id_policy` has body-part focus and reproduction surface
  rules:
  `backend/app/core/steps/render_prompt_card.py:1046-1324`.
- Scene_detail prompt says representation/perception constraints can override
  regular surface behavior:
  `prompts/_base/scene_detail/23.202605141758/system.md:61`.
- `visible_entities_validator` enforces visible IDs and body-part exemptions:
  `backend/app/core/visible_entities_validator.py:139-145`, `:212-218`,
  `:540-550`.
- `ref_contract_validator` phantom guard checks attached refs:
  `backend/app/core/ref_contract_validator.py:425-445`.

Current gap:

- Some enforcement still uses phrase matching or token windows.
- `representation_binding` is not yet a first-class render contract.
- ID and outlook policy need separation for photos/mirrors/screens:
  identity may be same, appearance may be representation-specific.

Remediation direction:

1. Introduce `representation_binding` requirement when a carrier surface
   represents a subject.
2. Split identity policy from outlook/reference policy:
   - use character identity
   - use representation-specific appearance
   - avoid current outlook ref if inappropriate
3. Replace face/close-up regex gates after structured focus/framing SOT exists.
4. Keep ID validator closed-world; remove open-world prompt phrase inference
   from hard behavior.

Suggested area:

- `area-id-policy-representation-binding`

Do not do:

- Do not ban character IDs inside represented surfaces blindly.
- Do not parse "photo/mirror/screen" nouns in code.

### Case 7. Close-up introduces other people

User problem:

- A close-up of one person can pull in another person from a previous shot
  reference or from descriptor confusion.

Current SOT:

- `frame_spatial_contract` includes reason `primary_subject_isolation`:
  `backend/app/core/frame_spatial_contract.py:50-52`.
- Scene generation currently skips chain background on close but still tries
  previous-shot refs:
  `backend/app/services/scene_generation_coordinator.py:371-381`, `:668-678`.
- Close detection is regex:
  `scene_generation_coordinator.py:76-81`,
  `backend/app/core/steps/detail_steps.py:104-109`,
  `render_prompt_card.py:560-566`.

Current gap:

- Close-up ref attach policy is incomplete.
- `zoom_in_detail` is safe/expected, but `exact_background`,
  `atmosphere_reference`, and fallback previous-shot refs are riskier.
- Detection itself is still regex-based.

Remediation direction:

1. Implement close x ref_usage matrix after or with framing-scale SOT.
2. Preserve `close + zoom_in_detail`.
3. Fail-fast or block unsafe close previous-shot references.
4. Keep `primary_subject_isolation` as prompt/card instruction, not sole
   reference-routing enforcement.

Suggested area:

- first candidate inside `area-framing-focus-sot`

Do not do:

- Do not simply skip all previous-shot refs on close.
- Do not rely only on prompt wording to prevent extra people.

### Case 8. Weird body shape, impossible crop, bad sitting/standing integration

User problem:

- Bodies get cut incorrectly, sit in impossible places, or combine impossible
  posture with camera/framing.

Current SOT:

- `shot_staging.character_angles[].body_pose` exists as short NL:
  `prompts/_base/shot_staging/10.202605141617/schema.json:24`.
- `shot_staging` prompt explicitly asks for varied grounded poses:
  `prompts/_base/shot_staging/10.202605141617/system.md:74-132`.
- RenderPromptCard contains camera-frame and primary-framing rules:
  `backend/app/core/steps/render_prompt_card.py:731-840`.
- `t2i_review` has physical inconsistency issue type and substring rewrite:
  `prompts/_base/t2i_review/3.202605121200/scene_system.md:49-63`,
  `backend/app/modules/pipeline/t2i_review.py:407-451`.

Current gap:

- body_pose is NL, not structured enough for validation.
- t2i_review patching by substring is fragile.
- Some physical constraints are prompt/card guidance, not image-level verified.

Remediation direction:

1. Do not solve by adding posture noun regex.
2. Keep body_pose as LLM wording for now.
3. Future area may introduce structured pose/focus constraints only for
   repeated/high-risk postures.
4. Replace t2i_review text surgery with regeneration diagnostics or structured
   patch spans.
5. Image-level LVM/post-check coverage should be audited before claiming closed.

Suggested area:

- `area-character-pose-state-reference` for repeated pose/state.
- `area-t2i-review-mutation-redesign` for substring rewrite.

Do not do:

- Do not make body posture a global enum too early.
- Do not add a fixed list of sitting/kneeling/crouching tokens to code.

### Case 9. Character does not integrate with background/furniture/device

User problem:

- Characters look pasted onto backgrounds, fail to share the same table/bench,
  point to the wrong door, or ignore furniture/device positions.

Current SOT:

- Background-owned objects are enumerated:
  `prompts/_base/background_prompt/6.202605091200/system.md:20`.
- Background chain planning has shared visual anchors:
  `prompts/_base/background_chain_planning/4.202604291315/system.md:44`,
  `:75`, `:82-87`.
- RenderPromptCard has `background_binding`:
  `backend/app/core/steps/render_prompt_card.py:1331-1467`.
- `frame_spatial_contract` has `shared_space_relation`:
  `backend/app/core/frame_spatial_contract.py:50`.
- Scene_detail prompt gives spatial consistency and frame_spatial_contract
  priority:
  `prompts/_base/scene_detail/23.202605141758/system.md:150-199`.

Current gap:

- Background objects are common nouns and not always anchored to screen/depth.
- Shared-space relation is opt-in.
- Code/prompt still has shared anchor keywords:
  `backend/app/core/steps/render_prompt_card.py:397-408` and
  `prompts/_base/scene_detail/23.202605141758/system.md:154`.

Remediation direction:

1. Keep `background_binding` for redraw prevention.
2. Use `frame_spatial_contract` only when the exact screen relation matters.
3. Use future anchor arbitration for recurring background-owned objects whose
   placement matters across shots.
4. Do not convert all background objects into props.
5. Avoid expanding shared-anchor keyword lists.

Suggested area:

- `area-background-anchor-arbitration`

Do not do:

- Do not use `shared bench`/`shared table` token matching as hard validation.
- Do not force every fg/bg scene to emit anchors.

## 4. Consolidated Dependency Order

### Step 0. Keep existing docs split

Already created:

- semantic string routing debt map
- prompt hygiene v1 spec

These remain supporting documents, not implementation plans.

### Step 1. Framing/Focus SOT

Cases covered:

- Case 5 partially
- Case 7 directly
- Case 6 partially
- Case 8 partially

Why first:

- The close-framing regex currently affects ref routing, RenderPromptCard
  framing scale, body-part exemptions, and canary scopes.

Deliverables:

- Structured producer field.
- Close x ref_usage matrix.
- Replace `_CLOSE_FRAMING_RE` consumers or demote to diagnostics.
- Tests for single and batch paths.

### Step 2. Prompt Hygiene v1

Cases covered:

- Cross-cutting prompt contamination.
- Domain nomenclature drift affecting Case 1 and Case 6.

Why second:

- Small, high-confidence cleanup.
- Reduces future LLM priming without code behavior risk.

Deliverables:

- New prompt versions.
- DB/prompt active-source verification.
- Residue grep.

### Step 3. Representation Binding / Information Surface

Cases covered:

- Case 4
- Case 6

Why after framing:

- These cases need `identity_policy` and `appearance_policy` separation.
- They should build on render_contracts, not invent case-specific prompt blocks.

Deliverables:

- `information_surface` requirement.
- `representation_binding` requirement.
- Enum/contract consumption in scene_detail/ref validators.

### Step 4. Character Pose/State Continuity

Cases covered:

- Case 1
- Case 8

Why later:

- State-variant already handles the strongest dead/unconscious cases.
- General pose continuity needs careful scope to avoid hardcoding pose nouns.

Deliverables:

- `appearance_continuity` requirement.
- Optional pose/state variant references only for high-risk repeated states.
- Case review for state_variant/previous-shot overlap.

### Step 5. Background Anchor Arbitration

Cases covered:

- Case 3
- Case 9
- Case 2 when background-owned objects need promotion.

Why later:

- Background chain and background_binding already provide partial continuity.
- The remaining problem is arbitration: when does an object remain background,
  become addressable, or become a screen anchor?

Deliverables:

- object-anchor placement policy.
- no global entity promotion.
- no hard-coded furniture/vehicle noun list.

### Step 6. t2i_review Mutation Redesign

Cases covered:

- Case 8
- Cross-cutting prompt mutation risk.

Why later:

- It changes a mutator step, not only validation.
- It should follow clearer upstream contracts.

Deliverables:

- replace blind substring mutation with structured patch/spans or regeneration
  diagnostics.

## 5. Implementation Rules

1. One area at a time.
2. No new open-world visual noun lists in code.
3. No prompt-only fix for a runtime routing bug.
4. No code-only regex replacement for semantic judgement.
5. Any contract field must have a consumer; otherwise it is over-engineering.
6. Any schema/prompt version change must update manifest/version registry and
   checkpoint invalidation where applicable.
7. Any prompt hygiene patch must verify DB override vs file fallback.
8. Canaries must prove actual downstream consumption, not only producer output.

## 6. What Is Not Solved By This Roadmap

- It does not claim current image outputs are fixed.
- It does not replace every regex.
- It does not convert all visual problems into render_contracts immediately.
- It does not make frame_spatial_contract a universal visual router.
- It does not decide final schema names for future contract dimensions.

## 7. Completion Criteria For Future Work

For any one future area, closure requires:

- current file:line inventory updated before implementation;
- one explicit SOT chosen;
- old regex/keyword path either removed, demoted to diagnostic, or justified as
  closed-world technical validation;
- producer, loader, consumer, validator, and tests updated atomically;
- prompt/version/schema/manifest drift checked;
- canary or focused production fixture proving downstream consumption.

