
    j#                    |   U d Z ddlmZ ddlmZ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<   d
Zde
d<   dZde
d<   dZde
d<    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+d"Zd,d#Z ed       G d$ d%             Z	 	 	 	 	 	 d-d&Zg d'Zy().u  anu_v2.polling_policy — long polling 금지 코드 게이트 (task-2556 §9 / §12).

회장 §명시 2026-05-12 KST (task-2556 12 필수 §9):
  "long polling 금지 — 5~15min normal wait / 30min first timeout / 1회 recheck / 봇 종료"

본 모듈은 ``ExecutorScheduler`` / ``IdlePRDiagnoser`` 가 의도치 않게 long polling 루프로
변질되는 것을 **코드 레벨에서 정적/동적 게이트** 로 차단한다.

설계 원칙:
  - ``MAX_SINGLE_SLEEP_SECONDS = 900`` (15 분) — 단일 호출의 sleep 상한.
  - ``FIRST_TIMEOUT_SECONDS = 1800`` (30 분) — 첫 trigger 후 fresh 미도착 대기 상한.
  - ``MAX_RECHECKS = 1`` — fresh 도착 여부 재확인 1 회만.
  - ``BotSessionExitRequired`` sentinel — recheck 1 회 후 봇은 즉시 종료해야 함을 알린다.

부수효과 0: 본 모듈은 sleep 자체를 수행하지 않고, **정책 위반 시 예외만 raise** 한다.
호출자는 본 모듈의 ``assert_sleep_allowed`` / ``advance_recheck`` 만 사용하면 long polling
패턴 (while True + 짧은 sleep 반복) 을 코드 레벨에서 표현 불가.

one-way isolation: anu_v2/ 외부 import 금지. stdlib + ``dataclasses`` 만 사용.
    )annotations)	dataclassreplace)AnyFinali  z
Final[int]MAX_SINGLE_SLEEP_SECONDSi  FIRST_TIMEOUT_SECONDS   MAX_RECHECKSi,  NORMAL_WAIT_MIN_SECONDSNORMAL_WAIT_MAX_SECONDS   SECOND_REVIEW_GRACE_SECONDSMAX_SECOND_REVIEW_RECHECKSTzFinal[bool]LONG_POLLING_FORBIDDENzpr_number+head_shaz
Final[str]&SECOND_REVIEW_OWNER_TRIGGER_DEDUPE_KEYc                      e Zd ZdZy)LongPollingViolationu;   long polling pattern detected — task-2556 §9 hard-block.N__name__
__module____qualname____doc__     7/home/jay/workspace/scripts/../anu_v2/polling_policy.pyr   r   )   s    Er   r   c                      e Zd ZdZy)BotSessionExitRequiredud   recheck 1 회 후 봇은 즉시 종료해야 함. 호출자는 본 예외를 catch 후 process exit.Nr   r   r   r   r   r   -   s    nr   r   )frozenc                  <    e Zd ZU dZdZded<   dZded<   dZded<   y	)
PollingStateu7  recheck 상태 머신 (immutable, replace 로 다음 state 생성).

    Attributes:
      rechecks_done: 지금까지 수행된 recheck 횟수.
      elapsed_seconds: trigger 후 누적 경과 시간 (외부에서 측정 후 주입).
      posted_marker_present: owner-trigger.posted marker 존재 여부.
    r   intrechecks_doneelapsed_secondsFboolposted_marker_presentN)r   r   r   r   r#   __annotations__r$   r&   r   r   r   r!   r!   1   s(     M3OS"'4'r   r!   c                    t        | t              rt        | t              r!t        dt	        |       j
                         | dk  rt        d|        | t        kD  rt        d|  dt         d      y)u  단일 sleep 호출 상한 검증. ``seconds > 900`` 시 ``LongPollingViolation``.

    Args:
      seconds: 호출자가 의도한 sleep 시간 (초).

    Raises:
      LongPollingViolation: 음수, 비-int, 또는 ``MAX_SINGLE_SLEEP_SECONDS`` 초과.
    z%sleep seconds must be plain int, got r   z"negative sleep seconds forbidden: zsingle sleep z#s exceeds MAX_SINGLE_SLEEP_SECONDS=u,   s — long polling forbidden (task-2556 §9)N)
isinstancer"   r%   r   typer   r   secondss    r   assert_sleep_allowedr-   @   s     gs#z'4'@"3DM4J4J3KL
 	
 {"%Gy#QRR))"G9$GH`Ga b9 :
 	
 *r   c                    t        |        | t        k  rt        d|  dt         d      | t        kD  rt        d|  dt         d      y)u   normal wait 구간 (5~15분) 검증. 회장 § 5~15min normal wait 명시 1:1.

    NORMAL_WAIT_MIN_SECONDS <= seconds <= NORMAL_WAIT_MAX_SECONDS 만 허용.
    범위 외 시 ``LongPollingViolation``.
    znormal wait z s below NORMAL_WAIT_MIN_SECONDS=u'   s — 1초 tight loop polling forbiddenz s above NORMAL_WAIT_MAX_SECONDS=sN)r-   r   r   r   r+   s    r   assert_normal_waitr0   V   sp     !(("7)#CD[C\ ]4 5
 	
 (("7)#CD[C\\]^
 	
 )r   c                    t        | t              rt        | t              r!t        dt	        |       j
                         | dk  rt        d|        | t        kD  rt        d|  dt         d      y)u   trigger 후 ``elapsed_seconds`` 가 ``FIRST_TIMEOUT_SECONDS`` 를 초과하면 violation.

    호출자는 본 예외를 catch 후 'POSTED_BUT_NO_FRESH_EVIDENCE' 경로로 escalate 해야 한다.
    z'elapsed_seconds must be plain int, got r   z*elapsed_seconds must be non-negative, got zfirst-timeout z s exceeds FIRST_TIMEOUT_SECONDS=uS   s — scheduler must exit bot session and let scheduled/event-driven recheck resumeN)r)   r"   r%   r   r*   r   r	   )r$   s    r   !assert_first_timeout_not_exceededr2   h   s    
 os+z/4/P"5d?6K6T6T5UV
 	
 "88IJ
 	
 .."_--MNcMd e` a
 	
 /r   c                    t        | t              s!t        dt        |       j                         | j
                  t        k\  r t        d| j
                   dt         d      t        | | j
                  dz         S )ur  recheck 카운터 +1. ``MAX_RECHECKS`` 초과 시 ``BotSessionExitRequired``.

    호출자는 매 recheck 직후 본 함수를 호출. 본 함수는 다음 state 를 반환한다.
    1 회 recheck 가 끝나면 다음 호출에서 ``BotSessionExitRequired`` 가 raise 되어
    봇은 즉시 종료해야 한다. 추가 recheck 는 scheduled (cron) 또는 event-driven
    (webhook) 경로로만 가능.

    Args:
      state: 현재 polling state.

    Returns:
      다음 polling state (``rechecks_done += 1``).

    Raises:
      BotSessionExitRequired: ``rechecks_done`` 이 이미 ``MAX_RECHECKS`` 에 도달.
    z*advance_recheck expects PollingState, got zrechecks_done=z reached MAX_RECHECKS=u6    — bot session must exit, next recheck via schedulerr
   )r#   )	r)   r!   r   r*   r   r#   r   r   r   states    r   advance_recheckr6   |   s    " e\*"8e9M9M8NO
 	
 l*$U0011G~ VD E
 	
 5(;(;a(?@@r   c                v    t        | t              sy| j                  t        k\  ry| j                  t
        kD  ryy)uZ  봇이 즉시 exit 해야 하는 상태인지 판단.

    조건 (둘 중 하나):
      1. ``rechecks_done >= MAX_RECHECKS`` — 추가 recheck 권한 없음.
      2. ``elapsed_seconds > FIRST_TIMEOUT_SECONDS`` — first timeout 초과.

    Returns:
      True 면 봇은 즉시 process exit. False 면 scheduled wait + 1 recheck 가능.
    TF)r)   r!   r#   r   r$   r	   r4   s    r   must_exit_nowr8      s7     e\*l*44r   c                       e Zd ZU dZdZded<   y)SchedulerCycleBudgetu  단일 scheduler cycle 의 누적 sleep 예산.

    같은 process 안에서 여러 PR 를 scan 하면서 각각 normal_wait 를 호출하면 누적 sleep
    이 long polling 으로 변질될 수 있다. 본 dataclass 는 single cycle 안에서 누적 sleep
    상한을 ``MAX_SINGLE_SLEEP_SECONDS`` 로 강제한다.

    Attributes:
      cumulative_sleep_seconds: 본 cycle 누적 sleep.
    r   r"   cumulative_sleep_secondsN)r   r   r   r   r;   r'   r   r   r   r:   r:      s     %&c%r   r:   c                    t        |       | j                  |z   }|t        kD  rt        d| dt         d      t	        | |      S )uD  sleep 예산 소비. 누적 합이 ``MAX_SINGLE_SLEEP_SECONDS`` 를 넘으면 violation.

    Args:
      budget: 현재 cycle 예산.
      requested_seconds: 요청 sleep 시간.

    Returns:
      다음 budget (누적 합 += requested_seconds).

    Raises:
      LongPollingViolation: 누적 합이 상한 초과.
    zcycle cumulative sleep z(s would exceed MAX_SINGLE_SLEEP_SECONDS=u*   s — split into multiple scheduled cycles)r;   )r-   r;   r   r   r   )budgetrequested_seconds
next_totals      r   consume_sleep_budgetr@      s`     *+003DDJ,,"%j\ 2((@'A B34
 	

 6J??r   )r   r	   r   r   r   r   r   r   r   r   r   r!   r:   r-   r0   r2   r6   r8   r@   N)r,   r"   returnNone)r$   r"   rA   rB   )r5   r   rA   r!   )r5   r   rA   r%   )r=   r:   r>   r"   rA   r:   )r   
__future__r   dataclassesr   r   typingr   r   r   r'   r	   r   r   r   r   r   r   r   RuntimeErrorr   r   r!   r-   r0   r2   r6   r8   r:   r@   __all__r   r   r   <module>rH      s  * # *  (+ * *$( z (j &)  )&)  ) +. Z -)* J *&*  *5I &
 IF< Fo\ o $( ( (
,
$
(A:& $& & &@ @58@@4r   