
    ֓j                    h   U d Z ddlmZ ddlZddlZddlZddlmZmZm	Z	m
Z
mZmZmZ ddiZded<   dZd	ed
<   dZd	ed<   dZd	ed<   d7dZ	 	 	 	 d8dZd9dZedd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d:dZ	 	 	 	 	 	 	 	 d;dZ eh d      Zded<   dZ ej6                  d      Zd<dZ	 	 	 	 	 	 	 	 d=dZ	 	 	 	 	 	 	 	 d>dZdZ dZ!	 	 	 	 	 	 d?dZ" G d  d!e#      Z$ eh d"      Z% eh d#      Z& eh d$      Z' eh d%      Z( eh d&      Z) eh d'      Z* eh d(      Z+d)Z,d*Z-d@d+Z.	 	 	 	 	 	 	 	 	 	 	 	 dAd,Z/	 	 	 	 	 	 	 	 	 	 dBd-Z0dd.	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dCd/Z1dddd0	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dDd1Z2dEd2Z3d3Z4 ej6                  d4      Z5	 	 	 	 	 	 	 	 dFd5Z6	 	 	 	 	 	 dGd6Z7y)HuE  W21B-wave-4 C2: shot_projection_card deterministic core.

Pure, deterministic core for the per-shot projection card — the common
shot-observation SOT that both ``background_prompt`` (BG plate) and
``scene_detail`` (full scene) consume so the BG and the final shot t2i
follow the same contract (design brief §3; wiring brief §6/§7).

This module performs NO LLM / VLM / image / DB / ImageAsset I/O. A real
VLM provider is injected via the ``vlm_provider`` slot of
``compute_projection_card`` (mirrors the W21B-wave-4 semantic readback
and W20A2 geometry patterns); the provider itself lives in a separate
module behind an explicit opt-in selector. With ``vlm_provider=None``
(default) the synthetic-fixture path is used and the FP image is ignored.

Design boundaries (design brief §5.1 / wiring brief §11 — scenario
leakage guard):
  - Code performs ONLY structural / exact-ID checks plus reads of the
    LLM-emitted enums. The leakage check is an exact-membership check of
    SELF-DEFINED INTERNAL TOKENS — the integer marker numbers the card
    itself emitted, the fixed English enum/layer literals this module
    declares, and the card_id. It is NOT semantic parsing: it never
    inspects Korean natural-language meaning, spatial words, or labels by
    substring / lexicon / particle / word-boundary matching. Korean
    spatial language (왼쪽/중앙/배경) is deliberately untouched.
  - ``expected_label`` / ``marker_layer`` are carried from the overlay /
    dossier inventory verbatim; the VLM echoes, it does not re-label.
  - The gate fails closed (design brief §5.2 / §9).
    )annotationsN)AnyCallableDictListOptionalSetTuple1z1.202605302212zDict[str, str]PROMPT_VERSION_TO_PACKstrDEFAULT_PACK_VERSIONzopenai/gpt-6-astraPROVIDER_MODELopenaiPROVIDER_NAMEc                R    t         j                  t        |       t        |             S )zMap a prompt-version selector to its concrete pack dir.

    An unknown selector is returned verbatim (NOT silently mapped to a
    default pack) so a mismatch surfaces in provenance instead of hiding.
    )r   getr   )prompt_versions    _/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/shot_projection_card.pyresolve_pack_versionr   2   s!     "%%c.&93~;NOO    c                \   d}g }g }g }g }|||d}|D ]  \  }}| j                  |      xs g D ]  }	t        |	t              s|	j                  d      }
|j                  |
|	j                  d      ||	j                  d      |	j                  d      |	j                  d      xs |	j                  d      d	       t        |
t              st        |
t
              r||   j                  |
         t        t        |      t        |      z  t        |      z        }t        t        | j                  d
      xs g       t        | j                  d      xs g       z
        }t        t        |      t        |      z
        }t        |      t        |      t        |      |||d}||fS )u  Assemble the marker inventory + union registry from one overlay entry.

    The inventory is the COMPLETE marker registry the VLM is shown:
    base markers ∪ transient markers ∪ ignored state-overlay markers, each
    carrying its ``marker_layer`` verbatim. The registry records the
    deterministic union so the validator can tell a transient marker
    (valid, in union) apart from a true input-contract gap
    (``out_of_union_referenced`` — a camera-referenced number absent from
    the union).

    Returns ``(inventory, registry)``. Camera-referenced numbers =
    ``use_numbered_elements`` − ``ignore_numbered_elements``.
    ))base_markers_to_referencebase)transient_markers_to_describe	transient)ignored_state_overlay_markersignored_state_overlay)r   r   r   numberlabelcategoryposition_hintbase_layer_decisionstate_layer_decision)r   expected_labelmarker_layerr!   r"   expected_layeruse_numbered_elementsignore_numbered_elements)base_numberstransient_numbersignored_numbersunion_numberscamera_referenced_numbersout_of_union_referenced)r   
isinstancedictappendintboolsortedset)ov_entrylayers	inventorybase_ntrans_nign_nbucketkeylayermarkernumunioncam_refout_of_unionregistrys                  r   build_union_registryrF   >   s    F
 ')IFGE7USF
Ull3'-2-Ffd+**X&C!&,jj&9$) &

: 6%+ZZ%@

#89 >!::&<=
 #s#JsD,Au$$S)% . * 3v;W-E
:;EHLL017R8
hll56<"
=	>G #g,U34Lv#G_!%=%,#/H hr   c                    t        j                  t        j                  | dd      j	                  d            j                         dd S )u  sha256 of a key-order-normalized JSON encoding of ``obj``.

    Normalization = ``sort_keys=True`` so dict key order does not change
    the hash, while any change to the actual values does (brief §4 —
    ``shot_context_hash`` is normalized, not raw text). 16-hex prefix.
    TF)	sort_keysensure_asciizutf-8N   )hashlibsha256jsondumpsencode	hexdigest)objs    r   _normalized_hashrR      s@     >>

3$U;BB7Kik#2 r   )pack_versiondossierc                    t        |       t        |      t        |      t        |      |dn
t        |      |dn
t        |      t        |      t        |      ||||	|
dS )u  Build the per-card ``source_hashes`` provenance dict (brief §4).

    Each input is normalized-hashed so the card invalidates when any input
    changes. ``semantic`` may be ``None`` when the optional semantic gate
    is disabled — recorded as the ``"not_available"`` sentinel rather than
    a hash, so a disabled gate never looks like a content change.
    ``pack_version`` is the resolved concrete pack dir (Required 3) so a
    selector change OR a pack bump invalidates the card.
    not_available)fp_render_hashsubstrate_hashoverlay_hashgeometry_hashdossier_hashsemantic_hashcamera_rec_hashshot_context_hashr   rS   schema_versionmodelproviderrR   )	fp_render	substrateoverlaygeometrysemantic
camera_recshot_contextr   r_   r`   ra   rS   rT   s                r   compute_source_hashesrj      sn    4 +95*95(1)(3&O4DW4M  (/O5Eh5O+J7-l;($(# r   c                     t        | ||d      S )uG   ``card_cache_key = hash(source_hashes + bg_id + shot_id)`` (brief §4).source_hashesbg_idshot_idrb   rl   s      r   compute_card_cache_keyrp      s     '%GL r   >   r   leftrightcenterpartialvisibleoccluded	midgroundr   
background
foregroundout_of_framer   zSet[str]_ENUM_LITERALS)scene_visible_descriptionbg_plate_visible_descriptionnot_visible_or_occluded_summaryz[A-Za-z0-9_]+c                    t               }| j                  d      xs g D ]F  }|j                  d      }t        |t              s%t        |t              r6|j                  |       H |S )Nvisible_itemsmarker_number)r6   r   r0   r3   r4   add)
vlm_outputoutitemns       r   _emitted_marker_numbersr      sW    EC/525HH_%ajD&9GGAJ 6 Jr   c                   g }t         j                  |       D ch c]  }|j                          }}t        t        |z        D ]  }|j                  d|         | j                         }t        |      D ]e  }t        j                  d| d|       s5t        j                  d| d|      st        j                  d| d|      sR|j                  d|        g |r|| v r|j                  d       |S c c}w )u   Return labels for any self-defined internal token leaked in ``prose``.

    NOT semantic parsing — exact membership of our own tokens only.
    zenum_literal:z#\s*z\bz\bmarker\s*z\bnumber\s*zmarker_number:card_id)_ASCII_TOKEN_REfindalllowerr5   r{   r2   research)	prosemarker_numbersr   leaksttokenslitlowr   s	            r   _find_internal_token_leaksr     s     E "1!8!8!?@!?Aaggi!?F@nv-.}SE*+ /
 ++-CN#IIaSmU+yyKs"-s3yyKs"-s3LL>!-. $ 7e#YL' As   C:c                Z    t        | xs dt        |xs i       t        |xs d            S )u  Public C4 consumer guard: reuse the C2 self-defined internal-token leak
    check on a card's plate prose before BG injection.

    Single-sourced with the C2 gate (no duplicate matcher — the user's
    no-duplicate-lexicon rule): same exact-membership semantics on our OWN
    tokens (integer marker numbers the card emitted, fixed English enum /
    layer literals, card_id). It is NOT semantic parsing — Korean spatial
    language is never inspected.
     r   r   r   )r   r   r   )plate_proser   r   s      r   find_plate_prose_leaksr   &  s2     &R.z/?R@GMr" r   )r   r_   r   rn   ro   fp_idsemantic_gate_statesubstrate_kindsubstrate_statusrm   r   g      ?c                \   t         D cg c]	  }|| vs| }}|rddd| gg dS g }| j                  d      xs i }| j                  d      |k7  }|r|j                  d       | j                  d      }|d	k(  r|j                  d
       | j                  d      dk(  }|r|j                  d       | j                  d      xs i }	t        |	j                  d      xs g       }
|
r|j                  d|
 d       |j                  d      }|dk(  }|dk(  }|r|j                  d       n|r|j                  d       |j                  d      xs i j                  d      dk(  }|r|j                  d       |j                  d      xs g }|r|j                  d|        t	        |      }t        | j                  d      xs d      }g }t        D ]Q  }|j                  |      xs d}t        |t
              s)t        |||      D ]  }|j                  | d |         S |r|j                  d!|        |rd"}n|rd#}n|
rd$}n|rd%}nd&}t        |      xs" |xs t        |
      xs |xs |xs	 |d	k(  xs |}|j                  d'      }t        |t        t        f      xr t        |t               xr	 |t        k  }t        |      xs |xs	 |d(k(  xs |}|rd}n|rd(}|j                  d)       nd*}|j                  d+|j                  d,             ||||dS c c}w )-u  v0 deterministic gate (design brief §9 / wiring brief §9).

    Returns ``{validator_state, card_state, diagnostics, leaks}``.

    ``validator_state``: ``ok | leak_detected | contradiction |
    marker_contract_gap | plate_transient_leak | malformed``.
    ``card_state``: ``pass | needs_review | blocked``.

    HARD block (→ ``blocked`` → v10 fallback): internal-token leak, VLM
    ``self_consistency=contradictory``, camera-referenced marker absent
    from the base∪transient∪ignored union, ``plate_description_excludes_
    transient='no'``, stale ``source_hashes``, ``semantic_gate_state=
    needs_fix``, or ``substrate_status='missing'``.

    SOFT hold (→ ``needs_review`` → diagnostic only, still injectable):
    non-empty ``missing_inputs``, low confidence, ``semantic_gate_state=
    needs_review``, or ``plate_description_excludes_transient='uncertain'``.

    ``vlm_reported_state`` is reference-only — the validator re-derives the
    authoritative state and never blindly trusts the VLM self-report.
    	malformedblockedzmissing envelope keys: )validator_state
card_statediagnosticsr   r   rm   u;   source_hashes mismatch (stale) — card invalid, regenerater   	needs_fixu9   semantic_gate_state=needs_fix — FP self-fidelity failedr   missingu4   substrate_status=missing — no projection substratemarker_registryr/   uN   camera_rec referenced marker(s) absent from base∪transient∪ignored union: u    — input gap, BLOCK$plate_description_excludes_transientno	uncertainuK   plate_description_excludes_transient=no — transient in plate prose, BLOCKu?   plate_description_excludes_transient=uncertain → needs_reviewself_consistencyprose_matches_structuredcontradictoryu?   vlm self_consistency=contradictory — prose↔structured BLOCKmissing_inputsu0   vlm missing_inputs non-empty (→needs_review): r   r   r   :z!internal token leakage in prose: leak_detectedcontradictionmarker_contract_gapplate_transient_leakok
confidenceneeds_reviewuB   needs_review (missing_inputs/low-conf/semantic) → BG inject holdpassz(ref) vlm_reported_state=vlm_reported_state)_REQUIRED_ENVELOPE_KEYSr   r2   listr   r   _PROSE_FIELDSr0   r   r4   r3   float_LOW_CONFIDENCE_FLOOR)cardcurrent_source_hasheskmissing_keysdiagsvlmstalesemsubstrate_missingregrD   
plate_exclplate_transient_blockplate_transient_reviewr   r   r   r   r   fieldtextlkr   
hard_blockconflow_conf	soft_holdr   s                               r   validate_shot_projection_cardr   L  s   4  7H6!!4-A6LH*#5l^DE	
 	
 E
((<
 
&BC HH_%)>>ERS (((
)C
kPQ !34	AKL (($
%
+C 9:@bAL11=>SU	
 ?@J&$.';6Y	
 
 VW 
#	$	*//0JK	  VW WW-.4"NGGWXY -S1N$((9%+,GEwwu~#$$,~w
B LLE7!B4)
	  8@A )	)	/	0 	U 			 !	 		
 +	   77< D$e-mjt6L2LmQUXmQmH^ 	"	". 	" "	  
	#
YZ
	LL,SWW5I-J,MNO + 	 g Is
   	L)L)c                      e Zd ZdZy)ProjectionCardErrorz;Fail-closed signal for projection-card assembly / dispatch.N)__name__
__module____qualname____doc__ r   r   r   r     s    Er   r   >   rt   ru   rv   rz   >   rq   rr   rs   >   rw   rx   ry   >   r   r   r   >   r   yesr   >   r   low_confidenceinsufficient_evidence>   r   
consistentr   
r   r&   r%   
visibilityhorizontal_band
depth_bandocclusion_noteevidence
source_refr   statusr   rn   ro   camera_pose_sourcer   r|   r}   r   r~   r   r   r   r   r   c                   i }| xs g D ]  }t        |t              s|j                  d      }t        |t              s6t        |t              rGt        |j                  dd            t        |j                  dd            d||<    |S )u]  ``{number: {expected_label, marker_layer}}`` for exact-ID echo.

    Both fields are the verbatim SOT the VLM must echo — it observes, it
    does NOT re-label OR re-classify. Carrying marker_layer here lets the
    validator reject a transient marker echoed as base (which would defeat
    the base/transient separation + plate-purity gate).
    r   r%   r   r&   )r%   r&   )r0   r1   r   r3   r4   r   )r9   r   entryrA   s       r   _inventory_mapr     s     &(Cb%&ii!c3
3(="%eii0@"&E"F #EIInb$A BCH ! Jr   c                   g }t        | t              sddt        |       j                   dgdS t        D cg c]	  }|| vs| }}|r|j                  d|        | j                  d      dk7  r$|j                  d| j                  d      d       d	|fd
|fd|ffD ]B  \  }}	| j                  |      |	k7  s|j                  | d| j                  |      d|	       D t        | j                  d      t              r| j                  d      s|j                  d       t        |      }
| j                  d      }t        |t              s|j                  d       g }t        |      D ]+  \  }}d| d}t        |t              s|j                  | d       2t        D cg c]	  }||vs| }}|r|j                  | d|        c|d   }t        |t              rt        |t              r|j                  | d|d       dt        fdt        fdt         fdt"        ffD ]D  \  }}||   }t        |t              r||vs |j                  | d| d|dt%        |              F dD ]3  }t        ||   t              r|j                  | d| d ||   d       5 |d!   }|=t        |t        t&        f      rt        |t              r|j                  | d"|d       ||
vr|j                  | d#| d$       |
|   }t        |d%   t              r.|d%   |d%   k7  r#|j                  | d&|d%   d'|d%   d(| d)       |j                  d      |d   k7  s|j                  | d*|j                  d      d+|d   d(| d,       . | j                  d-      }|t(        vr$|j                  d.|dt%        t(                      | j                  d/      }|t*        vr$|j                  d0|dt%        t*                      | j                  d1      }t        |t              r|j                  d2      t,        vr|j                  d3       n0t        |j                  d4      t              s|j                  d5       t.        D ]6  }t        | j                  |      t              r#|j                  | d6       8 | j                  d!      }|;t        |t        t&        f      rt        |t              r|j                  d7|d       d8D ]M  }| j                  |      }t        |t              rt1        d9 |D              s7|j                  | d:|d       O | |dS c c}w c c}w );uv  Pure JSON-shape + exact-ID validator for a projection-card VLM output.

    Returns ``{"ok": bool, "blockers": [str]}``. Structural / enum / exact-ID
    checks only — never inspects the free-text prose for meaning. A marker
    the VLM reports must exist in the supplied inventory and echo the
    inventory's verbatim label (the VLM observes; it does not re-label).
    Fzoutput not a dict ())r   blockersz#missing required top-level fields: r   r   zstatus must be 'ok' (got r   rn   ro   z mismatch: provider=z vs r   zFcamera_pose_source must be a non-empty string (selected, not invented)r   zvisible_items must be a listzvisible_items[]z not a dictz missing required fields: r   z .marker_number must be int (got r&   r   r   r   .=z not in )r%   r   r   r   z must be a string (got r   z(.confidence must be float|int|None (got z.marker_number=z not in supplied inventoryr%   z.expected_label=z  disagrees with inventory label=z for marker #z (re-label forbidden)z.marker_layer=z  disagrees with inventory layer=z (re-classify forbidden)r   z%plate_description_excludes_transient=r   zvlm_reported_state=r   r   z1self_consistency.prose_matches_structured invalidnotesz'self_consistency.notes must be a stringz must be a stringz$confidence must be number|null (got )r   r   c              3  >   K   | ]  }t        |t                 y w)N)r0   r   ).0xs     r   	<genexpr>z&validate_vlm_output.<locals>.<genexpr>  s     +PCq
1c0B,BCs   z must be a list[str] (got )r0   r1   typer   _VLM_OUTPUT_REQUIREDr2   r   r   r   r   	enumerate_VISIBLE_ITEM_REQUIREDr3   r4   _LAYER_VALUES_VISIBILITY_VALUES_HBAND_VALUES_DBAND_VALUESr5   r   _PLATE_EXCL_VALUES_REPORTED_STATE_VALUES_SELF_CONS_VALUESr   any)outputr9   r   rn   ro   r   r   missing_topr>   wantinv_mapitemsidxr   prefixfr   r   r   allowedval
text_fieldr   expplaterepscprose_fieldtop_conf
list_fields                                 r   validate_vlm_outputr  '  s    Hfd#,?V@U@U?VVW*X)YZZ 3F2avo12KF=k]KLzz(t#3FJJx4H3K1MN&%(89g:NO	T::c?d"OOse#7

37J$thWX P fjj!56<FJJE 	`aY'GJJ'EeT"67u%	T!#a($%OOvhk234F414FOOvh&@	JK!1c":a+>OOvh&Fqe1MN]+-..=)	
NE7 u+Cc3'3g+=6(!E7!C7(6'?BS TU
 YJd:.46(!J<7NtT^O_Nbbc de Y L!ZsEl%CzRVX\G]OOvh&NthVWXY GOOvhoaS8RST!*C$/0#64@P;QUXYiUj;jh.t4D/E.H I''*+;'<&?}QCOdf xx'3~+>>hnTXXn-E,H I''*>':&=]1#MegW &` JJ=>E&&?yQWXjQkPlmn
**)
*C
((-cWHVDZ=[<\]^	&	'Bb$266*D#EM^#^KLw-AB$&**[137OO{m+<=> % zz,'HZ3,%G:V^`dKe>xl!LM7
jj$#t$+PC+P(POOzl*DSG1MN 8
 ,H55y G4 Gs   	U9U9+	U>5U>c                (   g }| xs g D ]r  }t        |t              s|j                  d      }|j                  |t	        |j                  dd            t	        |j                  dd            ddddd	d
dd
       t d|||d
|dddddddddg dgdS )u  Return a non-authoritative synthetic projection-card VLM output.

    Mirrors the semantic-readback synthetic fixture: it asserts NOTHING
    about real image content, so the gate must never promote it to a
    production pass. Every marker is echoed verbatim from the inventory;
    prose is empty, the plate self-report is ``uncertain``, and
    ``vlm_reported_state`` is ``insufficient_evidence`` — so a synthetic
    card routes to ``needs_review`` (fail-closed), not ``pass``.
    r   r&   r   r%   r   rz   rs   rw   u9   synthetic_fixture placeholder — no real VLM observationsynthetic_fixtureNr   r   r   r   u)   synthetic_fixture — no real observation)r   r   zwsynthetic_fixture: placeholder output, not a real VLM observation; the gate must not promote this to a production pass.r   )r0   r1   r   r2   r   )r9   r   rn   ro   r  r   rA   s          r   compute_synthetic_fixturer    s      #%Eb%&ii!!$ #EIInf$E F"%eii0@"&E"F,#+)"$W1"	
	 !& 1%'(*0;+-5(3@
 C
% r   )r   c                ,    | |||||||||	|
|||xs i dS )zBAssemble the card envelope (provenance + vlm_output) for the gate.r   r_   r   r`   ra   rn   ro   r   r   r   r   rm   r   r   r   r  s                 r   build_card_enveloper    s=    & ((2(,& *0b r   )vlm_providerfp_image_pathprompt_contextc                   |t        | |||      S  || |||||      }t        |t              s"t        dt	        |      j
                   d      t        || |||      }|d   s%dj                  |d	         }	t        d
|	dd        |S )u  Return a projection-card VLM output for ``(bg_id, shot_id)``.

    With ``vlm_provider=None`` (default) the synthetic-fixture path is used
    and ``fp_image_path`` / ``prompt_context`` are ignored. When a provider
    callable is passed it is invoked as ``vlm_provider(inventory=...,
    fp_id=..., bg_id=..., shot_id=..., fp_image_path=...,
    prompt_context=...)`` — ``prompt_context`` carries the richer real-VLM
    inputs (camera recommendation, registry, shot/scene context) — and its
    return value runs through the FULL ``validate_vlm_output`` contract;
    any non-conforming return raises ``ProjectionCardError``. The
    dispatcher never returns an unvalidated provider payload.
    N)r9   r   rn   ro   )r9   r   rn   ro   r  r  z vlm_provider returned non-dict (r   )r  r9   r   rn   ro   r   z; r   z'vlm_provider output failed validation: i  )r  r0   r1   r   r   r   r  join)
r9   r   rn   ro   r  r  r  r   resjoineds
             r   compute_projection_cardr#     s    , (uE7
 	
 #%C c4 !$DT#YEWEWDXXY"Z[[
iuE7C t93z?+!$KFSWTWL>"Z[[Jr   c           	        | xs i j                  d|       xs i }i }t        |t              rI|j                         D ]4  \  }}t        |t              s||t	        |j                  d|            <   6 |S t        |t
              r;|D ]6  }t        |t              s|j                  d      s&||t	        |d         <   8 |S )Noverlaysrn   )r   r0   r1   r  r   r   )overlay_payloadovr   r>   r   s        r   _overlay_entriesr(  0  s    

R	$	$Z	A	GRB%'C"d((*JC%&49C		'3/01 % J	 
B	E%&599W+=+0Cg'(  Jr   )camera_directionkey_bg_elementsframe_spatial_contractframing_scalelighting_moodsubject_action	shot_typezS(\d+)_Shot(\d+)$c                   i }dddd}t         j                  | xs d      }|rt        |j                  d            }t        |j                  d            }|xs i j	                  d      xs g D ]Z  }t        |t              s|j	                  d      |k(  s)|j	                  d      |k(  s>t        D 	ci c]  }	|	|v s|	||	    }}	 n |xs i j	                  d      xs |xs i j	                  d	      xs g }
t        |
t              rt        |
j                               }
|
D ]  }t        |t              s|j	                  d      |k(  s)t        |j	                  d|            t        |j	                  d
d            t        |j	                  d      xs( |j	                  d      xs |j	                  d      xs d      d} n | ||dS c c}	w )a  Assemble the normalized shot_context for one ``shot_id`` (Required 1).

    Returns ``{shot_id, shot_intent, scene}`` where ``shot_intent`` is the
    upstream shot_staging subset that conditions framing and ``scene`` is
    the verbatim scene segment text/heading. Used for BOTH the
    ``shot_context`` source-hash input (so a shot/scene content change
    invalidates the card) AND the provider prompt_context (so the real VLM
    produces shot-conditioned prose). An unmatched shot_id yields a stable
    empty context (no crash).
    r   )	scene_refheadingr         shotsscene_index
shot_indexsegmentsscenesr2  r   
scene_textbody)ro   shot_intentscene)_SHOT_ID_REmatchr3   groupr   r0   r1   _SHOT_INTENT_FIELDSr   valuesr   )ro   shot_staging
scene_saver<  r=  r?  scene_noshot_nor   r
  r8  segs               r   build_shot_contextrH  S  s     #%KR8Egm,Eu{{1~&ekk!n%"(b--g6<"<E5$'IIm,8IIl+w6 *=)<AU
AuQxK)<    = 2"":.  b%%h/ 	
 h%HOO-.HCc4(ww}%1!$SWW]H%E!F"3779b#9: 77<0776? 		    {UKK9s   8	GGc                P   t        |       }|xs i j                  d      xs i }g }|j                         D ]l  \  }}|j                  |      xs i }|j                  d      xs g D ]:  }|j                  |t	        |      t	        |j                  dd            |d       < n |S )u  Enumerate the ``(bg_id, shot_id, fp_id, ov_entry)`` cards to build.

    One card per ``(bg_id, shot_id)`` — every shot in the background's
    ``applies_to_shots`` (design brief §4.1, per-shot SOT). Anchor / plate
    grouping is deferred to the plan vNext (C3); C2 produces the per-shot
    cards the planner later selects from. A bg with no overlay entry or no
    ``applies_to_shots`` yields no targets (it cannot be projected).
    background_catalogapplies_to_shotsr   r   )rn   ro   r   r7   )r(  r   r  r2   r   )	r&  master_planentriescatalogtargetsrn   r7   bg_metaro   s	            r   iter_card_targetsrQ    s     /G b%%&:;ArG$&G"==?x++e$*{{#56<"<GNN""7| gr!:; (	 = + Nr   )r   r   returnr   )r7   Dict[str, Any]rR  z+Tuple[List[Dict[str, Any]], Dict[str, Any]])rQ   r   rR  r   )rc   r   rd   r   re   r   rf   r   rg   r   rh   r   ri   r   r   r   r_   r3   r`   r   ra   r   rS   r   rT   r   rR  rS  )rm   rS  rn   r   ro   r   rR  r   )r   rS  rR  Set[int])r   r   r   rT  r   r   rR  	List[str])r   r   r   rS  r   r   rR  rU  )r   rS  r   rS  rR  rS  )r9   List[Dict[str, Any]]rR  zDict[int, Dict[str, str]])r  r   r9   rV  r   r   rn   r   ro   r   rR  rS  )
r9   rV  r   r   rn   r   ro   r   rR  rS  )r   r   r_   r3   r   r   r`   r   ra   r   rn   r   ro   r   r   r   r   r   r   r   r   r   rm   rS  r   rS  r   Optional[Dict[str, Any]]rR  rS  )r9   rV  r   r   rn   r   ro   r   r  z'Optional[Callable[..., Dict[str, Any]]]r  zOptional[str]r  rW  rR  rS  )r&  rS  rR  zDict[str, Dict[str, Any]])ro   r   rC  rS  rD  rS  rR  rS  )r&  rS  rL  rS  rR  rV  )8r   
__future__r   rK   rM   r   typingr   r   r   r   r   r	   r
   r   __annotations__r   r   r   r   rF   rR   rj   rp   	frozensetr{   r   compiler   r   r   r   r   r   r   	Exceptionr   r   r   r   r   r   r   r  r   r   r   r  r  r  r#  r(  rA  r>  rH  rQ  r   r   r   <module>r^     sT  8 #   	 B B B +./?)@  @, c ,* *s P==0=F	2 -++ + 	+
 + + + + + + + + + + +\	!	 	 		
 		F % $ "**-.#+69@%3>A*   R
R *R 	RnF) F QR 56CDHI9: "#TU JK    &*s6s6 $s6 	s6
 s6 s6 s6r9#9 9 	9
 9 9V 15!! ! 	!
 ! ! ! ! ! ! ! ! "! ! .!  !!T =A#'/3*#* * 	*
 * :* !* -* *`$  bjj-.9L9L !9L 	9L
 9Lx#   	r   