
     j t                       d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	 ddl
Z
 ee      j                         j                  j                  j                  Z ee      ej                   v r!ej                   j#                   ee             ej                   j%                  d ee             ddlmZmZmZmZmZmZmZ ddZ G d d	      Z G d
 d      Z G d d      Z G d d      Z G d d      Z  G d d      Z! G d d      Z"y)u   tests/regression/test_lifecycle_reconciliation_manager_2518.py — 14 회귀 + 5 replay fixture.

회장 명시 P0: lifecycle state machine + bot session decoupling + source-of-truth + idempotent reconcile.
    )annotationsN)Path)Any)LifecycleStateStuckReasonLifecycleEvidencedetermine_statedetect_stuck_cases	reconcileassert_no_manual_done_forgeryc                     t        di ddddddddddd	dd
ddddddddddddddddddddddddddd}|j                  |        t        di |S )zIDefault 'clean baseline' evidence; tests override fields they care about.task_id	task-9999	pr_numberNpr_statemerge_commitmerged_into_mainF	ci_statussmoke_statustimer_statustimer_end_timehas_donehas_done_ackedhas_merge_donehas_qc_resulthas_followuphas_escalate_markerescalate_marker_age_minutestelegram_reply_truncatedbot_session_statusworktree_existsbranch_pushed_to_remote )dictupdater   )	overridesbases     R/home/jay/workspace/tests/regression/test_lifecycle_reconciliation_manager_2518.pymake_evidencer)   $   s       	
           "  %)!" "'#$  %& '( !&)D, 	KK	$t$$    c                      e Zd ZdZd Zy)TestLifecycleStateEnumu,   A-1. LifecycleState enum 7종 멤버 검증.c                    h d}t         D ch c]  }|j                   }}||k(  sJ d| d|        t        t        t                     dk(  sJ yc c}w )u7   회장 명시: LifecycleState가 정확히 7종 멤버.>   PR_OPENRUNNING	ESCALATED	FINALIZEDRECONCILINGSTUCK_NEEDS_RECONCILEMERGED_PENDING_RECONCILEu/   LifecycleState enum 멤버 불일치: expected=	, actual=   N)r   valuelenlist)selfexpectedmemberactuals       r(   'test_lifecycle_state_enum_seven_membersz>TestLifecycleStateEnum.test_lifecycle_state_enum_seven_membersG   sh    
 .<<6&,,<<! 	
=hZyQWPXY	
! 4'(A---	 =s   AN)__name__
__module____qualname____doc__r>   r#   r*   r(   r,   r,   D   s
    6.r*   r,   c                  "    e Zd ZdZd Zd Zd Zy)TestSourceOfTruthu1   A-2~4. source-of-truth evidence → state 결정.c                    t        ddddddddd	      }t        |      \  }}|t        j                  k(  s
J d|        |g k(  s
J d	|        y
)uh   A-2: PR merged + ci PASS + smoke PASS + has_done_acked + has_merge_done + timer completed → FINALIZED.MERGED4d45947eaabbccddTSUCCESSPASS	completed)	r   r   r   r   r   r   r   r   r   u   FINALIZED 기대, 실제: u+   FINALIZED에는 stuck_cases 없어야 함: N)r)   r	   r   r1   )r:   evidencestatestuck_casess       r(   /test_source_of_truth_merged_smoke_pass_finalizezATestSourceOfTruth.test_source_of_truth_merged_smoke_pass_finalize\   s{     +!$

 -X6{000 	
(0	
0 b ]$OP[}"]] r*   c                v    t        ddd      }t        |      \  }}|t        j                  k(  s
J d|        y)u9   A-3: timer running + PR OPEN → RUNNING 유지 (정상).OPEN*   running)r   r   r   u2   RUNNING 기대 (PR OPEN + timer running), 실제: N)r)   r	   r   r/   )r:   rK   rL   _stucks       r(   2test_source_of_truth_timer_running_pr_open_runningzDTestSourceOfTruth.test_source_of_truth_timer_running_pr_open_runningo   sJ     "

 (1v... 	
@H	
.r*   c                
   t        dddd      }t        |      \  }}|t        j                  t        j                  hv s
J d|        |D ch c]  }|j
                   }}t        j                  |v s
J d|        yc c}w )	u   A-4: timer running인데 PR MERGED → GitHub 우선, state ∈ {STUCK_NEEDS_RECONCILE, MERGED_PENDING_RECONCILE}.

        timer는 stuck signal 또는 backfill 대상.
        rF   11223344aabbccddTrR   )r   r   r   r   uK   GitHub PR MERGED > timer running 우선순위 반영 필요, 실제 state: u?   TIMER_RUNNING_BUT_PR_MERGED stuck case 기대, actual reasons: N)r)   r	   r   r3   r4   reasonr   TIMER_RUNNING_BUT_PR_MERGED)r:   rK   rL   rM   scstuck_reasonss         r(   (test_evidence_conflict_github_over_timerz:TestSourceOfTruth.test_evidence_conflict_github_over_timer{   s    
 !+!"	
 -X6{0033
 
 	
 ZZ_Y`a		
 
 .99r9966-G 	
Mm_]	
G :s   B N)r?   r@   rA   rB   rN   rT   r[   r#   r*   r(   rD   rD   Y   s    ;^&


r*   rD   c                  @    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
d	 Zy
)TestStuckDetectionuC   B. stuck detection 8 케이스 — 회장 §1~7 + Telegram cut-off.c                    t        ddddd      }t        |      }|D ch c]  }|j                   }}t        j                  |v s
J d|        yc c}w )	uj   B-5: timer running + PR merged → TIMER_RUNNING_BUT_PR_MERGED 감지 (회장 §2, task-2517 dev2 사례).rF   d   abcdef99FrR   r   r   r   r   r   u,   TIMER_RUNNING_BUT_PR_MERGED 기대, actual: N)r)   r
   rW   r   rX   r:   rK   casesrY   reasonss        r(   &test_stuck_timer_running_but_pr_mergedz9TestStuckDetection.test_stuck_timer_running_but_pr_merged   sh     #""
 #8,',-299--66'A 	
:7)D	
A .   Ac           	         t        ddddddd      }t        |      }|D ch c]  }|j                   }}t        j                  |v s
J d|        yc c}w )	uQ   B-6: PR merged + .done 없음 → PR_MERGED_BUT_DONE_MISSING 감지 (회장 §1).rF      abcdef12TFr   r   r   r   r   r   r   u+   PR_MERGED_BUT_DONE_MISSING 기대, actual: N)r)   r
   rW   r   PR_MERGED_BUT_DONE_MISSINGrb   s        r(   %test_stuck_pr_merged_but_done_missingz8TestStuckDetection.test_stuck_pr_merged_but_done_missing   sn     #!  
 #8,',-299--55@ 	
9'C	
@ .   Ac                    t        dddd      }t        |      }|D ch c]  }|j                   }}t        j                  |v s
J d|        yc c}w )ui   B-7: mergeCommit 존재 + .merge-done 없음 → MERGE_COMMIT_BUT_MERGE_DONE_MISSING 감지 (회장 §1).rF   deadbeef12345678TF)r   r   r   r   u4   MERGE_COMMIT_BUT_MERGE_DONE_MISSING 기대, actual: N)r)   r
   rW   r   #MERGE_COMMIT_BUT_MERGE_DONE_MISSINGrb   s        r(   .test_stuck_merge_commit_but_merge_done_missingzATestStuckDetection.test_stuck_merge_commit_but_merge_done_missing   se     +! 	
 #8,',-299-->>'I 	
B7)L	
I .s   Ac           	         t        ddddddd      }t        |      }|D ch c]  }|j                   }}t        j                  |v s
J d|        y	c c}w )
ue   B-8: PR merged + CI PASS + smoke PASS + .done.acked/.merge-done 누락 → CI_PASS_BUT_NOT_FINALIZED.rF   cafebabe12341234TrH   rI   F)r   r   r   r   r   r   r   u*   CI_PASS_BUT_NOT_FINALIZED 기대, actual: N)r)   r
   rW   r   CI_PASS_BUT_NOT_FINALIZEDrb   s        r(   $test_stuck_ci_pass_but_not_finalizedz7TestStuckDetection.test_stuck_ci_pass_but_not_finalized   sn     +!  
 #8,',-299--44? 	
8	B	
? .rm   c                    t        dddddddddd
      }t        |      }|D ch c]  }|j                   }}t        j                  |v s
J d	|        y
c c}w )uu   B-9: cron history 마지막 라인 truncated → TELEGRAM_REPLY_CUT_OFF 감지 (task-2517 dev2 cron 2BAB8982 패턴).	task-2517rF   4d45947eTrH   rI   rJ   )
r   r   r   r   r   r   r   r   r   r   u'   TELEGRAM_REPLY_CUT_OFF 기대, actual: Nr)   r
   rW   r   TELEGRAM_REPLY_CUT_OFFrb   s        r(   !test_stuck_telegram_reply_cut_offz4TestStuckDetection.test_stuck_telegram_reply_cut_off   sw     #!$%)
 #8,',-299--11W< 	
5gY?	
< .s   Ac                    dD ]T  }t        ddd|      }t        |      }|D ch c]  }|j                   }}t        j                  |v rIJ d| d|         yc c}w )	ur   B-10: cron status=cancelled/error + task evidence 정상(merged_into_main=True) → BOT_SESSION_ENDED_BUT_TASK_OK.)	cancellederrorrF   aabbccdd11223344T)r   r   r   r    u9   BOT_SESSION_ENDED_BUT_TASK_OK 기대 (bot_session_status=z), actual: N)r)   r
   rW   r   BOT_SESSION_ENDED_BUT_TASK_OK)r:   statusrK   rc   rY   rd   s         r(   (test_stuck_bot_session_ended_but_task_okz;TestStuckDetection.test_stuck_bot_session_ended_but_task_ok   s}    , 	F$!/!%#)	H 'x0E+01Rryy1G1<<G KF8S^_f^ghG	 2s   Ac                    t        ddd      }t        |      }|D ch c]  }|j                   }}t        j                  |v s
J d|        yc c}w )u[   B-11: worktree 존재 + branch_pushed_to_remote + PR 미생성 → FINISH_TASK_INTERRUPTED.TNr!   r"   r   u(   FINISH_TASK_INTERRUPTED 기대, actual: r)   r
   rW   r   FINISH_TASK_INTERRUPTEDrb   s        r(   "test_stuck_finish_task_interruptedz5TestStuckDetection.test_stuck_finish_task_interrupted   sb      $(

 #8,',-299--22g= 	
6wi@	
= .   Ac                    t        ddddd      }t        |      }|D ch c]  }|j                   }}t        j                  |v s
J d|        yc c}w )ui   B-12: has_escalate_marker + age > 30분 + Critical 7종 매칭 evidence 없음 → STALE_ESCALATE_MARKER.Tg     F@rH   rF   )r   r   r   r   r   uF   STALE_ESCALATE_MARKER 기대 (age=45min, no active critical), actual: N)r)   r
   rW   r   STALE_ESCALATE_MARKERrb   s        r(    test_stuck_stale_escalate_markerz3TestStuckDetection.test_stuck_stale_escalate_marker  sj      $(,!
 #8,',-299--00G; 	
TU\T]^	
; .rf   N)r?   r@   rA   rB   re   rl   rq   ru   r{   r   r   r   r#   r*   r(   r]   r]      s-    M

"

"
(

r*   r]   c                  "    e Zd ZdZd Zd Zd Zy)TestIdempotentAndForgeryBlocku5   C. idempotent reconcile + manual .done 위장 차단.c                   dd}dd}g }t        d      D ]#  }t        dd|||      }|j                  |       % |D ch c]  }|j                   }}t	        |      dk(  s"J d|D cg c]  }|j                   c}        t        |      D ].  \  }	}|j                  g k(  rJ d	|	dz    d
|j                           |d   j                  du sJ yc c}w c c}w )uf   C-13: 동일 reconcile 3회 호출 (apply=False) → state 동일, actions_taken 누적 없음, no-op.c                    ddddidS )Ni  rF   oiddeadbeef00000001numberrL   mergeCommitr#   _task_ids    r(   fake_pr_lookupzXTestIdempotentAndForgeryBlock.test_repeated_reconcile_idempotent.<locals>.fake_pr_lookup&      ! %'9: r*   c                    dddS )NrJ   z2026-05-09T00:00:00Zr   end_timer#   r   s    r(   fake_timer_loaderz[TestIdempotentAndForgeryBlock.test_repeated_reconcile_idempotent.<locals>.fake_timer_loader-      )7MNNr*      r   F)applyworkspace_root	pr_lookuptimer_loader   u   3회 호출 state 불일치: zcall u<   : apply=False인데 actions_taken 비어있어야 함, got: r   TNr   strreturnr$   )ranger   appendrL   r8   	enumerateactions_takendry_run)
r:   tmp_pathr   r   results_reportrstatesis
             r(   "test_repeated_reconcile_idempotentz@TestIdempotentAndForgeryBlock.test_repeated_reconcile_idempotent#  s	   		O q 	#A'(.F NN6"	# $++a!''++6{a]#@SZA[a!''A[@\!]] g& 	DAq??b( !uXYZYhYhXij(	 qz!!T))) ,A[s    C)Cc                    t               }t        j                  t        d      5  t	        d||       ddd       y# 1 sw Y   yxY w)uv   C-14: evidence 부족 상태에서 assert_no_manual_done_forgery 호출 시 RuntimeError(MANUAL_DONE_FORGERY_BLOCKED).MANUAL_DONE_FORGERY_BLOCKEDmatchr   r   Nr)   pytestraisesRuntimeErrorr   r:   r   rK   s      r(    test_manual_done_forgery_blockedz>TestIdempotentAndForgeryBlock.test_manual_done_forgery_blockedF  sC      
 ]]</LM 	)'	 	 	s	   >Ac                    t        ddd      }t        j                  t        d      5  t	        d||       d	d	d	       y	# 1 sw Y   y	xY w)
uF   C-14 변형: merge_commit 있지만 merged_into_main=False → 차단.cafecafe12345678FrH   )r   r   r   r   r   r   r   Nr   r   s      r(   1test_manual_done_forgery_blocked_partial_evidencezOTestIdempotentAndForgeryBlock.test_manual_done_forgery_blocked_partial_evidenceR  sL     +"

 ]]</LM 	)'	 	 	s   AAN)r?   r@   rA   rB   r   r   r   r#   r*   r(   r   r      s    ?!*F
r*   r   c                  .    e Zd ZdZd Zd Zd Zd Zd Zy)TestReplayFixturesu   5 실제 사례 replay fixture.c                    t        ddddddddddd      }t        |      }|D ch c]  }|j                   }}t        j                  |v s
J d	|        y
c c}w )u  Fixture 1: task-2517 dev2 Telegram cut-off (cron 2BAB8982 마지막 'Pr'로 잘림).

        evidence: pr_state=MERGED, merge_commit=4d45947e..., merged_into_main=True,
                  ci_status=SUCCESS, smoke_status=PASS, timer_status=completed,
                  has_done=True, has_merge_done=True, telegram_reply_truncated=True
        기대: TELEGRAM_REPLY_CUT_OFF stuck case 감지
        rw   rF   4d45947eaabb1234TrH   rI   rJ   )r   r   r   r   r   r   r   r   r   r   r   u1   Fixture1: TELEGRAM_REPLY_CUT_OFF 기대, actual: Nry   rb   s        r(   -test_replay_fixture_task2517_telegram_cut_offz@TestReplayFixtures.test_replay_fixture_task2517_telegram_cut_offh  s|     !+!$%)
 #8,',-299--11W< 	
?yI	
< .s   Ac           	         t        ddddddd      }t        |      }|D ch c]  }|j                   }}t        j                  |v s
J d|        t        j
                  |v s
J d|        y	c c}w )
u-  Fixture 2: PR merged but .done missing (회장 §1).

        evidence: pr_state=MERGED, merge_commit=abcdef12, merged_into_main=True,
                  has_done=False, has_done_acked=False, has_merge_done=False
        기대: PR_MERGED_BUT_DONE_MISSING + MERGE_COMMIT_BUT_MERGE_DONE_MISSING
        rF   i-  ri   TFrj   u5   Fixture2: PR_MERGED_BUT_DONE_MISSING 기대, actual: u>   Fixture2: MERGE_COMMIT_BUT_MERGE_DONE_MISSING 기대, actual: N)r)   r
   rW   r   rk   rp   rb   s        r(   *test_replay_fixture_pr_merged_done_missingz=TestReplayFixtures.test_replay_fixture_pr_merged_done_missing  s     !#!  
 #8,',-299--55@ 	
CG9M	
@ >>'I 	
LWIV	
I	 .s   A0c                "   t        ddddd      }t        |      }|D ch c]  }|j                   }}t        j                  |v s
J d|        t        |      \  }}|t        j                  t        j                  hv s
J d|        y	c c}w )
u  Fixture 3: mergeCommit 존재 + timer running.

        evidence: pr_state=MERGED, merge_commit=11223344, merged_into_main=True, timer_status=running
        기대: TIMER_RUNNING_BUT_PR_MERGED + state ∈ {STUCK_NEEDS_RECONCILE, MERGED_PENDING_RECONCILE}
        rF   i  rV   TrR   ra   u6   Fixture3: TIMER_RUNNING_BUT_PR_MERGED 기대, actual: uV   Fixture3: state ∈ {STUCK_NEEDS_RECONCILE, MERGED_PENDING_RECONCILE} 기대, actual: N)	r)   r
   rW   r   rX   r	   r   r3   r4   )r:   rK   rc   rY   rd   rL   r   s          r(   .test_replay_fixture_merge_commit_timer_runningzATestReplayFixtures.test_replay_fixture_merge_commit_timer_running  s     !+!"
 #8,',-299--66'A 	
DWIN	
A #8,q0033
 
 	
 gglfmn		
 
 .s   Bc                    t        ddd      }t        |      }|D ch c]  }|j                   }}t        j                  |v s
J d|        yc c}w )u   Fixture 4: finish-task interrupted.

        evidence: worktree_exists=True, branch_pushed_to_remote=True, pr_number=None
        기대: FINISH_TASK_INTERRUPTED
        TNr   u2   Fixture4: FINISH_TASK_INTERRUPTED 기대, actual: r   rb   s        r(   +test_replay_fixture_finish_task_interruptedz>TestReplayFixtures.test_replay_fixture_finish_task_interrupted  sd     ! $(

 #8,',-299--22g= 	
@	J	
= .r   c           
        dd}dd}g dfd}dd}g }t        d      D ]R  }t        dd|||||      }|j                  |j                         |j                  g k(  r@J d	|j                           t        t        |            d
k(  s
J d|        dd}	t        dd||||||	      }
dd}dd}|dz  dz  }|j                  dd       |dz  j                  d       |dz  j                  d       |dz  j                  d       g dfd}t        dd||||||	      }|j                  t        j                  k(  s1|j                  g k(  s!J d|j                   d|j                          yy) u)  Fixture 5: repeated reconcile idempotency.

        - 동일 evidence로 reconcile 3회 호출 (apply=False) → state 동일
        - apply=True 첫 호출 → 일부 backfill 액션 (planned/taken)
        - evidence(backfill 후) + 두번째 apply=True → 추가 액션 0건 (멱등)
        c                    ddddidS Ni  rF   r   f1x7ure5abcdef12r   r#   r   s    r(   fake_pr_lookup_pendingzeTestReplayFixtures.test_replay_fixture_repeated_reconcile_idempotency.<locals>.fake_pr_lookup_pending  r   r*   c                    dddS NrJ   z2026-05-09T01:00:00Zr   r#   r   s    r(   fake_timer_loader_completedzjTestReplayFixtures.test_replay_fixture_repeated_reconcile_idempotency.<locals>.fake_timer_loader_completed  r   r*   c                ,    j                  | |f       y Nr   )pathcontentcaptured_writess     r(   fake_file_writerz_TestReplayFixtures.test_replay_fixture_repeated_reconcile_idempotency.<locals>.fake_file_writer  s    ""D'?3r*   c                     y r   r#   )r   _timer_datas     r(   fake_timer_writerz`TestReplayFixtures.test_replay_fixture_repeated_reconcile_idempotency.<locals>.fake_timer_writer  s    r*   r   ztask-fixture5F)r   r   r   r   file_writertimer_writer5   apply=False인데 actions_taken 비어있어야 함: r   u   3회 dry-run state 불일치: c                4    t        j                  | ddd      S )Nr   zf1x7ure5abcdef12
 stdoutstderr
subprocessCompletedProcessargs_kwargss     r(   fake_runner_foundz`TestReplayFixtures.test_replay_fixture_repeated_reconcile_idempotency.<locals>.fake_runner_found  s    ..tQ?S\^__r*   T)r   r   r   r   r   r   runnerc                    ddddidS r   r#   r   s    r(   fake_pr_lookup_finalizedzgTestReplayFixtures.test_replay_fixture_repeated_reconcile_idempotency.<locals>.fake_pr_lookup_finalized
  r   r*   c                    dddS r   r#   r   s    r(   fake_timer_loader_finalizedzjTestReplayFixtures.test_replay_fixture_repeated_reconcile_idempotency.<locals>.fake_timer_loader_finalized  r   r*   memoryeventsparentsexist_okztask-fixture5.donez{}ztask-fixture5.done.ackedztask-fixture5.merge-donec                ,    j                  | |f       y r   r   )r   r   captured_writes_seconds     r(   fake_file_writer2z`TestReplayFixtures.test_replay_fixture_repeated_reconcile_idempotency.<locals>.fake_file_writer2  s    "))4/:r*   uF   두번째 apply=True: 추가 액션 없거나 FINALIZED 기대, state=z, actions_taken=Nr   )r   r   r   r   r   None)r   r   r   r$   r   r   r   r9   r   zsubprocess.CompletedProcess)r   r   r   rL   r   r8   setmkdir
write_textr   r1   )r:   r   r   r   r   r   
dry_statesr   r   r   report_apply1r   r   
events_dirr   report_apply2r   r   s                   @@r(   2test_replay_fixture_repeated_reconcile_idempotencyzETestReplayFixtures.test_replay_fixture_repeated_reconcile_idempotency  s   		O (*	4	 
q 	A'08,.F fll+''2- GH\H\G]^-	 3z?#q(W,J:,*WW(	` "#,4(*$	
		O (83
5	*	*66t<	0	0<<TB	0	0<<TB.0	; "#.4)*$	
 ""n&>&>>-B]B]acBc 	
"(())9-:U:U9VX	
cBc>r*   N)	r?   r@   rA   rB   r   r   r   r   r  r#   r*   r(   r   r   e  s    )
6
2
6
"h
r*   r   c                      e Zd ZdZd Zy)TestLivePilotReplayu  task-2520 live pilot self-host 중 발견된 한계점 박제.

    다그다 hang 사례 (bot_session_status=cancelled + timer running + worktree/branch alive +
    PR 미생성)는 현 본체에서 FINISH_TASK_INTERRUPTED로만 매칭되며,
    bot_session_status=cancelled 단독 case는 어떤 stuck reason으로도 격상되지 않는다.
    operational hardening은 task-2521 영역.
    c                   t        ddddddddddddd      }t        |      \  }}|D ch c]  }|j                   }}|t        j                  k(  s
J d	|        t
        j                  |v s
J d
|        t
        j                  |vsJ d       d!d}d!d}d"d}	|dz  dz  }
|
j                  dd       |dz  }|j                  dd       |dz  }|j                  t        j                  dddd      dz   d       t        dd||	|||      }|j                  du sJ |j                  t        j                  k(  sJ d|j                          t        d |j                   D              }|sJ d|j                           t        d |j                   D              rJ d|j                           |j"                  g k(  sJ d |j"                          yc c}w )#u  task-2520: bot 종료 + timer 미정지 + worktree/branch 살아있음 + PR 미생성.

        현 본체 동작 정확 박제 (본체 변경 시 회귀 깨짐):
        1. state == STUCK_NEEDS_RECONCILE
           (PR 없음, escalate 없음, stuck_cases 존재 → fallback)
        2. stuck_cases ⊃ {FINISH_TASK_INTERRUPTED}
           (worktree_exists + branch_pushed_to_remote + pr_number None 매칭)
        3. BOT_SESSION_ENDED_BUT_TASK_OK NOT in stuck reasons
           (merged_into_main=True 조건 매칭 실패 — 본체 한계점, task-2521 영역)
        4. reconcile(apply=False) → actions_planned에 'BLOCKED (insufficient evidence)' 포함
           (assert_no_manual_done_forgery 차단)
        z	task-2520NFrR   r}   TrI   )r   r   r   r   r   r   r   r   r   r    r!   r"   r   uG   STUCK_NEEDS_RECONCILE 기대 (PR 없음 + stuck_cases 존재), 실제: uG   FINISH_TASK_INTERRUPTED 기대 (worktree+branch+no_pr 매칭), actual: u   현 본체 한계점 회귀 박제: bot_session_status=cancelled 단독은 격상 안됨. 격상 시 task-2521에서 신규 stuck reason 추가.c                    i S r   r#   r   s    r(   r   zbTestLivePilotReplay.test_bot_session_cancelled_with_evidence_intact_replay.<locals>.fake_pr_lookupp  s    Ir*   c                    dd dS )NrR   r   r#   r   s    r(   r   zeTestLivePilotReplay.test_bot_session_cancelled_with_evidence_intact_replay.<locals>.fake_timer_loaders  s    'T::r*   c                n    d| v rt        j                  | ddd      S t        j                  | ddd      S )Nz	ls-remoter   z0deadbeefcafe1234	refs/heads/task/task-2520-dev4
r   r   r   r   r   s     r(   fake_runnerz_TestLivePilotReplay.test_bot_session_cancelled_with_evidence_intact_replay.<locals>.fake_runnerv  sB    d"!22O	  ..tQr"MMr*   z
.worktreesztask-2520-dev4r   cron_historyztask-2520.logz[task-2520] live pilotok)promptr   response
zutf-8)encoding)r   r   r   cron_history_dirr   r   u6   reconcile state STUCK_NEEDS_RECONCILE 기대, 실제: c              3  <   K   | ]  }d |v xr d|v xs d|v   yw)BLOCKEDzinsufficient evidencer   Nr#   .0actions     r(   	<genexpr>z]TestLivePilotReplay.test_bot_session_cancelled_with_evidence_intact_replay.<locals>.<genexpr>  s<      
  & F%<%F 7,67
s   uj   actions_planned에 'BLOCKED (insufficient evidence)' 또는 'MANUAL_DONE_FORGERY_BLOCKED' 기대, actual: c              3  r   K   | ]/  }|j                  d       xs |j                  d      xs |dk(   1 yw)created_wrote_ended_timerN)
startswithr  s     r(   r  z]TestLivePilotReplay.test_bot_session_cancelled_with_evidence_intact_replay.<locals>.<genexpr>  sJ      
  j) '  *'&'
s   57uA   forgery 차단 후 다른 backfill 액션 기록 금지, actual: r   r   r   )r)   r	   rW   r   r3   r   r   r   r   r   jsondumpsr   r   rL   anyactions_plannedr   )r:   r   rK   rL   rM   rY   rd   r   r   r  worktree_dircron_dircron_logr   forgery_blockeds                  r(   6test_bot_session_cancelled_with_evidence_intact_replayzJTestLivePilotReplay.test_bot_session_cancelled_with_evidence_intact_replay@  s    !""  * $(
  -X6{'2329933<<< 	
UV[U\]	
< 22g= 	
UV]U^_	
= 88G 	
E	
G		;		N  ,.1AA4$7n,td3o-JJ6) $   	 
	
 #%$*
 ~~%%%||~CCC 	
DV\\NS	
C  
 !00
 

  	
==C=S=S<TV	

  
 !00	
 
 	
 PPVPfPfOgh	
 
 ##r) 	
CFDXDXCYZ	
)m 4s   GN)r?   r@   rA   rB   r&  r#   r*   r(   r  r  7  s    v
r*   r  c                      e Zd ZdZd Zy)TestStuckReasonEnumu   StuckReason enum이 회장 명시 8종(task-2518) 포함 확인.
    task-2521 §3에서 BOT_CANCELLED_* 4종이 추가되어 총 12종이 되었으므로
    superset 검증으로 전환 (task-2518 contract = 8종 모두 present).
    c                x    h d}t         D ch c]  }|j                   }}||z
  }|rJ d| d|        y c c}w )N>   r   rz   r   rt   rk   rX   r   rp   u&   task-2518 StuckReason 누락: missing=r5   )r   r7   )r:   expected_2518r<   r=   missings        r(   $test_stuck_reason_enum_eight_membersz8TestStuckReasonEnum.test_stuck_reason_enum_eight_members  sR    	
 .996&,,99&( 	
4WIYvhO	
{7 :s   7N)r?   r@   rA   rB   r,  r#   r*   r(   r(  r(    s    

r*   r(  )r   r   )#rB   
__future__r   r  r   syspathlibr   typingr   r   __file__resolveparent_WORKTREE_ROOTr   r   removeinsert&utils.lifecycle_reconciliation_managerr   r   r   r	   r
   r   r   r)   r,   rD   r]   r   r   r  r(  r#   r*   r(   <module>r8     s    #   
    h'')0077>>~#(("HHOOC'( 3~& '  %@. .*9
 9
@@
 @
N> >JK
 K
d
 
L
 
r*   