# Semantic String Debt LLM Audit Findings

- result chunks: `17`
- findings: `20`

## `backend/app/modules/pipeline/entity_extractor_v3.py`

- `88` **P2 / schema_or_enum_drift**
  - evidence: "entity_type": {"type": "string"}
  - why: The 'entity_type' field in ENTITY_DETAIL_SCHEMA is defined as a generic string, but the pipeline logic (lines 471-477 and 504-509) performs exact string comparisons against 'character' and 'location' to route entities into specific processing buckets. This creates a risk where LLM variations in output (e.g., 'Character' or 'loc') would cause entities to be misrouted as 'props' in the final output.
  - fix: Update the ENTITY_DETAIL_SCHEMA to define 'entity_type' as an enum: ["character", "location", "prop"].

## `backend/app/modules/semantic_contract_router.py`

- `22-107` **P1 / semantic_string_judgment**
  - evidence: IMMOBILIZED_GAZE = frozenset({"dead", "unconscious", "severely_injured"}) ... gaze = entry.get("gaze_target")
  - why: The system performs semantic classification of a character's physical state (immobilized vs active) by matching specific natural-language keywords within the 'gaze_target' field of an LLM-produced shot description. This field is semantically overloaded to carry state information, and the resulting classification directly controls critical sanitizer behaviors such as forbidding state polarity rewrites.
  - fix: Transition to the structured 'subject_state.immobility_state' field as planned in the B-next patch, ensuring the LLM outputs a formal enum rather than relying on keyword matching in the gaze field.

## `backend/app/modules/t2i_visual_converter.py`

- `128-133` **P1 / blind_string_mutation**
  - evidence: [이름]은 목록의 이름을 공백 포함 정확히 복사 ... [] 마커는 참조 이미지 치환용이므로 목록 외 사용 시 시스템 오류 발생
  - why: The prompt defines a strict contract for the LLM to generate exact substrings (markers) within natural language prose for later substitution. This is a form of blind semantic string mutation where the system's stability depends on the LLM's ability to perfectly replicate names from a provided list without any variation in spacing or characters, which is prone to hallucination or formatting drift.
  - fix: Use a structured output format where the LLM identifies entities by ID or index rather than embedding markers in prose, or use a post-processing step that performs fuzzy matching or entity linking instead of exact substring replacement.

## `backend/app/services/scene_generation_coordinator.py`

- `718-719` **P1 / blind_string_mutation**
  - evidence: if shot_name and shot_name.lower() not in var_t2i.lower(): var_t2i = f"[Camera: {shot_name}] {var_t2i}"
  - why: This uses a brittle case-insensitive substring check over natural-language prompt text to decide whether to prepend a camera directive. Variations in phrasing or punctuation in the generated prompt can cause redundant or missing directives.
  - fix: Pass camera directives as a separate structured field to the T2I generator or use a dedicated prompt assembly helper that handles deduplication semantically.

## `backend/app/services/scene_reference_service.py`

- `65-88` **P2 / semantic_string_judgment**
  - evidence: _re.search(r'character\s+(C\d{2,3}(?:O\d{2,3})?)', label)
  - why: Internal metadata (entity IDs) is extracted from intermediate string labels to decide how to further transform those labels and attach descriptions. This creates a brittle internal string contract between different parts of the service.
  - fix: Pass structured metadata (e.g., a dict or object) through the indexing pipeline instead of encoding/decoding information in label strings.

- `111-130` **P1 / blind_string_mutation**
  - evidence: rewritten = _re.sub(pattern, replacement, rewritten)
  - why: The function blindly replaces short IDs (C##, P##) within the generated T2I prompt text with descriptive phrases. This risks corrupting the prompt if these patterns appear naturally or in other metadata fields within the string.
  - fix: Use a template-based prompt generation system where placeholders are replaced in a controlled manner, or perform replacement on a structured representation of the prompt.

- `367-478` **P1 / semantic_string_judgment**
  - evidence: _re.finditer(r'(C\d{2,3})(O\d{2,3})', t2i_prompt), _re.finditer(r'\[\[([^\]]+)\]\+\[([^\]]+)\]\]', t2i_prompt)
  - why: The service resolves entity identity and decides which reference images to attach by parsing natural-language prompt text (both short_id patterns and legacy name-based patterns). This makes reference resolution dependent on the LLM's ability to maintain exact string formatting in prose.
  - fix: Pass a structured list of active entity IDs alongside the prompt instead of inferring them from the prompt text via regex.

- `932-988` **P1 / semantic_string_judgment**
  - evidence: ca.get("gaze_target", "") in ("unconscious", "dead", "severely_injured")
  - why: The 'gaze_target' field, which nominally describes orientation, is overloaded to carry physical/biological state information. Downstream code uses these specific strings to branch prompt label generation and reference image selection (state_variant).
  - fix: Introduce a dedicated 'physical_state' or 'status' field in the staging schema and use a formal enum for these values.

## `prompts/_base/background_chain_planning/4.202604291315/system.md`

- `21-25` **P1 / llm_closed_list_instruction**
  - evidence: INDOOR / ENCLOSED / FIXED-SET (room, hallway, store interior, office, kitchen, basement, closed vehicle interior, fixed studio set, etc.) ... DETACHED OPEN AREA ... (a road far from any building, an unrelated forest, a wide beach, a public street block, a mountain trail, a public square not attached to a tracked building)
  - why: The prompt uses a closed list of location examples to instruct the LLM to perform a semantic classification that determines the 'skip_chain' routing logic. This makes the pipeline's core branching logic dependent on the LLM's interpretation of open-world descriptions against a brittle list of examples.
  - fix: Define a structured 'location_category' enum in the input metadata (e.g., 'architectural' vs 'detached') and use that to drive the skip_chain decision upstream or as a strict instruction.

- `45-105` **P1 / llm_closed_list_instruction**
  - evidence: Close-ups, prop inserts, hand-scale plates, 'background plate' shots, photograph inserts... DO NOT need their own background image
  - why: This defines a semantic classifier for shot types that determines whether a shot should reuse a parent background or create a new node. It relies on the LLM matching these specific concepts in natural language descriptions to decide graph topology.
  - fix: Introduce a 'shot_kind' enum in the input schema and define the node-reuse policy based on those technical categories.

- `48-106` **P2 / scenario_dependent_prompt**
  - evidence: different room state (clean / lived-in / disturbed / heavily-ransacked) ... different active fixed-element zones (e.g. 'open window with torn curtain' vs. 'closed window')
  - why: The prompt contains concrete scenario-specific examples ('heavily-ransacked', 'torn curtain') that function as semantic classifiers for state-based node splitting. These examples bias the LLM towards specific story tropes and may not generalize well to other genres.
  - fix: Replace scenario-specific examples with abstract state categories or project-neutral descriptions of visual variance.

## `prompts/_base/entity_extractor_v2/9.202605130226/system.md`

- `97-101` **P1 / llm_closed_list_instruction**
  - evidence: allowed_space_keys 는 controlled vocab 안에서 선택: main / kitchen / rooftop / stairs / yard / exterior / office. ... 위 controlled vocab 밖 단어 사용 절대 금지.
  - why: This instruction forces the LLM to map arbitrary scenario locations (e.g., bedroom, laboratory, cockpit) into a very limited set of hardcoded semantic categories. It acts as a brittle semantic classifier that limits the system's ability to handle diverse environments and forces lossy 'main' fallback for any non-matching space, which directly affects the deterministic background ID assignment mentioned in line 106.
  - fix: Allow the LLM to generate descriptive keys based on the scenario text (e.g., 'bedroom', 'bridge') or expand the controlled vocabulary to a comprehensive set of common architectural spaces. If a fixed list is required for downstream logic, move the mapping to a post-processing step rather than a hardcoded prompt constraint.

## `prompts/_base/location_consistency/2.202604201230/system.md`

- `5-11` **P1 / blind_string_mutation**
  - evidence: t2i_prompt에 쓰인 `[L##: 설명]` 블록 ... scene_detail이 t2i_prompt에 그대로 삽입
  - why: The pipeline relies on a brittle bracketed string pattern ([L##: ...]) within generated natural-language prompt prose to identify and replace location descriptions. This is blind mutation of semantic text that can fail if the pattern is slightly altered or missing during the generation of the base prompt.
  - fix: Transition to a structured prompt representation where location descriptions are managed as distinct fields or objects in a schema rather than being embedded and replaced within a flat string using bracketed patterns.

## `prompts/_base/scene_extractor_v2/18.202605150955/system.md`

- `47` **P1 / semantic_string_judgment**
  - evidence: (<몽타주> 표시 또는 빠른 컷 전환)
  - why: Instructs the LLM to classify a scene as 'montage' based on the presence of a specific Korean string pattern in the input scenario text, which is a brittle way to infer cinematic structure.
  - fix: Instruct the LLM to identify montage sequences based on semantic characteristics (e.g., rapid time jumps, multiple locations) rather than specific bracketed markers.

- `60` **P2 / scenario_dependent_prompt**
  - evidence: rule_type=possession, remote_identity, visible_body
  - why: Hardcodes a specific 'possession' or 'remote control' story mechanic into the base scene extraction logic. This is scenario pollution that assumes the existence of specific supernatural or sci-fi tropes in arbitrary scenarios.
  - fix: Move scenario-specific entity relationship rules to a dynamic configuration or a specialized prompt layer rather than the base system prompt.

## `prompts/_base/scene_extractor_v2/18.202605150955/turn_scene_detail.md`

- `34-38` **P1 / semantic_string_judgment**
  - evidence: 얼굴/형태 식별 불가 인물 — short_id 사용 금지: 실루엣, 그림자, 창문 반사, 역광, 안개 속 등으로 인물의 얼굴이나 신체 형태를 식별할 수 없는 경우 C##O## short_id를 사용하지 마세요.
  - why: This instruction requires the LLM to perform semantic judgment on the scenario text (identifying visual states like 'silhouette' or 'backlight') to decide whether to include or exclude a character's short_id. This is a brittle routing mechanism that affects entity membership and reference attachment in the generated image.
  - fix: Pass a structured 'visibility_state' or 'is_silhouette' flag for each entity from the previous analysis step instead of asking the LLM to infer it from prose.

- `91-92` **P2 / llm_closed_list_instruction**
  - evidence: 카메라 구도 선택지: low angle / high angle / dutch angle / over-the-shoulder / bird's eye / extreme wide / tight medium
색감 선택지: warm amber / cold blue / high contrast / desaturated / golden hour / neon-lit / silhouette backlight
  - why: The prompt provides a closed list of semantic categories for camera angles and color palettes. If downstream code or validators expect these exact strings, it creates a schema drift risk where the prompt and code must be manually synchronized.
  - fix: Define these options in a central schema/enum and inject them into the prompt dynamically, or ensure the downstream consumer handles arbitrary descriptive text.

- `105-109` **P1 / semantic_string_judgment**
  - evidence: visible_entities 주의사항: 이 씬의 화면에 물리적으로 존재하는 대상만 넣으세요 ... 예: "<container descriptor> 안의 인물들"이 <transport vehicle>에 타고 있다면 → container 는 transport vehicle 에 없으므로 제외
  - why: The LLM is instructed to perform complex semantic filtering of entities based on physical presence and containment logic described in natural language. This is a high-risk area for inconsistent entity membership across scenes.
  - fix: Move entity visibility logic to a dedicated structured analysis step that uses a world-state model rather than relying on LLM interpretation of prose during prompt generation.

## `prompts/_base/visual_world_rules/6.202605021400/system.md`

- `38-51` **P2 / schema_or_enum_drift**
  - evidence: rule_type: possession, transformation, ghost, projection, superpower, body_deformation, time_period, costume, technology, other
  - why: The prompt defines a closed list of semantic categories for open-world supernatural or technical phenomena. While structured as an enum, these categories are inferred from natural language and must be manually synchronized with downstream logic that handles these specific visual types.
  - fix: Ensure this enum is centrally managed in a shared schema and that the prompt dynamically injects the allowed values to prevent drift.

- `57-80` **P1 / llm_closed_list_instruction**
  - evidence: director_notes (유형적 메타 판단 기준) ... 회상/F.B 장면 ... 환각/현시 대상 ... CCTV·모니터·창문 너머 ... 교차편집/몽타주 ... 안개·어둠
  - why: The prompt provides a specific list of semantic categories (Flashback, Hallucination, Media/CCTV, Montage, Occlusion) as 'Correct Examples' for judging 'physical existence'. This functions as a closed-list classifier for a core routing decision—whether an entity is physically present in a scene—which is often consumed by downstream logic or other LLMs using brittle string matching.
  - fix: Define a formal enum for physicality_type in the schema and have the LLM select from it, rather than relying on the LLM to replicate specific phrase patterns in a free-text notes field.
