
    ¿iR                    &   d Z ddlmZ ddlZddlmZmZ ddlmZ  G d dej                        Z
e G d d	             Ze G 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eeeeeeeegZddZddZedk(  r e e             y)u  tools/poc/termination_classifier.py

Phase B termination classifier (DRY-RUN ONLY, 격리 POC).

⚠️ Production 미반영. task-timer.py / dispatch.py / finish-task.sh 변경 없음.
⚠️ classify()는 dataclass를 반환만 한다. 마커 파일 생성 / dispatch 호출 금지.

Usage (dry-run):
    from tools.poc.termination_classifier import classify, TaskEvidence
    evidence = TaskEvidence(task_id="task-2466", pr_state="MERGED", ...)
    result = classify(evidence)
    print(result.classification, result.confidence, result.followup_conditions)

근거 문서:
- memory/orchestration/phase_b_integration_items_260507.md  (섹션 2.1 — 8종 + 9단계 분류 룰)
- memory/feedback/feedback_merge_pending_dependency_classification_260507.md  (6항목 자동 감지)
- memory/feedback/feedback_merged_close_blocked_external_classification_260507.md  (5항목 자동 감지)
- .claude/projects/.../memory/feedback_dogfooding_pending_classification_260507.md  (4항목 자동 감지)
    )annotationsN)	dataclassfield)Optionalc                  4    e Zd ZdZdZdZdZdZdZdZ	dZ
d	Zd
Zy)TerminationClassificationug   8종 종료 분류 + UNCLASSIFIED. 우선순위 순 정렬이 아니라 도메인 의미 단위 정렬.DONE	ESCALATEDDOGFOODING_PENDINGMERGE_PENDING_DEPENDENCYMERGED_CLOSE_BLOCKED_EXTERNALBLOCKED_BY_EXTERNAL_DEPENDENCYFAILED_PREEXISTINGWAITING_FOR_CHAIR_DECISIONUNCLASSIFIEDN)__name__
__module____qualname____doc__r	   r
   r   r   r   r   r   r   r        7/home/jay/workspace/tools/poc/termination_classifier.pyr   r      s8    qDI-9$C!%E"-!=!Lr   r   c                  P   e Zd ZU 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<   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<   dZded<   dZded<   dZded<    ee      Zd ed!<   y)"TaskEvidenceu  Classifier 입력. 모든 필드 optional — 기본값이 안전하여 UNCLASSIFIED로 흐른다.

    필드 설명:
      pr_state              : OPEN | MERGED | CLOSED | None (PR 없음)
      pr_merged_at          : ISO8601 문자열 또는 None
      ci_rollup             : PASS | FAIL | UNKNOWN
      qc_result             : PASS | WARN | FAIL
      close_lifecycle_state : CLEAN | FAIL | NOT_RUN
      close_blocker_owner   : internal | external | None
      essence_verdict       : PASS | FAIL | UNKNOWN
      retry_count           : 현재 재시도 횟수
      retry_max             : 재시도 임계값 (초과 시 ESCALATED 후보)
      dependency_task_id    : 의존/후속 task 식별자
      dogfooding_external_dependency : BOT 토큰 / ruleset / approval 등 외부 의존으로
                                       dogfooding layer 미완 여부
      blocked_external_system        : 외부 API/infra로 본질 진행 자체가 차단
      failed_preexisting             : 기존 결함이 본 task 진행을 차단 (본 task 책임 아님)
      waiting_chair_decision         : 방향성/정책 결정을 회장이 대기
      forbidden_violations           : 금지 규칙 위반 건수 (0이어야 MERGED_CLOSE_BLOCKED 적용)
      admin_override_used            : admin override 사용 여부 (false이어야 함)
      branch_protection_bypass       : branch protection bypass 여부 (false이어야 함)
      ci_fail_owner         : INTERNAL | EXTERNAL_DEPENDENCY | PREEXISTING | None
                              CI 실패 귀책 주체. None은 fail 없음 또는 미판별.
      done_marker_exists    : .done 마커 파일 존재 여부 (default False)
      workspace_dirty_owner : internal | external | None
                              본 task workspace 자체 dirty 여부 귀책 주체.
                              close_blocker_owner와 의미 중복 아님; 본 필드는
                              본 task workspace 자체 dirty 여부.
      extra                          : 확장용 자유 dict
    strtask_idNOptional[str]pr_statepr_merged_at	ci_rollup	qc_resultclose_lifecycle_stateclose_blocker_owneressence_verdictr   intretry_count   	retry_maxdependency_task_idFbooldogfooding_external_dependencyblocked_external_systemfailed_preexistingwaiting_chair_decisionforbidden_violationsadmin_override_usedbranch_protection_bypassci_fail_ownerdone_marker_existsworkspace_dirty_ownerdefault_factorydictextra)r   r   r   r   __annotations__r   r   r    r!   r"   r#   r$   r&   r(   r)   r+   r,   r-   r.   r/   r0   r1   r2   r3   r4   r   r7   r8   r   r   r   r   r   0   s    < L"Hm""&L-&#I}##I}#+/=/)--%)O])KIs(,,+0"D0$)T)$$#(D( !#! %%%*d*#'M='$$+/=/-E4-r   r   c                      e Zd ZU dZded<   ded<   ded<    ee      Zd	ed
<    ee      Zd	ed<   dZ	ded<    ee      Z
d	ed<   y)ClassificationResultuV  classify() 반환값. 이 오브젝트만 반환; 마커 파일 생성·dispatch 호출 없음.

    marker_file     : (dry-run 참고용) 실제 운영에서 발행될 마커 파일명. 본 POC에서는 생성 안 함.
    preserve_markers: 동시 보존해야 할 기존 마커 (예: MERGED_CLOSE_BLOCKED_EXTERNAL → .done, .escalate)
    r   classificationfloat
confidencer   matched_ruler5   z	list[str]followup_conditionsnotesNr   marker_filepreserve_markers)r   r   r   r   r9   r   listr@   rA   rB   rC   r   r   r   r;   r;   l   sT    
 .-%*4%@@T2E92!%K%"'"=i=r   r;   c                    | j                   dk(  rV| j                  J| j                  dk(  r;| j                  dk(  r,| j                  dk(  rt        t        j                  ddd	      S y)
u  R1: 본질 PASS + merge + lifecycle close 모두 완료 → DONE (성공 종료).

    근거: phase_b_integration_items 섹션 2.1 규칙 1.
    조건: PR MERGED + pr_merged_at not null + close_lifecycle_state CLEAN
          + essence_verdict PASS + forbidden_violations == 0.
    MERGEDNCLEANPASSr         ?R1_DONE.done)r<   r>   r?   rB   )r   r   r"   r$   r/   r;   r   r	   evs    r   
_rule_donerN      sh     	xOO'$$/&(##q(#499"	
 	
 r   c           
         | j                   dk(  rk| j                  dk(  r\| j                  dk(  rM| j                  dk(  r>| j                  s2| j
                  s&t        t        j                  ddddd	gg d
ddg      S y)u  R2: 머지 완료 + post-merge close lifecycle 외부 차단 → MERGED_CLOSE_BLOCKED_EXTERNAL.

    근거: feedback_merged_close_blocked_external 섹션 4 (5항목 자동 감지).
    조건:
      - PR MERGED (mergedAt not null)
      - admin override 0, branch protection bypass 0, forbidden 0
      - close lifecycle FAIL
      - 차단 사유 외부 workspace dirty (close_blocker_owner == "external")
    핵심: 이미 머지됐으므로 FAILED 계열로 분류하면 부정확.
    rF   FAILexternalr   rI    R2_MERGED_CLOSE_BLOCKED_EXTERNALz.close-blocked-externalrK   	.escalate)uh   외부 workspace dirty 정리 task 발행 (타 task 영역 / 운영 복사본 / 시스템 활동 파일)u*   정리 완료 후 finish-task.sh 재실행uU   성공 시 .close-blocked-external.resolved 추가 + final_state_resolved=true 박제uW   lifecycle 마커 3개 동시 보존 필요: .done / .escalate / .close-blocked-externaluC   taskctl 최종 상태 = MERGED_CLOSE_BLOCKED_EXTERNAL (DONE 아님))r<   r>   r?   rB   rC   r@   rA   N)	r   r"   r#   r/   r0   r1   r;   r   r   rL   s    r   #_rule_merged_close_blocked_externalrT      s     	x$$.""j0##q(&&++#4RR;1%{3! jU
 	
  r   c           	         | j                   dk(  r[| j                  dk(  rL| j                  dk(  r=| j                  dv r/| j                  r#t        t        j                  dddg dg d	      S y
)uD  R3: 본질 PASS + CI PASS + dogfooding layer 외부 의존 미완 → DOGFOODING_PENDING.

    근거: feedback_dogfooding_pending 섹션 '발동 조건' (4항목).
    조건:
      - PR MERGED
      - essence_verdict PASS
      - ci_rollup PASS
      - qc_result PASS 또는 WARN (FAIL 아님)
      - dogfooding_external_dependency True (BOT 토큰 / ruleset / approval)
    주의: ESCALATED는 본질 실패 의미; 본질 PASS인데 외부 의존으로 dogfooding 못 끝낸 경우
          ESCALATED로 박제하면 retry 룰이 본질 재작업 시도 → 무의미.
    rF   rH   )rH   WARNffffff?R3_DOGFOODING_PENDINGz.dogfooding-pending)uf   외부 의존 항목 복구 (BOT_GITHUB_TOKEN 갱신 / ruleset 임시 해제 / approval 정책 조정)u9   bot-authored PR 재발행 또는 handoff 경로 재시도u   no-admin enqueue-merge 성공u7   layer 5 dogfooding evidence 확보 (audit jsonl 박제))uA   수동 사람 approve 우회 금지 (self-approval 정책 위반)u   admin override 사용 금지u9   4가지 후속 조건 모두 충족 전까지 DONE 금지r<   r>   r?   rB   r@   rA   N)r   r$   r    r!   r+   r;   r   r   rL   s    r   _rule_dogfooding_pendingrZ      sn     	x&(LLF"LL,,--#4GG0-!
 	
" r   c                *   | j                   dk(  r| j                  x| j                  dk(  ri| j                  ]| j                  sQ| j
                  dv rCt        t        j                  dddd| j                   d	d| j                   d
ddddgg d      S y)u^  R4: 본질 PASS + PR OPEN + 후속 task 의존으로 merge 자체가 차단 → MERGE_PENDING_DEPENDENCY.

    근거: feedback_merge_pending_dependency 섹션 5 (6항목 자동 감지).
    조건:
      - PR OPEN (mergedAt null)
      - essence_verdict PASS (본질 결함 0)
      - dependency_task_id 명시 (후속/의존 task 명확히 추적 가능)
      - done_marker_exists False (.done 미발행 상태)
      - ci_fail_owner None 또는 EXTERNAL_DEPENDENCY (내부 결함 아님)
    주의: 실패가 아니라 조건부 대기 상태; retry 룰 적용 시 본질 재작업 → 무의미.
    OPENNrH   )NEXTERNAL_DEPENDENCYrW   R4_MERGE_PENDING_DEPENDENCYz.merge-pendingu   의존 task u    DONE 대기u    DONE 후 본 PR CI rerunu   guard PASS 확인z	PR MERGEDu   lifecycle close 진입u   .merge-pending → .done 변환)u#   admin override / force merge 금지u0   본질 코드 추가 수정 금지 (이미 PASS)u9   후속 6조건 미충족 상태에서 .done 발행 금지rY   )	r   r   r$   r)   r3   r2   r;   r   r   rL   s    r   _rule_merge_pending_dependencyr_      s     	vOO#&(!!-%%!>>#4MM6(r445\Br4455NO#(1!
 	
& r   c                    | j                   r<| j                  dv r.| j                  dv r t        t        j
                  dddg d      S y)	uy  R5: 외부 시스템(API/infra/ruleset)이 본질 진행 자체를 차단 → BLOCKED_BY_EXTERNAL_DEPENDENCY.

    근거: phase_b_integration_items 섹션 2.1 규칙 5.
    조건:
      - blocked_external_system True
      - essence_verdict None 또는 UNKNOWN (본질 진행 시작조차 못한 상태)
      - pr_state None 또는 OPEN (이미 MERGED면 해당 없음)
    )NUNKNOWNNr\   g333333?!R5_BLOCKED_BY_EXTERNAL_DEPENDENCYz.blocked-external)uR   외부 시스템 복구 확인 (API 다운 / infra 장애 / ruleset 차단 해소)u   재시도 가능 여부 검증u   lifecycle 진입r<   r>   r?   rB   r@   N)r,   r$   r   r;   r   r   rL   s    r   $_rule_blocked_by_external_dependencyre     sQ     	"""33KK>)#4SS<+!

 
	
 r   c                    | j                   r=| j                  dv r/| j                  dk(  r t        t        j
                  dddg d      S y)	u  R6: 기존 결함이 본 task 진행을 차단 — 본 task 책임 아님 → FAILED_PREEXISTING.

    근거: phase_b_integration_items 섹션 2.1 규칙 6.
    조건:
      - failed_preexisting True (예: main lint/test 기존 실패가 본 task CI를 차단)
      - pr_state None 또는 OPEN (이미 MERGED면 해당 없음)
      - ci_fail_owner PREEXISTING (CI 실패 귀책이 기존 결함임이 명시)
    rb   PREEXISTING?R6_FAILED_PREEXISTINGz.failed-preexisting)uD   기존 결함 별도 task 발행 (본 task 범위 외 수정 금지)u9   기존 결함 수정 task DONE 후 본 task 영향 확인u*   본 task 재시도 가능 여부 재평가rd   N)r-   r   r2   r;   r   r   rL   s    r   _rule_failed_preexistingrj   +  sP     	KK>)-#4GG0-!

 
	
 r   c                Z    | j                   rt        t        j                  ddddg      S y)u   R7: 방향성/우선순위/예외 승인을 회장이 결정 대기 → WAITING_FOR_CHAIR_DECISION.

    근거: phase_b_integration_items 섹션 2.1 규칙 7.
    조건: waiting_chair_decision True (정책 충돌, 사용자 영향 큰 변경 등).
    rI   R7_WAITING_FOR_CHAIR_DECISIONz.waiting-chairu)   회장 결정 명시 후 분류 재산정rd   N)r.   r;   r   r   rL   s    r   _rule_waiting_chairrm   G  s7     
  #4OO8(!L M
 	
 r   c                    | j                   | j                  k\  r/| j                  dk(  r t        t        j
                  dddddg      S y)	u  R8: retry 초과 + 본질 결함 + 본질 재작업 필요 → ESCALATED (실패 종료).

    근거: phase_b_integration_items 섹션 2.1 규칙 8.
    조건:
      - retry_count >= retry_max
      - essence_verdict FAIL
    주의: retry 초과라도 차단 사유가 외부이면 R2~R7에서 먼저 매칭됨.
          이 룰은 실제로 본질 결함인 경우만 도달한다.
    rP   rh   R8_ESCALATEDrS   u=   본질 재작업 필요 (essence 결함 근본 원인 분석)u   회장/아누 판단 대기rd   N)r&   r(   r$   r;   r   r
   rL   s    r   _rule_escalatedrp   X  sP     
~~%"*<*<*F#4>>'#O-!	
 		
 r   c                r    t         D ]  } ||       }||c S  t        t        j                  ddddg      S )u  입력 evidence를 8종 룰 트리에 순서대로 매칭하여 최초 매칭 결과 반환.

    마커 파일 생성 / dispatch 호출 / 상태 변경 없음 — dry-run only.
    어떤 룰도 매칭되지 않으면 UNCLASSIFIED 반환 (회장 알림 + 수동 분류 필요).

    Args:
        evidence: TaskEvidence 인스턴스 (필드 기본값 = 안전)

    Returns:
        ClassificationResult (dataclass, 읽기 전용 활용 권장)
    Ng        R9_UNCLASSIFIEDu4   룰 미해당. 회장 알림 + 수동 분류 필요.)r<   r>   r?   rB   rA   )_RULESr;   r   r   )evidenceruleresults      r   classifyrw     sN      hM  0==&EF r   c            
     ,   ddl } ddl}ddl}| j                  dd      }|j	                  dd       |j                         }	 t        |j                  d	
      5 }|j                  |      }ddd       t        t        j                  j!                               }j#                         D 	
ci c]  \  }	}
|	|v s|	|
 }}	}
t        |j!                               |z
  }|r#t        dt%        |       |j                         t        di |}t'        |      }|j(                  |j*                  j,                  |j.                  |j0                  |j2                  |j4                  |j6                  |j8                  dd	}t        |j;                  |dd             y# 1 sw Y   4xY w# t        $ r' t        d|j                   |j                         Y y|j                  $ r$}t        d| |j                         Y d}~yd}~ww xY wc c}
}	w )u   fixture JSON → TaskEvidence → classify() → JSON 출력 (dry-run CLI).

    실행 예시:
        python3 tools/poc/termination_classifier.py fixture.json
    r   Ntermination_classifieru]   Phase B termination classifier (DRY-RUN only). 마커 파일 생성 / dispatch 호출 없음.)progdescriptionfixtureuA   fixture JSON 파일 경로 (TaskEvidence 필드 key-value 매핑))helpzutf-8)encodingu-   [ERROR] fixture 파일을 찾을 수 없음: )file   u   [ERROR] JSON 파싱 실패: u%   [WARN] 알 수 없는 필드 무시: T)	r   r<   r>   r?   rB   rC   r@   rA   dry_runF   )ensure_asciiindentr   )argparsejsonsysArgumentParseradd_argument
parse_argsopenr|   loadFileNotFoundErrorprintstderrJSONDecodeErrorsetr   __dataclass_fields__keysitemssortedrw   r   r<   valuer>   r?   rB   rC   r@   rA   dumps)r   r   r   parserargsfdataexcknown_fieldskvfilteredunknownrt   rv   outputs                   r   _mainr     s    $$%= % F P   D$,,1 	 Q99Q<D	  |88==?@L!%CAl1B1CHC$))+-G5fWo5FGcjjY'h'HhF ## //55''++))"33%99
F 
$**V%*
:;=	  	  =dll^LSVS]S]^ ,SE2D
 DsB   F, F-F, /H<HF)$F, ,-HH)HH__main__)rM   r   returnzOptional[ClassificationResult])rt   r   r   r;   )r   r%   )r   
__future__r   enumdataclassesr   r   typingr   Enumr   r   r;   rN   rT   rZ   r_   re   rj   rm   rp   rs   rw   r   r   
SystemExitr   r   r   <module>r      s   & #  ( 
"		 
"" 4. 4. 4.v > > >&.#L%P'T88": '"(	
 :6r z
UW
 r   