
    Oj0                       d Z ddlmZ ddlZddlmZ ddlmZmZm	Z	m
Z
  ej                  e      ZdZdZdZd	Zd
Z G d de      ZddZ eg       ZddZddZddZddZ G d d      Zdddd	 	 	 	 	 	 	 ddZddZ	 	 d dZ	 	 	 	 d!dZy)"u  정책 owner(character·prop)의 **사람이 확인한** 조사 사진 → canonical ref 생성의 실제 입력.

★★★실측 (Codex BLOCK 2026-09-02): 중앙 정책(`episode_reference_policy`)은 verified
C/P 를 `required_refs` 의 **ID 로만** 올렸다. 그런데 `ReferencePhase1Service` 의
`extra_references` 는 의존 엔티티(변형/기본형)의 ref map 만 봤다 — 중앙 조사에서
받은 사진 bytes 를 읽는 길이 **없었다**. 그래서 P01 의 사람이 확인한 사진은
「P01 참조가 필요하다」는 boolean 만 만들고, 실제 P01 canonical ref 는 그 사진
**없이** 다시 생성됐다. 조사를 했는데 결과가 그림에 안 닿는 결함이다.

이 모듈은 그 사진을 canonical ref 생성의 **labeled extra reference** 로 잇는다.

결속 규칙:
  - 키는 typed id 하나 — 중앙 CP 의 `ledger_row.final_id` == `EntityCanon.short_id`.
    이름·부분문자열 결속 없음.
  - 갈래는 계약이 정한다 — `canonical_ref_owner_types()` (= 정책 집행 갈래). 배경
    sidecar 갈래(location·location_part)는 여기 **안 든다** — 그 갈래의 집행자는
    sidecar 하나다.
  - 붙여도 되는가는 `reference_acquisition.usable_as_reference` **한 곳**이 판단한다
    (selected ∧ 사람이 verified). unverified·rejected·unavailable → 0장.
  - 사진 bytes·sha 는 sidecar·probe 와 **같은 helper** 로 읽는다
    (`grounding_sidecar_writer.row_content_sha256` · `resolved_reference_path`).
  - 이미지 호출 수를 늘리지 않는다 — 같은 한 번의 생성 호출에 입력만 더 실린다.
    사진이 없으면 기존 C/P 생성 동작은 한 바이트도 안 바뀐다.
    )annotationsN)Path)AnyDictListOptionaluq   Real-world grounding photo of this subject (human-verified) — match its actual form, materials and proportions:grounding_inputsgrounding_input_digestz1.202609021800reference_acquisitionc                      e Zd ZdZy)GroundingInputErroruM   중앙 CP 의 verified 줄이 canonical ref 입력이 되기에 모자란다.N)__name__
__module____qualname____doc__     i/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/grounding_canonical_ref_inputs.pyr   r   2   s    Wr   r   c                   ddl }ddl}t        d | xs dD              }t        |D cg c]  }t	        |       c}d}|j                  |j                  |d      j                  d            j                         dd	 S c c}w )
uF  한 subject 의 **입력 지문** — 결정적. ★「입력 없음」도 **명시 값**이다.

    ★★★Codex BLOCK (2026-09-02, 재개 계약): verified 사진이 생성 입력이 됐는데
    resume 은 「primary 자산 파일이 있다」만 보고 already_done 으로 넣었다.
    그러면 P01 을 나중에 verified 로 바꾸거나 사진 SHA 가 바뀌어도 옛 canonical
    ref 가 영구히 primary 다. 입력의 좌표를 정렬해 지문으로 접고, 자산과 CP 행
    둘 다에 남기며, resume 은 둘이 지금 값과 같을 때만 되쓴다.
    r   Nc              3     K   | ]]  }t        |j                  d       xs d      t        |j                  d      xs d      t        |j                  d      xs d      f _ yw)final_id acquisition_identitycontent_sha256Nstrget).0gs     r   	<genexpr>z)grounding_input_digest.<locals>.<genexpr>B   s_      NAK1 quuZ(.B/QUU;Q5R5XVX1Yquu-.4"57AKs   A#A%r   )contractinputsT	sort_keysutf-8   )	hashlibjsonsortedGROUNDING_INPUT_DIGEST_CONTRACTlistsha256dumpsencode	hexdigest)refsr'   r(   rowsrpayloads         r   r
   r
   6   s      NAEN ND:+/04a$q'402G>>$**W*=DDWMNXXZ[^\^__ 1s   Bc                F    | s	|t         k(  S t        |       t        |      k(  S )u2  저장된 지문이 지금 입력과 같은가.

    ★옛 자산(지문 칸이 없다)은 **지금 입력이 없을 때만** 신선하다 — 이 기능 전에
    구운 legacy/v2 프로젝트의 참조 수백 장을 resume 마다 다시 굽지 않는다.
    지금 입력이 있으면(사람이 확인한 사진) 옛 자산은 stale 이다 — 그 사진 없이
    구워진 것이므로. 저장된 지문이 있으면 **정확히 같아야** 한다: grounded 자산이
    rejected 로 바뀌면 지금 지문은 EMPTY 라 달라져 다시 굽는다.
    )EMPTY_INPUT_DIGESTr   )storedcurrents     r   is_freshr8   M   s&     ,,,v;#g,&&r   c                    ddl }t        | dd      }|sy	 |j                  |      }t        |t              sy|j                  t              }|rt        |      S dS # t        t        f$ r Y yw xY w)uF   `ImageAsset.pipeline_metadata_json` 에 남긴 지문. 없으면 None.r   Npipeline_metadata_json)
r(   getattrloads
ValueError	TypeError
isinstancedictr   GROUNDING_INPUT_DIGEST_KEYr   )assetr(   rawparsedgots        r   stored_digest_of_assetrF   [   st    
%14
8CC fd#
**/
0C3s8$$ 	" s   A A.-A.c                R    ddl m} ddlm}  | || t	        |            xs i       S )uM   이 프로젝트의 grounding 모드 — production 의 같은 두 함수로.r   )resolve_grounding_mode)_load_project_config)app.core.grounding_moderH   #app.services.step_execution_servicerI   r   )db
project_idrH   rI   s       r   project_grounding_moderN   l   s$    >H!"6r3z?"K"QrRRr   c                B    ddl m} t         |t        |                   S )ue   중앙 조사가 **반드시 돈** 판인가 — `v2_chunk` 만. legacy/v2 는 CP 없음 → 옛 길.r   )uses_chunk_producer)rJ   rP   boolr   )grounding_moderP   s     r   central_cp_is_requiredrS   t   s    ;#C$7899r   c                       e Zd ZdZddZddZy)_CheckpointReaderu  `central_cp_with_reviews(runner)` 가 읽는 **네 속성만** 가진 읽기 전용 모양.

    ★스텝이 아닌 자리(orchestrator — RefImageGenStep 과 API 가 **둘 다** 여기로
    온다)에서 production 과 같은 reader 를 쓰기 위한 어댑터다. 판정을 얹는
    법은 `grounding_fidelity_review.central_cp_with_reviews` 한 곳이 갖고 있다 —
    여기서 CP 를 따로 읽고 판정을 따로 얹으면 두 벌이 된다.
    c                R    || _         t        |      | _        t        |      | _        y )N)rL   r   rM   
episode_id)selfrL   rM   rW   s       r   __init__z_CheckpointReader.__init__   s    j/j/r   c                    dd l }ddlm} t        |j                        | j
                  z  dz  dz  | j                  z  |z  dz  }|j                         sy |j                  |j                  d            S )Nr   )settingscheckpointsepisodeszmanifest.jsonr%   )encoding)
r(   app.core.configr[   r   projects_dirrM   rW   is_filer<   	read_text)rX   step_idr(   r[   ps        r   _load_prev_checkpointz'_CheckpointReader._load_prev_checkpoint   sq    ,(''(4??:]J OO,.568GHyy{zz!++w+788r   N)rL   r   rM   r   rW   r   returnNone)rc   r   rf   zOptional[Dict[str, Any]])r   r   r   r   rY   re   r   r   r   rU   rU   {   s    *
	9r   rU   )rootrR   fail_closedc                  ddl m} ddl m} ddlm} ||xs t        | |      }	t        |	      }t        | ||      }
|
j                  d      }|r|j                  |       |i S t         |             }|t        |      n|j                         }i }|j                  d      xs i j                  d      xs d	D ]S  }|j                  d
      xs i }t        |j                  d      xs d      |vr:|j                  |      sLt        |j                  d      xs d      }|st!        |j                  d      d      |j#                  |      }|j%                  ||      }|r||z  nd}|r|r||j'                         st!        | d|d      |j)                  |g       j+                  |t        |j                  d      xs d      t        |j                  d      xs d      |t        |      |j-                         d       V |rNt.        j1                  d|j3                         D ci c]  \  }}||D cg c]
  }|d   dd  c} c}}}       |S c c}w c c}}}w )u)  final_id → 그 subject 의 **사람이 확인한** 사진들(bytes 포함).

    ★`v2_chunk` 판(`fail_closed`)에서 중앙 CP 가 없거나 깨졌으면 **provider 앞에서
    선다** — public `/generate-reference-images` 가 중앙 조사 전에 눌리면 고증 없는
    canonical ref 가 만들어지고 resume 이 그것을 영구 재사용한다 (Codex BLOCK
    2026-09-02 E). legacy/v2 는 CP 없음 → `{}` 옛 길 그대로.
    ★판정 기록을 **못 읽으면** 선다(`ReviewsUnreadable`) — 「없다」가 아니다.
    r   )grounding_sidecar_writer)r   )canonical_ref_owner_typesNr   datar1   r   
ledger_row
owner_typer   r   research_subject_idum    는 verified 인데 `final_id` 가 없다 — 어느 canonical ref 에 실을지 모르는 채 안 싣는다)rh   u3    는 verified 인데 사진 파일이 없다 (path=uT   ) — 판정은 그 bytes 위에 선 것이라 다른 것을 대신 싣지 않는다identity)r   rq   r   r   pathbyteszgrounding canonical inputs: %sr      )app.modules.pipelinerk   r   .app.modules.pipeline.grounding_entity_contractrl   rN   rS   rU   re   assert_central_checkpointsetr   default_reference_rootr   r   usable_as_referencer   resolved_reference_pathrow_content_sha256ra   
setdefaultappend
read_bytesloggerinfoitems)rL   rM   rW   rh   rR   ri   swrarl   modereadercpownersbaseoutrowledgerr   relsharr   kvr   s                           r   verified_policy_referencesr      s}    D@ G!7J!G,T2r:z:F		%	%&=	>B
$$R(	z	*,-F)4:r/H/H/JD+-C%2**628b8&,"vzz,'-2.f<%%c*vzz*-34%77014 5\ \] ] ((-##Cd#3 tczd#T\\^%*OPSw Wa ab b 	x$++ CGG$9:@bA$'
(;(Ar$B!I__&-
 	% 94 4JM))+V+$!QQ1=1a+,Sb11==+V	XJ >Vs   9I*I%I*%I*c                    | D cg c]/  }dD ci c]!  }|t        |j                  |      xs d      # c}1 c}}S c c}w c c}}w )u4   자산에 남길 출처 — bytes 를 뺀 좌표만.)r   rq   r   r   r   r   )r0   r   r   s      r   provenance_ofr      s`      PQO )*QAEE!HN##OQ  Q s   	A&>AAc                p    | xs i j                         D ci c]  \  }}|t        |       c}}S c c}}w )uc   final_id → 입력 지문. 없는 subject 는 부르는 쪽이 `EMPTY_INPUT_DIGEST` 로 읽는다.)r   r
   )grounding_referencesfidr0   s      r   digests_by_final_idr      sJ     38b??ACA	T '--AC C Cs   2c               n   ddl }ddl}t        | ||      }|j                  d      }|
t        dd}nBt        | |||d      }	t        t        t        t        |	      j                                     d}|j                  |j                  |d	
      j                  d            j                         dd S )uj  이 에피소드 canonical ref 입력 **전체**의 지문 — `RefImageGenStep._config_hash`
    가 `v2_chunk` 에서 접는다(Codex D). ★여기서는 CP 부재에 서지 않는다 — 지문은
    늘 계산돼야 하고(부재는 `"central_cp": "absent"` 로 접힌다), 서는 문은 생성
    입구(`verified_policy_references(fail_closed=True)`)다.r   Nr   absent)r!   
central_cpF)rM   rW   rR   ri   )r!   by_final_idTr#   r%   r&   )r'   r(   rU   re   r*   r   r@   r)   r   r   r,   r-   r.   r/   )
rL   rM   rW   rR   r'   r(   r   r   r3   r0   s
             r   canonical_grounding_digestr      s     r:z:F		%	%&=	>B	z/N19#; *"PZ9GUZ\>"&v.A$.G.M.M.O'P"QS>>$**W*=DDWMNXXZ[^\^__r   )r0   r   rf   r   )r6   Optional[str]r7   r   rf   rQ   )rB   r   rf   r   )rL   r   rM   r   rf   r   )rR   r   rf   rQ   )rL   r   rM   r   rW   r   rh   zOptional[Path]rR   r   ri   zOptional[bool]rf   Dict[str, List[Dict[str, Any]]])r0   zList[Dict[str, Any]]rf   zList[Dict[str, str]])r   r   rf   zDict[str, str])
rL   r   rM   r   rW   r   rR   r   rf   r   ) r   
__future__r   loggingpathlibr   typingr   r   r   r   	getLoggerr   r   GROUNDING_REFERENCE_LABELGROUNDING_INPUTS_KEYrA   r*   CENTRAL_STEP_IDRuntimeErrorr   r
   r5   r8   rF   rN   rS   rU   r   r   r   r   r   r   r   <module>r      s   0 #   , ,			8	$
8 
 * 5  #3 )X, X`( ,B/ '%"S:9 96 7;?C=A=%3=/<= -;= %D	=@C+C`/2`7:`r   