
    Oj$                        d Z ddlmZ ddlZddlmZmZmZmZm	Z	 dZ
dZ G d de      Zdd	Zdd
ZddZddZ	 	 	 	 ddZddZ	 	 	 	 ddZddZ	 	 	 	 ddZy)u  야외 구조물 **형태 참조**의 보충 획득 — 중앙 조사가 산 것에 없는 `structure_form` 의무를 21.915 가 **같은 경계**로 산다.

★설계 `docs/design/2026-09-03-grounding-v2-21915-adapter.md` §8 (HITL 0 · Codex 좁힌 (가)):
  - 의무는 **결정적**으로 만든다 — 그룹 → 실외 장소 하나(`members[].loc_id`) → 중앙 CP·보충 CP 에 `structure_form`
    줄이 없는 장소마다 하나. 뼈대는 그 장소의 중앙 ledger_row 를 복사하고 부류 이름을 place spec 이 낸
    `structure_desc`(런타임 LLM 산출 · 고정 명사 아님)로 바꾼다. 원고 근거는 그대로.
  - 사는 것은 중앙 조사기(`grounding_central_acquisition.run`) 그대로 — 5단계(텍스트 조사 → 좁은 검색 → CRITERIA
    판정 → 넓은 재검색 → 반드시 한 장). 여기서는 사지 않는다.
  - 남기는 곳은 **별도 CP**(`reference_acquisition_supplement`) — append-only. 중앙 CP 는 안 고친다.
  - 읽는 곳은 **merge view 하나**(`outdoor_reference_rows`) — base + 살아 있는 보충 줄. 같은 subject 가 양쪽에 있으면 선다.
★사람 대기 없음. 이름·부분문자열·regex 결속 없음 — ID(loc_id ↔ final_id)로만 잇는다.
    )annotationsN)AnyDictListOptionalSequence reference_acquisition_supplementz1.202609030310c                      e Zd ZdZy)SupplementConflictue   base 와 보충이 같은 대상을 둘 다 든다 — 어느 쪽을 쓸지 모르는 채 안 쓴다.N)__name__
__module____qualname____doc__     g/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/grounding_outdoor_supplement.pyr   r      s    or   r   c                $    ddl m}m}  || |      S )Nr   )OBLIGATION_STRUCTURE_FORMobligation_subject_id)4app.modules.pipeline.grounding_reference_obligationsr   r   )locr   r   s      r   structure_form_subject_idr      s    : &?@@r   c                l    t        | xs i j                  d      xs i j                  d      xs g       S )Ndatarows)listget)cps    r   _rowsr       s/    28..(.B33F;BCCr   c                z    ddl m} | j                  d      xs i }t        |j                  d      xs d      |k(  S )Nr   r   
ledger_rowobligation_kind )r   r   r   str)rowr   leds      r   _is_structure_formr(   $   s8    ^
'',

%2Csww()/R04MMMr   c                b    t        |       D cg c]  }|j                  d      r| c}S c c}w )uG   보충 CP 의 **살아 있는** 줄 — `superseded_by` 가 없는 것.superseded_by)r   r   )supp_cprs     r   live_supplement_rowsr-   *   s)    W~D~!QUU?-CA~DDDs   ,,c           
     2   t        |       }t        |      }|D ch c]   }t        |j                  d      xs d      " }}t	        |D ch c]   }t        |j                  d      xs d      " c}|z        }|rt        d|       ||z   S c c}w c c}w )us   ★야외 전용 merge view — base(중앙) + 살아 있는 보충. 같은 subject 가 양쪽에 있으면 선다.research_subject_idr$   u@   중앙 CP 와 보충 CP 가 같은 대상을 둘 다 든다 — )r   r-   r%   r   sortedr   )front_cpr+   basesuppr,   base_idsdups          r   outdoor_reference_rowsr6   /   s     ?D(DAEFAAEE/06B7HF
4H4a#aee128b94H8S
TC
 #cdgch!ijj$;	 GHs   %B%Bc                ^   t        |       D ch c]g  }t        |j                  d      xs i j                  d      xs d      dk(  r1t        |j                  d      xs i j                  d      xs d      i }}g }|D ]  }t        |j                  d      xs d      }t        |j                  d      |j                  d      n$|j                  d      r|j                  d      gng       }|D cg c]  }|st        |       }}t	        |      d	k7  r|j                  | d
|xs d d       t        |j                  d      xs d      j                         s|j                  | d|d    d       |d   |vs|j                  | d|d    d       " |S c c}w c c}w )u   ★provider 앞에서 **한 번에** 본다 (Codex BLOCK 3 2026-09-03): 그룹의 실외 장소가 정확히 하나인가 ·
    structure_desc 가 있는가 · 그 장소 줄이 중앙 CP 에 있는가. 문제 목록을 돌려준다(비면 통과).r"   
owner_typer$   locationfinal_idgidloc_idsloc_id   u   : 실외 장소가 u   없음u"    — 정확히 하나여야 한다structure_descu   →r   u-   : place spec 의 structure_desc 가 비었다uM   : 중앙 CP 에 그 장소 줄이 없다 — 뼈대를 지어내지 않는다)r   r%   r   r   lenappendstrip)	r1   targetsr,   by_locproblemstr;   locsxs	            r   validate_targetsrI   ;   s    XY&!aeeL)/R44\BHbIZW 155&,"11*=CD&  Y H!%%,$"%i(8(DAEE)$`a`e`efn`oAEERZOK\uwy $*1A*t9>OOse#6t7Gx6HHjkl155)*0b1779OOse3tAwi/\]^7& OOse3tAwi/|}~  OY +s   A,F%6F*>F*c                   ddl m} ddlm} t	        | |      }|rt        ddj                  |      z         t        |       D ch c],  }t        |      st        |j                  d      xs d      . }}i }t        |       D ]i  }|j                  d      xs i }t        |j                  d	      xs d      d
k(  s:|j                  d      sL|j                  t        |d         |       k g }	t               }
|D ]4  }t        |j                  d      xs d      }t        |j                  d      xs d      j                         }t        |      }||v s||
v rb|
j                  |       t!        j"                  ||         }t%        |j                  |      xs i       }|j'                  |||d       |j'                  d|ddd|d|gdt)        t        |j                  d      xs d      hdhz
        dt        |j                  d      xs d      ||i       |	j+                  |       7 |	t,        dS c c}w )ut  `targets` = `[{"gid", "loc_id", "structure_desc"}, …]` → 보충 의무 장부 `{"rows": [...]}`.
    ★중앙(base) CP 에 `structure_form` 줄이 있는 장소만 뺀다. **기존 보충 CP 는 의무를 막지 않는다** —
    (Codex BLOCK 2 2026-09-03) 옛 줄이 retryable/unavailable 이거나 desc·좌표·팩이 바뀌어 새 신원이 필요해도
    `ca.run` 에 닿아야 한다. 되쓰기/재시도/신원 판단은 `ca.run` 과 장부가 한다. 같은 장소를 두 그룹이
    가리키면 하나. ★뼈대는 그 장소의 중앙 ledger_row 복사 — 없으면 선다(뜻을 지어내지 않는다).r   )PRODUCER_PAYLOADr!   u$   보충 의무를 세울 수 없다: z | r/   r$   r"   r8   r9   r:   r=   r?   )coarse_type_labelsurface_formvisual_briefpurposeNr#   coversorigin_subject_idssupplement_for_groupr;   )r   contract).app.modules.pipeline.grounding_entity_contractrK   r   r   rI   
ValueErrorjoinr   r(   r%   r   
setdefaultsetrB   r   addcopydeepcopydictupdater0   rA   SUPPLEMENT_CONTRACT_VERSION)r1   rC   rK   r   rE   r,   haverD   r'   r   seenrF   r   descsidpayloads                   r   structure_form_obligationsrd   P   s    P^'2H?%**XBVVWW=B8_f_PbcdPeC+,23_Df(*F8_eeL!'Rsww|$*+z9cggj>Qc#j/2C8  "$DD!%%/'R(155)*0b1779',$;#+mmF3K(sww/06B7T4Y]^_

!3	48(SE &#cgg6K.L.RPR*S)TXZW[)["\"Ce(:$;g
 	 	C# $ &ABB5 gs   I  I c           	        | j                  d      xs i }t        | j                  d      xs d      t        | j                  d      xs d      t        |j                  d      xs( |j                  d      xs i j                  d      xs d      t        |j                  d      xs d      t        | j                  d	      xs d      d
S )up   줄의 **유효 획득 결과** — 같은 신원이라도 이것이 달라지면 새 줄이다 (Codex BLOCK 2).acquisitionstatusr$   outcomechosen_pathchosenpathmatch_qualitywhy_unbought)rg   rh   ri   rl   rm   )r   r%   )r&   acqs     r   effective_resultro   y   s    
''-
 
&BC#''(+1r2s3779CUC[Y[?\sww}5f3778;L;RPR:W:WX^:_fdfg !9!?R@ 7 =2>@ @r   c          	        t        |       D cg c]  }t        |       }}|D ]/  }t        |j                  d      xs d      }t        |j                  d      xs d      }|D cg c]8  }t        |j                  d      xs d      |k(  s%|j                  d      r7|: }}t	        |      dkD  rt        d|dt	        |       d      |r?t        |d	   j                  d      xs d      |k(  rt        |d	         t        |      k(  r| d
t        |      d    }	|D ]  }|	|d<   	 |j                  t        j                  |             2 |D cg c]2  }|j                  d      rt        |j                  d      xs d      4 }
}t        |
D ch c]  }|
j                  |      dkD  s| c}      }|rt        d|       dd|t        |t	        |
      ddS c c}w c c}w c c}w c c}w )u  ★append-only: 있던 줄은 지우지 않는다. 같은 subject 에 대해 **신원과 유효 결과가 완전히 같을 때만**
    no-op. 신원이 다르거나(desc·좌표·팩 변경) 같은 신원의 유효 결과가 달라졌으면(예: incomplete → selected)
    옛 살아 있는 줄에 `superseded_by` 를 적고 새 줄을 덧붙인다. 살아 있는 줄이 subject 당 둘 이상이면 선다.r/   r$   identityr*   r>   u   보충 CP 에 u    의 살아 있는 줄이 u)   개다 — 어느 것을 쓸지 모른다r   :rg   u#   살아 있는 줄이 겹친다 — 	completed)rS   r   
live_count)rg   schema_versionconfig_hashr   )r   r\   r%   r   r@   r   ro   rA   rZ   r[   r0   countr^   )existingnew_rowsrv   r,   r   nrb   identlivestamplive_idsrH   r5   s                r   merge_supplementr      s   
 #8_-_DG_D-!%%-.4"5AEE*%+,q4a3quu-B'C'Ir#Jc#QZ[Z_Z_`oZp4qt9q=$~cW<VWZ[_W`Va  bK  &L  M  MCQJ/526%?DTUYZ[U\D]aqrsatDt'+A.x89:A!&Ao DMM!$%  BFdAQUUSbMcAEE/06B7Hd
X?X):Q)>!X?
@C
 #Fse!LMM!Q{!<dZ]^fZghj j# . r e?s/   G*(&G/G/!G/G42 G4G97G9)r   r%   returnr%   )r   Optional[Dict[str, Any]]r   List[Dict[str, Any]])r&   Dict[str, Any]r   bool)r+   r   r   r   )r1   r   r+   r   r   r   )r1   r   rC   Sequence[Dict[str, Any]]r   z	List[str])r1   r   rC   r   r   r   )r&   r   r   r   )rx   r   ry   r   rv   r%   r   r   )r   
__future__r   rZ   typingr   r   r   r   r   SUPPLEMENT_STEP_DIRr^   RuntimeErrorr   r   r   r(   r-   r6   rI   rd   ro   r   r   r   r   <module>r      s    #  6 68 . p pADNE
	$<	AU	*&C(@&CES&CR@j%(j-;jr   