
    nj!                    ,   U d Z ddlmZ ddlmZmZ dZded<   dZ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e	e
eeh      Zded<   dZded<   dZded<   dZded<    eh d      Zded<    G d de      Zd&dZd&d Zd&d!Zd&d"Zd'd#Zg d$Zy%)(u?  utils.ci_watch_handoff_schema — task-2642 CI_WATCH_HANDOFF JSON v1 schema.

회장 verbatim (2026-05-23 19:38 KST):
  ANU 는 CI/Gemini 를 직접 기다리지 않는다. PR open 이후 대기/감시/자동수렴은
  반드시 bot 또는 watcher task 에 위임한다.

본 helper 책임 (system_ci_watch_handoff_runner_spec_260523.md §2 + 정책 spec §4/§5):
  - 12 필수 필드 1:1 박제 (pr_number / head_sha / branch / expected_files /
    forbidden_paths / watcher_owner / max_watch_minutes / poll_interval_seconds /
    gemini_nudge_policy / auto_remediation_policy / callback_on_terminal_state /
    terminal_states)
  - 5 terminal_states enum (MERGE_READY / CHAIR_REQUIRED /
    GEMINI_EXTERNAL_TRIGGER_STALE / CI_FAILED_NON_REMEDIABLE / LOOP_BOUNDARY)
  - validate_handoff(): contract 위반 시 SchemaError raise

one-way isolation: utils/ 외부 import 0. live cokacdir / gh CLI 호출 0.

frozen anchor:
  ANCHOR-1: 12 필수 필드 1:1 박제 (회장 verbatim §4)
  ANCHOR-2: 5 terminal_states enum (회장 verbatim §5)
  ANCHOR-3: gemini_nudge_policy.max_nudges_per_pr_head <= 1 hard limit
            (회장 verbatim §9, PR #144 NUDGE_HARD_LIMIT_PER_PR_HEAD 정합)
  ANCHOR-4: auto_remediation_policy.allow_severities ⊆
            {medium, style, quality, non-critical-high} (회장 verbatim §8 watcher 책임)
    )annotations)AnyFinalz utils.ci_watch_handoff_schema.v1z
Final[str]SCHEMAMERGE_READYTERMINAL_MERGE_READYCHAIR_REQUIREDTERMINAL_CHAIR_REQUIREDGEMINI_EXTERNAL_TRIGGER_STALE&TERMINAL_GEMINI_EXTERNAL_TRIGGER_STALECI_FAILED_NON_REMEDIABLE!TERMINAL_CI_FAILED_NON_REMEDIABLELOOP_BOUNDARYTERMINAL_LOOP_BOUNDARYzFinal[frozenset[str]]ALL_TERMINAL_STATES)	pr_numberhead_shabranchexpected_filesforbidden_pathswatcher_ownermax_watch_minutespoll_interval_secondsgemini_nudge_policyauto_remediation_policycallback_on_terminal_stateterminal_stateszFinal[tuple[str, ...]]REQUIRED_FIELDSx   z
Final[int]DEFAULT_MAX_WATCH_MINUTES<   DEFAULT_POLL_INTERVAL_SECONDS>   non-critical-highstylemediumquality#ALLOWED_AUTO_REMEDIATION_SEVERITIESc                      e Zd ZdZy)SchemaErroru3   CI_WATCH_HANDOFF JSON 의 schema / contract 위반.N)__name__
__module____qualname____doc__     4/home/jay/workspace/utils/ci_watch_handoff_schema.pyr)   r)   M   s    =r/   r)   c                V    t        | t              xr t        | t               xr | dkD  S Nr   
isinstanceintboolvalues    r0   _is_positive_intr9   Q   s'    eS!O*UD*A&AOeaiOr/   c                V    t        | t              xr t        | t               xr | dk\  S r2   r3   r7   s    r0   _is_non_negative_intr;   U   s'    eS!P*UD*A&APeqjPr/   c                j    t        | t              xr" t        |       dkD  xr t        d | D              S )Nr   c              3  D   K   | ]  }t        |t              xr |  y wNr4   str.0xs     r0   	<genexpr>z,_is_non_empty_string_list.<locals>.<genexpr>]   s     8Q
1c"(q(8    )r4   listlenallr7   s    r0   _is_non_empty_string_listrI   Y   s4    5$ 	9JN	98%88r/   c                J    t        | t              xr t        d | D              S )Nc              3  D   K   | ]  }t        |t              xr |  y wr>   r?   rA   s     r0   rD   z._is_string_list_allow_empty.<locals>.<genexpr>b   s$      +%&
1c q +rE   )r4   rF   rH   r7   s    r0   _is_string_list_allow_emptyrL   a   s*    eT" s +*/+ ( r/   c           	        t        | t              s!t        dt        |       j                         t
        D cg c]	  }|| vs| }}|rt        d|       | d   }t        |      st        d      | d   }t        |t              rt        |      dk7  rt        d      |j                         }t        d |D              rt        d	      | d
   }t        |t              r|j                         st        d      | d   }t        |      st        d      | d   }t        |      st        d      | d   }	t        |	t              r|	j                         st        d      | d   }
t        |
      st        d      | d   }t        |      st        d      | d   }t        |t              st        d      d|vst        |d   t              st        d      |j                  dd      }t!        |      st        d      |dkD  rt        d      |j                  dd      }|dk7  rt        d       | d!   }t        |t              st        d"      d|vst        |d   t              st        d#      |j                  d$g       }t        |t"              st        d%      |D ]8  }t        |t              r	|t$        vst        d&|d't'        t$                      | d(   }t        |t              st        d)      | d*   }t        |t"              r|st        d+      |D ](  }|t(        vst        d,|d't'        t(                      t        |       }||d<   |S c c}w )-us  CI_WATCH_HANDOFF JSON 의 contract 검증 + 정규화.

    Args:
      handoff: 검증 대상 dict (12 필수 필드 포함).

    Returns:
      정규화된 handoff dict (head_sha lower).

    Raises:
      SchemaError: 12 필수 필드 누락 / 타입 위반 / 5 terminal_states enum 위반 /
        nudge hard limit 위반 / severity 화이트리스트 위반.
    zhandoff must be dict, got zmissing required fields: r   zpr_number must be positive intr   (   z#head_sha must be 40-char hex stringc              3  $   K   | ]  }|d v 
 yw)0123456789abcdefNr.   )rB   cs     r0   rD   z#validate_handoff.<locals>.<genexpr>   s     
:11&&
:s   z-head_sha must be 40-char hex (a-f / 0-9 only)r   zbranch must be non-empty stringr   z4expected_files must be non-empty list[non-empty str]r   z+forbidden_paths must be list[non-empty str]r   z&watcher_owner must be non-empty stringr   z&max_watch_minutes must be positive intr   z*poll_interval_seconds must be positive intr   z gemini_nudge_policy must be dictenabledz(gemini_nudge_policy.enabled must be boolmax_nudges_per_pr_head   zCgemini_nudge_policy.max_nudges_per_pr_head must be non-negative intuc   gemini_nudge_policy.max_nudges_per_pr_head must be <= 1 (회장 verbatim §9 nudge 1회 hard limit)on_403reportuP   gemini_nudge_policy.on_403 must be 'report' (회장 verbatim §8 NUDGE_403 path)r   z$auto_remediation_policy must be dictz,auto_remediation_policy.enabled must be boolallow_severitiesz5auto_remediation_policy.allow_severities must be listzCauto_remediation_policy.allow_severities contains invalid severity z
; allowed=r   z'callback_on_terminal_state must be boolr   z&terminal_states must be non-empty listz&terminal_states contains invalid enum )r4   dictr)   typer*   r   r9   r@   rG   loweranystriprI   rL   r6   getr;   rF   r'   sortedr   )handoffkmissingprhead	head_normr   r   r   r   mwmpisgnp
max_nudgesrU   arp
severitiessevcbtermsterm
normalizeds                         r0   validate_handoffrp   g   s    gt$(g)?)?(@A
 	
 *>QQg-=q>G>5gY?@@		BB:;;:DdC CIO?@@

I

:	
::IJJXFfc"&,,.;<<-.N$^4PQQ/0O&7GHHO,MmS)1D1D1FBCC
%
&CC BCC
)
*CC FGG
'
(Cc4 <==:c)nd#CDEE115J
+Q
 	
 A~:
 	
 WWXx(F^
 	
 +
,Cc4 @AA:c)nd#CHII+R0Jj$'QRR #s#s2U'U7*=>?A  
-	.Bb$CDD%&EeT"%BCC **8 A!"5679  gJ&Jz} ?s   	M%M%)r   r   r
   r   r   r   r   r   r    r"   r'   r)   rp   N)r8   r   returnr6   )r_   r   rq   rX   )r-   
__future__r   typingr   r   r   __annotations__r   r
   r   r   r   	frozensetr   r   r    r"   r'   
ValueErrorr)   r9   r;   rI   rL   rp   __all__r.   r/   r0   <module>rx      s   2 #  8
 7 $1 j 0&6  65T &
 T0J !: J%4 
 4-6.). * +'   ), : +,. z . >G7> #%: 
>* >PQpfr/   