
    ݦj{&                    R   d Z ddlmZ ddlmZmZ ddlmZmZm	Z	 dZ
h dZdZdZd	Zd
ZdZ G d de      Z G d de      Z G d de      Z G d de      Z ed       G d d             ZddZd dZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 d!dZdeedd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d"dZy)#u  PR Open Watcher Auto-Wrapper — task-2643 Track C (dry-run only · live 호출 0).

목적: PR open 직후 ANU 본체가 `gh pr view` 폴링 욕구를 갖지 않도록,
PR 생성 + watcher_contract_v1 자동 발급 + watcher dispatch 등록을 한 함수로 묶는다.

본 모듈은 **dry-run 만 수행**한다 (회장 verbatim):
- live `gh pr create` 호출 0
- live `cokacdir --cron` 호출 0
- 모든 외부 효과는 mock callable 로 주입받는다 (regression test 친화).

호출 사이트가 ANU 본체일 때:
- watcher_owner 가 ANU 본체 식별자면 즉시 fail (owner 위장 차단).
- collector_role 이 "ANU" 가 아니면 fail (collector_role 위장 차단).
- 위 검사 통과 시 contract 생성 + (mock) dispatch → schedule_id 반환.

bzaona6au 사건 (PR #145) 재현 fixture 를 fixture 폴더에서 동일 wrapper 로
재현하여 fail-closed 동작을 박제한다.
    )annotations)	dataclassfield)AnyCallableOptionalc119085addb0f8b7>   ANUanuANU_MAINanu_mainANU_SESSIONanu_sessionr
   )MERGE_READYCHAIR_REQUIREDGEMINI_EXTERNAL_TRIGGER_STALECI_FAILED_NON_REMEDIABLELOOP_BOUNDARY)delivery_outcome
miss_causeroot_cause_tagscanonical_rootregistration_statusi   zanu.watcher_contract.v1c                      e Zd ZdZy)PrOpenWatcherWrapperErroru   공통 베이스.N__name__
__module____qualname____doc__     N/home/jay/workspace/.worktrees/task-2643-dev6/utils/pr_open_watcher_wrapper.pyr   r   E   s    r"   r   c                      e Zd ZdZy)OwnerImpersonationErroru3   owner 가 ANU 본체 식별자로 위장한 경우.Nr   r!   r"   r#   r%   r%   I   s    =r"   r%   c                      e Zd ZdZy)CollectorRoleViolationErroru6   collector_role 위장 (anu_watcher / anu_polling 등).Nr   r!   r"   r#   r'   r'   M   s    @r"   r'   c                      e Zd ZdZy)LiveCallViolationErroruR   dry_run=True 인데 live mock 이 실제 호출을 시도한 경우 (mock 가드).Nr   r!   r"   r#   r)   r)   Q   s    \r"   r)   T)frozenc                      e Zd ZU ded<   ded<   ded<   ded<   ded<   d	Zd
ed<   dZded<   dZded<   dZded<    ee	      Z
ded<   y)WrapperResultint	pr_numberstrhead_shawatcher_schedule_idwatcher_ownerdict[str, Any]contractTbooldry_runr   live_calls_emittedNzOptional[str]rejection_reasonrejection_detail)default_factoryz	list[str]notes)r   r   r   __annotations__r6   r7   r8   r9   r   listr;   r!   r"   r#   r,   r,   Z   sX    NMGT&*m*&*m*T2E92r"   r,   c                    | r| j                         st        d      | j                         t        v rt        d|  d      y )Nzowner must be non-empty stringzowner='u?   ' is an ANU self-identifier — ANU 본체 위장 폴링 차단)stripr%   ANU_SELF_IDENTIFIERS)owners    r#   _validate_owner_not_anurB   m   sG    %&FGG{{},,%eW[\
 	
 -r"   c                @    | t         k7  rt        d|  dt               y )Nzcollector_role='u   ' 위장 폴링. ALLOWED=)ALLOWED_COLLECTOR_ROLEr'   )collector_roles    r#   _validate_collector_rolerF   v   s1    //)~..GH^Gab
 	
 0r"   c                t    t         | ||t        t              |dt        t        t              dd|t
        d|dS )NANU_NORMAL_CALLBACK)type	owner_keyenvelope_axesDEDUPE_ON_PR_HEAD_SHAT)schematask_idr.   r0   terminal_statesttl_secondscallback_targetduplicate_policyrA   rE   callback_only_reportingwatcher_owner_kind)CONTRACT_SCHEMA_IDr=   DEFAULT_TERMINAL_STATESANU_OWNER_KEYDEFAULT_ENVELOPE_AXESrD   rN   r.   r0   rA   rP   rT   s         r#   _build_contractrZ   }   sK     % 78")&!"78

 40#'0! r"   dev_bot)rT   rE   rP   r6   c                d   |
du rt        d      t        |       t        |        ||||d      }t        |t              s!t        dt        |      j                         t        |d         }t        |d         }t        |      dk7  rt        d	t        |             t        | |||||
      } |	|d      }t        |t              s!t        dt        |      j                         t        |d         }|st        d      dd|dd  d| d| ddg}t        |||||dddd|
      S )ua  PR open + watcher dispatch 를 묶어 ANU 가 직접 polling 할 욕구를 없앤다.

    Args:
        task_id: 발사 주체 task id (예: task-2643).
        branch: PR head branch name.
        title: PR title.
        body: PR body.
        owner: watcher 발사 주체 (dev bot name 또는 cron-watcher-*).
        watcher_owner_kind: "dev_bot" | "cron_watcher".
        collector_role: callback collector role. "ANU" 만 허용.
        ttl_seconds: watcher TTL (sec).
        gh_pr_create: gh pr create 호출자 (regression 에선 mock).
            반환: {"pr_number": int, "head_sha": str (40hex)}
        watcher_dispatch: watcher dispatch 호출자 (regression 에선 mock).
            반환: {"schedule_id": str}
        dry_run: True 면 mock 만 호출. False (live) 는 본 task 범위에서 호출 0.

    Returns:
        WrapperResult.
    FuL   dry_run=False is forbidden in task-2643 scope (live `gh pr create` 호출 0)T)branchtitlebodyr6   z#gh_pr_create must return dict, got r.   r0   (   z'head_sha must be 40 hex chars, got len=rY   )r4   r6   z'watcher_dispatch must return dict, got schedule_idz+watcher_dispatch returned empty schedule_idz?PR open + watcher dispatch completed in dry-run (live calls=0).zwatcher contract head_sha=N   z pr=#z ttl=zs.uG   ANU 본체는 본 PR 의 CI/Gemini 를 직접 polling 하지 않는다.r   )
r.   r0   r1   r2   r4   r6   r7   r8   r9   r;   )r)   rB   rF   
isinstancedictr   rI   r   r-   r/   lenrZ   r,   )rN   r]   r^   r_   rA   rT   rE   rP   gh_pr_createwatcher_dispatchr6   	pr_resultr.   r0   r4   dispatch_resultra   r;   s                     r#   open_pr_and_dispatch_watcherrj      s   D %$Z
 	

 E"^, F%dDQIi&'1$y/2J2J1KL
 	
 Ik*+I9Z()H
8}'5c(m_E
 	

 -H '$GOot,'5d?6K6T6T5UV
 	
 om45K'(UVV J
$Xbq\N%	{%}TVW
QE ' r"   N)rA   r/   returnNone)rE   r/   rk   rl   )rN   r/   r.   r-   r0   r/   rA   r/   rP   r-   rT   r/   rk   r3   )rN   r/   r]   r/   r^   r/   r_   r/   rA   r/   rT   r/   rE   r/   rP   r-   rf   Callable[..., dict[str, Any]]rg   rm   r6   r5   rk   r,   )r    
__future__r   dataclassesr   r   typingr   r   r   rW   r@   rD   rV   rX   DEFAULT_TTL_SECONDSrU   	Exceptionr   r%   r'   r)   r,   rB   rF   rZ   rj   r!   r"   r#   <module>rs      s  & # ( * * #       . 	 >7 >A"; A]6 ] $
3 
3 
3$

  	
   P (0* ^^ ^ 	^
 ^ ^ ^ ^ ^ 0^ 4^ ^ ^r"   