
    sQ	j                        U d Z ddlmZ ddlmZ ddlmZ ddlmZ dZ	de
d<    ed	
       G d d             Z	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 ddZy)uY  SemanticContractRouter — moderation sanitize 시점의 polarity 보존 인터페이스.

LLM-produced structured SOT (shot_staging / render_prompt_card) 만 읽어
SemanticContract 를 deterministic 하게 산출. 호출자 (scene_image_pipeline 등) 가
contract.sanitizer_constraints 를 PromptSanitizer.sanitize() 에 전달하면 user
prompt 의 SEMANTIC CONSTRAINTS 섹션 + final sanitized_prompt 의 SEMANTIC
OVERRIDE block 으로 2-layer defense.

Patch B-min wiring + Area #2 Q5 closure (Rule 1 = subject_state, Rule 2 =
character_state). subject_state 는 shot_staging v13 schema required field —
character_angles[*].subject_state ∈ {alive, unconscious, dead, severely_injured}.
is_immobilized_state helper (app.core.subject_state SOT) 가 dispatch 결정.

regex / LLM judge / LVM gate 없음 — 다국어 + 정확도 한계 (spec §1.3).
    )annotations)	dataclass)Any)is_immobilized_state)nonepose_lockedimmobilizedtuple[str, ...]SEMANTIC_PRIMARY_MODEST)frozenc                  T    e Zd ZU ded<   ded<   ded<   ded<   ded<   d	ed
<   ded<   y)SemanticContractstrprimary_moder
   tagspose_locked_entity_idsimmobilized_entity_idsztuple[tuple[str, str], ...]source_statestuple[dict[str, Any], ...]evidencedict[str, Any] | Nonesanitizer_constraintsN)__name__
__module____qualname____annotations__     Z/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/semantic_contract_router.pyr   r      s,    
++++..((00r   r   c                    | dk(  ry| t        |      t        |      dd|D cg c]  }t        |       c}d}| dk(  r|j                  dddd       |S |j                  dddd       |S c c}w )uR   sanitizer_constraints 산출. SOT = dataclass field 하나만 (이중 SOT 차단).r   NT)semantic_mode
entity_idsr   preserve_poseoverride_strategy_prefixr   r	   )preserve_subject_stateforbid_state_polarity_rewriteforbid_unharmed_rewriteforbid_active_reactionF)listdictupdate)r   r   source_state_mapr   econstraintss         r   _build_sanitizer_constraintsr/   &   s     v%12./$(&./hT!Wh/#K }$#'*.$(#'	 	 	
  	#(*/$)#(	 	 	
 ! 0s   A,c                `   |xs g D ci c]A  }|j                  d      dk(  r+|j                  d      r|j                  d      r	|d   |d   C }}t               }i }g }| xs i j                  dg       xs g D ]e  }|d   }	t        |	      s|j                  |j                  d      xs d      }
|
s;|j                  |
       |	||
<   |j	                  d|
|	d	       g |xs i j                  d
i       j                  dg       xs g }t               }|D ]~  }|j                  d      dk7  r|j                  d      xs d}|j                  |      }
|
sA|
|vsF|j                  |
       |j	                  d|
|j                  d      xs dd	        |rd}n|rd}nd}|j                  |       t        t        |            }t        |d|t        t        |            t        t        |j                                     t        |      t        |||t        |                  S c c}w )uN  LLM-produced structured SOT 만 읽어 SemanticContract 산출.

    Rule 1 — subject_state ∈ IMMOBILIZED_STATES → immobilized.
    Rule 2 — render_prompt_card.continuity_elements_used.fixed_elements 의
             element_type=='character_state' → pose_locked (단, Rule 1 이 잡지
             않은 entity 만).
    entity_type	characternameshort_idcharacter_anglessubject_state z+shot_staging.character_angles.subject_state)source	entity_idvaluecontinuity_elements_usedfixed_elementselement_typecharacter_statecharacter_namezIrender_prompt_card.continuity_elements_used.fixed_elements.character_name
element_idr	   r   r   r   )r   r   r   r   r   r   r   )getsetr   addappendr+   tuplesortedr   itemsr/   )shot_stagingrender_prompt_cardvisible_entitiesr-   name_to_sidr	   r,   evidence_listentrystatesidr<   r   element	char_namer   pose_locked_idss                    r   build_semantic_contractrS   H   sT    #(b((A55;.155=QUU:EV 	
&	1Z= (    EK')*,M $"))*<bAGRGo&#E*ooeii4:; %C
 	 H& 
	!r	'	,	r	"	)&( 
  EK!;;~&*;;KK 017R	ooi(3k)OOC   e  \2G6G"  " $	${#F;/0O!.$VK%89F#3#9#9#;<=}%:/+;U==Q

 
ws   AH+N)
r   r   r   r
   r,   zdict[str, str]r   r   returnr   )rH   dict | NonerI   rU   rJ   zlist[dict[str, Any]]rT   r   )__doc__
__future__r   dataclassesr   typingr   app.core.subject_stater   r   r   r   r/   rS   r   r   r   <module>r[      s    # !  7 +Q  P $1 1 1+ % )	
 DRR $R +	R
 Rr   