
     j[                       d Z ddlmZ ddlZddlmc m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 dZdZ d%d	Z!d
dd&dZ"dddd	 	 	 	 	 d'dZ#d
d
dd
d	 	 	 	 	 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+ G d# d$      Z,y))u  tests/regression/test_worktree_timer_reconcile_2528.py — 회귀 7건.

회장 명시 task-2528 P0:
  worktree에서 완료된 task가 task-timers.json에 reconcile 안 되는 lifecycle bug fix.
  본 회귀는 task-2527 audit (2026-05-10)에서 식별된 root cause를 박제한다.

회귀 7건:
  1. happy path: worktree 완료 → timer entry 추가 PASS
  2. task-timers.json 부재 fixture (5/9 14:32 8 task 사고): 8개 task 부재 → reconcile 후 8개 모두 entry 존재
  3. idempotent: reconcile 2회 실행 → entry 1개만 존재 (중복 X)
  4. archived 충돌: archived에 이미 있는 task → active에 중복 추가 X
  5. mtime fallback 회귀: reconcile 후 helpers.py:450-454 mtime fallback 미발동 검증
  6. chat=6937032012 격리: 다른 chat record fixture → reconcile 결과에 포함 X
  7. token raw 0: reconcile 결과 JSON에 ghs_/ghp_/github_pat_ prefix 부재
    )annotationsN)Path)Any)LifecycleEvidence_build_reconciled_timer_entry_read_timers_file(_reconcile_worktree_completion_to_timers_timer_entry_present	reconcile	task-2514	task-2515z	task-2516ztask-2516+1	task-2517ztask-2518-self-host	task-2519ztask-2519-self)ghs_ghp_github_pat_c                     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 )Ntask_id	task-9999	pr_number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     J/home/jay/workspace/tests/regression/test_worktree_timer_reconcile_2528.py_make_evidencer0   :   s       	
           "  %)!" "'#$  %& '( !&)D, 	KK	$t$$    T)with_activec                   | dz  dz  j                  dd       |r0| dz  dz  j                  t        j                  di id      d	
       | S )z;Create memory/ tree under tmp_path. Returns workspace root.memoryeventsTparentsexist_oktask-timers.jsontasksFensure_asciiutf-8encoding)mkdir
write_textjsondumps)tmp_pathr2   s     r/   _setup_workspacerE   U   s\    8#**4$*G	H	1	1==JJ}59 	> 	
 Or1   	dev3-teamg     J@PASS)team_idduration_seconds	qc_resultc                   | dz  dz  | dz  }|j                  t        j                  |||||d|dd      d	       y )
Nr4   r5   z.donedone)r   rH   end_timerI   rJ   statuscompleted_atFr;   r=   r>   )rA   rB   rC   )	workspacer   rM   rH   rI   rJ   ps          r/   _write_done_jsonrR   `   s^    Hx'WIU*;;ALL

""$$4&  ( 	
   r1   MERGED)r   r!   r   r   c               .    t        | d|d|dd|d|d      S )NF   (aaaa1111bbbb2222cccc3333dddd4444eeee5555SUCCESSrG   T)r   r   r   r   r   r   r   r   r    r!   r"   )r0   )r   r   r!   r   r   s        r/   _evidence_completedrX   t   s0    ?)% r1   c                       e Zd ZdZddZddZy)TestHappyPathu7   회귀 #1: worktree 완료 → timer entry 추가 PASS.c           	        t        |      }d}d}t        |||dd       t        |      }g }g }t        |d||d||      }|d	   }	d}
|	|
u }|st	        j
                  d
|fd|	|
f      t	        j                  |	      t	        j                  |
      dz  }t	        j                  d|       dz   d|iz  }t        t	        j                  |            d x}	x}}
d}	|	|v }|st	        j
                  d|fd|	|f      t	        j                  |	      dt        j                         v st	        j                  |      rt	        j                  |      nddz  }dd|iz  }t        t	        j                  |            d x}	}g }||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd|iz  }t        t	        j                  |            d x}}t        |dz  dz        }|d   }||v }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }t	        j                  d |       d!z   d|iz  }t        t	        j                  |            d x}}|d   |   }|d"   }	|	|k(  }|st	        j
                  d|fd#|	|f      t	        j                  |	      dt        j                         v st	        j                  |      rt	        j                  |      nddz  }dd|iz  }t        t	        j                  |            d x}	}|d$   }	|	|k(  }|st	        j
                  d|fd#|	|f      t	        j                  |	      d$t        j                         v st	        j                  |      rt	        j                  |      nd$dz  }dd|iz  }t        t	        j                  |            d x}	}|d%   }	d&}
|	|
k(  }|slt	        j
                  d|fd'|	|
f      t	        j                  |	      t	        j                  |
      dz  }d(d|iz  }t        t	        j                  |            d x}	x}}
|d)   }	d}
|	|
k(  }|slt	        j
                  d|fd'|	|
f      t	        j                  |	      t	        j                  |
      dz  }d(d|iz  }t        t	        j                  |            d x}	x}}
|d*   }	t        j                  }d} ||      }|	|k(  }|st	        j
                  d|fd+|	|f      t	        j                  |	      d,t        j                         v st	        j                  t              rt	        j                  t              nd,t	        j                  |      t	        j                  |      t	        j                  |      d-z  }d.d/|iz  }t        t	        j                  |            d x}	x}x}x}}|d0   }	d1}
|	|
k(  }|slt	        j
                  d|fd'|	|
f      t	        j                  |	      t	        j                  |
      dz  }d(d|iz  }t        t	        j                  |            d x}	x}}
|d2   }	d3}
|	|
k(  }|slt	        j
                  d|fd'|	|
f      t	        j                  |	      t	        j                  |
      dz  }d(d|iz  }t        t	        j                  |            d x}	x}}
|d4   }	d}
|	|
k(  }|slt	        j
                  d|fd'|	|
f      t	        j                  |	      t	        j                  |
      dz  }d(d|iz  }t        t	        j                  |            d x}	x}}
y )5N	task-29992026-05-09T03:02:43+00:00rF   gffffft@)rM   rH   rI   run-1Tworkspace_rootapplyactions_takenactions_plannedentry_insertedisz%(py1)s is %(py4)spy1py4zmeta=
>assert %(py6)spy6reconciled_worktree_timerinz%(py1)s in %(py3)srb   ri   py3assert %(py5)spy5==z%(py0)s == %(py3)src   py0rr   r4   r9   r:   z%(py0)s in %(py3)stidztimers=
>assert %(py5)sr   )z%(py1)s == %(py3)srM   rN   	completedz%(py1)s == %(py4)sassert %(py6)srH   rI   )zL%(py1)s == %(py9)s
{%(py9)s = %(py5)s
{%(py5)s = %(py3)s.approx
}(%(py7)s)
}pytest)ri   rr   rt   py7py9zassert %(py11)spy11reconciled_from	done_jsonended_by lifecycle_reconciliation_managerreconcile_run_id)rE   rR   rX   r	   
@pytest_ar_call_reprcompare	_saferepr_format_assertmsgAssertionError_format_explanation@py_builtinslocals_should_repr_global_namer   r   approx)selfrD   wsr{   rM   evrb   rc   meta@py_assert0@py_assert3@py_assert2@py_format5@py_format7@py_format4@py_format6@py_assert1timersentry@py_assert4@py_assert6@py_assert8@py_format10@py_format12s                           r/   test_happy_path_inserts_entryz+TestHappyPath.test_happy_path_inserts_entry   s   h'.S8[[ab %#%%'7'+
 $%==%-===%===%======tf~=======*;*m;;;;*m;;;*;;;;;;m;;;m;;;;;;;"$$"$$$$"$$$$$$$$$$$$"$$$$$$$"2=3E#EFWo9so%999so999999s999s999o999'9999999w$Y&3&&&&3&&&&&&&&&3&&&3&&&&&&&Z , H,,,, H,,, ,,,,,,H,,,H,,,,,,,X-+-+----+------+-------Y.;.;....;......;.......'(AFMMA&AM&,AA(,AAAAA(,AAAA(AAAAAAFAAAFAAAMAAA&AAA,AAAAAAAA&'6;6';6666';666'666;6666666Z F$FF $FFFFF $FFFF FFF$FFFFFFFF'(3G3(G3333(G333(333G3333333r1   c           	        t        |      }d}t        ||d       t        |      }g }g }t        |d||d||      }|d   }d}	||	u }
|
slt	        j
                  d|
fd	||	f      t	        j                  |      t	        j                  |	      d
z  }dd|iz  }t        t	        j                  |            d x}x}
}	d}||v }
|
st	        j
                  d|
fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      nddz  }dd|iz  }t        t	        j                  |            d x}}
g }
||
k(  }|st	        j
                  d|fd||
f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |
      dz  }dd|iz  }t        t	        j                  |            d x}}
t        |dz  dz        }|j                  }	d}i } |	||      }||v}|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      nddt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |	      t	        j                  |      t	        j                  |      t	        j                  |      dz  }d d!|iz  }t        t	        j                  |            d x}x}	x}x}}y )"Nr\   r]   rM   zrun-dryFr_   rd   re   rg   rh   r   rl   rm   rn   rp   rc   rq   rs   rt   ru   rw   rb   rx   r4   r9   r:   not inzX%(py0)s not in %(py10)s
{%(py10)s = %(py4)s
{%(py4)s = %(py2)s.get
}(%(py6)s, %(py8)s)
}r{   r   ry   py2rj   rl   py8py10assert %(py12)spy12)rE   rR   rX   r	   r   r   r   r   r   r   r   r   r   get)r   rD   r   r{   r   rb   rc   r   r   r   r   r   r   r   r   r   r   @py_assert5@py_assert7@py_assert9@py_format11@py_format13s                         r/   test_dry_run_does_not_insertz*TestHappyPath.test_dry_run_does_not_insert   s(   h'S+FG %#%%'7'+
 $%..%....%...%..........*=*o====*o===*======o===o======= ""}""""}""""""}"""}"""""""""""2=3E#EF **1W1b1*Wb11s11111s1111111s111s111111&111&111*111W111b111111111111r1   NrD   r   )__name__
__module____qualname____doc__r   r   r*   r1   r/   rZ   rZ      s    A#4J2r1   rZ   c                      e Zd ZdZddZy)TestEightStuckTaskFixtureuk   회귀 #2: 5/9 14:32 사고 fixture — 8개 task 모두 부재 → reconcile 후 8개 모두 entry 존재.c           
     v   t        |      }|dz  dz  j                  t        j                  di id      d       dd	d
dddddd}|j	                         D ]  \  }}t        |||        t        D ]&  }t        |      }g }g }t        |d| ||d||       ( t        |dz  dz        }	t        |	j                  di       j                               }
t        t              |
z
  }| }|s~t        j                  d|       dz   ddt        j                          v st        j"                  |      rt        j$                  |      ndiz  }t'        t        j(                  |            d }t+        |
      }d}||k(  }|st        j,                  d|fd||f      dt        j                          v st        j"                  t*              rt        j$                  t*              nddt        j                          v st        j"                  |
      rt        j$                  |
      ndt        j$                  |      t        j$                  |      dz  }d d!|iz  }t'        t        j(                  |            d x}x}}t        D ]3  }|	d   |   }|d"   }||   }||k(  }|st        j,                  d|fd#||f      t        j$                  |      t        j$                  |      d$z  }t        j                  | d%|d"    d&||    d'      d(z   d)|iz  }t'        t        j(                  |            d x}x}}|d*   }d+}||k(  }|slt        j,                  d|fd#||f      t        j$                  |      t        j$                  |      d$z  }d,d)|iz  }t'        t        j(                  |            d x}x}}6 y )-Nr4   task-timers-archived.jsonr:   Fr;   r=   r>   r]   2026-05-09T04:18:03+00:00z2026-05-09T05:30:00+00:00z2026-05-09T06:00:00+00:002026-05-09T09:11:17+00:00z2026-05-09T11:00:00+00:002026-05-09T12:00:00+00:00z2026-05-09T13:00:00+00:00r   r   zrun-Tr_   r9   u   미부착 task 존재: z
>assert not %(py0)sry   missing   ru   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenpresent)ry   ri   rr   rl   zassert %(py8)sr   rM   r~   rh   u   : end_time 손실 (got=z, expected=)rk   rl   r   r   r   )rE   rA   rB   rC   itemsrR   EIGHT_STUCK_TASKSrX   r	   r   setr   keysr   r   r   r   r   r   r   r   r   r   )r   rD   r   	end_timesr{   etr   rb   rc   r   r   r   r   @py_format2r   r   r   r   @py_format9r   r   r   r   s                          r/   &test_eight_stuck_tasks_all_get_entriesz@TestEightStuckTaskFixture.test_eight_stuck_tasks_all_get_entries   s   h'	h4	4@@JJ}59 	A 	
 54464#>49	
	 !( 	3GCRr2	3 % 	C$S)B')M)+O4se!+ /		 #2=3E#EFfjj"-2245'(72{?{??5gY???????7???7??????7| q |q    |q      s   s      7   7   |   q        % 	;C7OC(E$ 	# $6  $  I %  I )7    %.uZ/@.AYWZ^L\\]^     *+:{:+{::::+{:::+:::{:::::::	;r1   Nr   )r   r   r   r   r   r*   r1   r/   r   r      s
    u1;r1   r   c                      e Zd ZdZddZy)TestIdempotentuE   회귀 #3: reconcile 2회 실행 → entry 1개만 존재 (중복 X).c           
        t        |      }d}t        ||d       t        |      }dD ]  }g }g }t        ||||d||        t	        |dz  dz        }|d	   }	||	v }
|
st        j                  d
|
fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      dz  }dd|iz  }t        t        j                  |            d x}
}	|d	   |   }|d   }d}||k(  }	|	st        j                  d|	fd||f      t        j                  |      t        j                  |      dz  }t        j                  d      dz   d|iz  }t        t        j                  |            d x}x}	}g }g }t        |d||d||      }|d   }d}||u }	|	slt        j                  d|	fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}	}|d   }d}||k(  }	|	slt        j                  d|	fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}	}d}||v}	|	st        j                  d |	fd!||f      t        j                  |      d"t        j                         v st        j                  |      rt        j                  |      nd"d#z  }dd|iz  }t        t        j                  |            d x}}	y )$Nr   r   r   )run-Azrun-BTr_   r4   r9   r:   rn   rz   r{   rx   rs   rt   r   r   ru   r~   rh   u3   두 번째 run이 첫 entry를 덮어쓰면 안 됨rk   rl   zrun-Cskippedre   rg   r   reasontimer_entry_present_in_activerm   r   z%(py1)s not in %(py3)sactions_taken2rq   )rE   rR   rX   r	   r   r   r   r   r   r   r   r   r   r   )r   rD   r   r{   r   run_idrb   rc   r   r   r   r   r   r   r   r   r   r   r   actions_planned2meta2s                        r/   test_idempotent_two_runsz'TestIdempotent.test_idempotent_two_runs  sl   h'S+FG %( 	F')M)+O4!+ /	 #2=3E#EFWo%so%%%%so%%%%%%s%%%s%%%o%%%%%%%w$'(jGj(G3jjj(Gjjj(jjjGjjj5jjjjjjjj %'&(8(,
 Y'4'4''''4''''''4'''''''XA"AA"AAAAA"AAAAAAA"AAAAAAAA*@*.@@@@*.@@@*@@@@@@.@@@.@@@@@@@r1   Nr   )r   r   r   r   r   r*   r1   r/   r   r     s    O(Ar1   r   c                      e Zd ZdZddZy)TestArchiveCollisionuH   회귀 #4: archived에 이미 있는 task → active에 중복 추가 X.c                
   t        |      }d}t        ||d       |dz  dz  j                  t        j                  d||dddd	d
iid      d       t        |      }g }g }t        |d||d||      }|d   }d}	||	u }
|
slt        j                  d|
fd||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            d x}x}
}	|d   }d}	||	k(  }
|
slt        j                  d|
fd||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            d x}x}
}	|d   }d}	||	u }
|
slt        j                  d|
fd||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            d x}x}
}	d}||v}
|
st        j                  d|
fd||f      t        j                  |      d t        j                         v st        j                  |      rt        j                  |      nd d!z  }d"d#|iz  }t        t        j                  |            d x}}
t        |dz  d$z        }|j                   }	d}i } |	||      }||v}|st        j                  d|fd%||f      d&t        j                         v st        j                  |      rt        j                  |      nd&d't        j                         v st        j                  |      rt        j                  |      nd't        j                  |	      t        j                  |      t        j                  |      t        j                  |      d(z  }d)d*|iz  }t        t        j                  |            d x}x}	x}x}}t#        ||dz  d$z  |dz  dz  +      \  }}d}
||
u }|st        j                  d|fd,||
f      d-t        j                         v st        j                  |      rt        j                  |      nd-t        j                  |
      d.z  }d"d#|iz  }t        t        j                  |            d x}}
d/}
||
k(  }|st        j                  d|fd0||
f      d1t        j                         v st        j                  |      rt        j                  |      nd1t        j                  |
      d.z  }d"d#|iz  }t        t        j                  |            d x}}
y )2Nr   r   r   r4   r   r:   rF   r}   gfffff@)r   rH   rM   rN   rI   Fr;   r=   r>   zrun-collisionTr_   r   re   rg   rh   r   rl   r   timer_entry_present_in_archiveru   r~   rd   rm   r   r   rb   rq   rs   rt   r9   r   r{   activer   r   r   )active_patharchive_pathz%(py0)s is %(py3)sr   rx   archiverw   source)rE   rR   rA   rB   rC   rX   r	   r   r   r   r   r   r   r   r   r   r   r
   )r   rD   r   r{   r   rb   rc   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   s                           r/   )test_archive_present_blocks_active_insertz>TestArchiveCollision.test_archive_present_blocks_active_insert@  s   h'S+FG 
h4	4@@JJ'*'2(C&106
 #  	A 	
$ !%#%%'7'+
 I&$&$&&&&$&&&&&&$&&&&&&&H~A!AA~!AAAAA~!AAAA~AAA!AAAAAAAA$%..%....%...%..........*?*-????*-???*??????-???-??????? #2=3E#EF **1W1b1*Wb11s11111s1111111s111s111111&111&111*111W111b11111111111 /X(::h)DD

 w$w$ww$""v""""v""""""v"""v""""""""""r1   Nr   )r   r   r   r   r   r*   r1   r/   r   r   =  s
    R6#r1   r   c                       e Zd ZdZddZddZy)TestMtimeFallbackRegressionuO   회귀 #5: reconcile 후 dashboard helpers.py:450-454 mtime fallback 미발동.c           	        t        |      }d}d}t        |||       t        |      }t        |d||dg g        t	        |dz  dz        }|d	   |   }|j                  d
      }|s{t        j                  d      dz   ddt        j                         v st        j                  |      rt        j                  |      ndiz  }	t        t        j                  |	            |j                  d
      }
|
 }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }t        j                  d      dz   d|iz  }t        t        j                  |            dx}}|
|k(  }|st        j                  d|fd|
|f      dt        j                         v st        j                  |
      rt        j                  |
      nddt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            d}y)uW   helpers.py:450-454 등가 로직: task-timers.json의 end_time 우선, 없으면 mtime.r   r]   r   r^   Tr_   r4   r9   r:   rM   u>   reconcile 후 end_time 부재 → mtime fallback 발동 위험z
>assert %(py0)sry   end_time_from_timersFre   r   used_mtime_fallbackrx   uv   task-timers.json end_time이 있는데도 mtime fallback이 발동되면 안 됨 (helpers.py:450-454 회귀 게이트)r|   rt   Nru   )z%(py0)s == %(py2)stask_info_end_timetrue_end_timery   r   zassert %(py4)srj   )rE   rR   rX   r	   r   r   r   r   r   r   r   r   r   r   r   )r   rD   r   r{   r   r   r   r   r   @py_format1r   r   r   r   r   r   @py_format3r   s                     r/   ?test_end_time_present_after_reconcile_so_mtime_fallback_skippedz[TestMtimeFallbackRegression.test_end_time_present_after_reconcile_so_mtime_fallback_skipped  s   h'3S=9 %0"Tb	
 #2=3E#EFw$$yy4#ee%eeeeeee#eee#eeeee #YYz2"44&+ 	
"e+ 	
 	
"e 	
 	
 
6	
 	
  # 	
 	
 
	 # 	
 	
 
	 ', 	
 	
 4	
 	
 	
 	
 	
 "]2222!]222222!222!222222]222]2222222r1   c           	        t        |      }t        ddd      }g }g }t        dd||d||      }|d   }d}||u }	|	slt        j                  d	|	fd
||f      t        j
                  |      t        j
                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}	}|d   }d}||k(  }	|	slt        j                  d|	fd||f      t        j
                  |      t        j
                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}	}t        |dz  dz        }d}|j                  }d}i } |||      }||v}	|	st        j                  d|	fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      ndt        j
                  |      t        j
                  |      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            dx}x}	x}x}x}}y)uB   완료 evidence 없는 in-progress task는 reconcile하지 않음.r   OPENrunning)r   r   r   zrun-xTr_   r   re   rg   rh   r   rl   Nr   no_completion_evidenceru   r~   r4   r9   r:   r   )zX%(py1)s not in %(py11)s
{%(py11)s = %(py5)s
{%(py5)s = %(py3)s.get
}(%(py7)s, %(py9)s)
}r   )ri   rr   rt   r   r   r   zassert %(py13)spy13)rE   r0   r	   r   r   r   r   r   r   r   r   r   r   )r   rD   r   r   rb   rc   r   r   r   r   r   r   r   r   r   r   @py_assert10r   @py_format14s                      r/   !test_no_completion_evidence_skipsz=TestMtimeFallbackRegression.test_no_completion_evidence_skips  s   h'K&yY#%%'7"T'

 I&$&$&&&&$&&&&&&$&&&&&&&H~9!99~!99999~!9999~999!99999999"2=3E#EF9&**9W9b9*Wb"99{"99999{"9999{999999&999&999*999W999b999"999999999r1   Nr   )r   r   r   r   r   r   r*   r1   r/   r   r   }  s    Y3B:r1   r   c                      e Zd ZdZddZy)TestChatIsolationuQ   회귀 #6: 다른 chat record fixture는 reconcile 결과에 포함되지 않음.c                   t        |      }d}t        ||d       |dz  }|j                  dd       |dz  j                  t	        j
                  dd	d
dddd      dz   d       t        |      }t        |d||dg g       }|d   }d}||u }	|	slt        j                  d|	fd||f      t        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}	}t        |dz  dz        }|d   |   }t	        j
                  |d      }d }||v}	|	st        j                  d!|	fd"||f      t        j                  |      d#t        j                         v st        j                   |      rt        j                  |      nd#d$z  }t        j"                  d%      d&z   d'|iz  }t        t        j                  |            dx}}	d
}||v}	|	st        j                  d!|	fd"||f      t        j                  |      d#t        j                         v st        j                   |      rt        j                  |      nd#d$z  }t        j"                  d(      d&z   d'|iz  }t        t        j                  |            dx}}	d}||v}	|	st        j                  d!|	fd"||f      t        j                  |      d#t        j                         v st        j                   |      rt        j                  |      nd#d$z  }d)d'|iz  }t        t        j                  |            dx}}	d*}||v}	|	st        j                  d!|	fd"||f      t        j                  |      d#t        j                         v st        j                   |      rt        j                  |      nd#d$z  }d)d'|iz  }t        t        j                  |            dx}}	y)+u   task-2528 fix는 task-timers.json만 건드림. 다른 chat의 schedule history /
        cron record는 별도 namespace이며 reconcile 결과 entry에 포함되어선 안 됨.r   r   r    fake_schedule_history_other_chatTr6   z	OTHER.logz2026-05-10T14:31:35+09:00l   c(	 	OTHER1234u   task-2517 무관ok
irrelevant)tschat_idschedule_idpromptrN   response
r=   r>   zrun-isor_   rd   re   rg   rh   r   rl   Nr4   r9   r:   Fr;   
9999999999r   r   	entry_strrq   u   다른 chat id 노출r|   rt   u   다른 chat schedule_id 노출rs   
6937032012)rE   rR   r@   rA   rB   rC   rX   r	   r   r   r   r   r   r   r   r   r   r   )r   rD   r   r{   other_chat_dirr   r   r   r   r   r   r   r   r   r  r   r   s                    r/   .test_other_chat_record_not_in_reconcile_outputz@TestChatIsolation.test_other_chat_record_not_in_reconcile_output  s    h'S+FG "$FFTD9	+	%11JJ1%*,(    	2 
	
 !%7BTb

 $%--%----%---%----------"2=3E#EFw$JJu59	E|9,EEE|9EEE|EEEEEE9EEE9EEEE.EEEEEEEM{)+MMM{)MMM{MMMMMM)MMM)MMMM-MMMMMMM1B1BBBB1BBB1BBBBBBBBBBBBBBBB ,|9,,,,|9,,,|,,,,,,9,,,9,,,,,,,r1   Nr   )r   r   r   r   r  r*   r1   r/   r   r     s
    ['-r1   r   c                       e Zd ZdZddZddZy)TestTokenRawZerouH   회귀 #7: reconcile 결과 JSON에 ghs_/ghp_/github_pat_ prefix 부재.c           	        t        |      }d}t        ||d       t        |      }t        |d||dg g       }|d   }d}||u }|slt	        j
                  d|fd	||f      t	        j                  |      t	        j                  |      d
z  }	dd|	iz  }
t        t	        j                  |
            d x}x}}|dz  dz  j                  d      }t        j                  |d      }t        D ]  }||v}|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      nddt        j                         v st	        j                  |      rt	        j                  |      nddz  }t	        j                   d| d      dz   d|iz  }	t        t	        j                  |	            d }||v}|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      nddt        j                         v st	        j                  |      rt	        j                  |      nddz  }t	        j                   d| d      dz   d|iz  }	t        t	        j                  |	            d } g }d}||v}|}|sd}||v}|}|sXt	        j
                  d|fd ||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndd!z  }d"d#|iz  }|j#                  |       |st	        j
                  dfd$|f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndd%z  }d&d'|iz  }|j#                  |       t	        j$                  |d(      i z  }d)d*|iz  }t        t	        j                  |            d x}x}x}x}x}}y )+Nr   r   r   z	run-tokenTr_   rd   re   rg   rh   r   rl   r4   r9   r=   r>   Fr;   r   z%(py0)s not in %(py2)sprefixtimers_textr   u"   task-timers.json에 token prefix 'u   ' 노출
>assert %(py4)srj   	meta_textu    reconcile meta에 token prefix 'GITHUB_TOKENzGITHUB_TOKEN=)z%(py3)s not in %(py5)s)rr   rt   z%(py7)sr   )z%(py10)s not in %(py12)s)r   r   z%(py14)spy14   zassert %(py17)spy17)rE   rR   rX   r	   r   r   r   r   r   	read_textrB   rC   TOKEN_PREFIXESr   r   r   r   append_format_boolop)r   rD   r   r{   r   r   r   r   r   r   r   r  r  r  r   r   r   r   @py_assert11r   @py_format8r   @py_format15@py_format16@py_format18s                            r/   'test_reconciled_entry_no_token_prefixesz8TestTokenRawZero.test_reconciled_entry_no_token_prefixes  s   h'S+FG %7bTb

 $%--%----%---%---------- H}'99DDgDVJJt%8	$ 	`F,ccc6cccccc6ccc6ccccccccccccc0RSYRZZb.ccccccc*___6______6___6_____________.NvhV^,_______	` 	W~V~[0VOVO;4VVVVV~[VVV~VVVVVV[VVV[VVVVVVVO;VVVOVVVVVV;VVV;VVVVVVVVVVVVVVr1   c           	        t        |      }d}t        ||d       t        |      }t        |d||      }d }||u}|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      d	z  }d
d|iz  }	t        t	        j                  |	            d x}}fd |      D ]  }
t        D ]  }||
v}|st	        j
                  d|fd||
f      dt        j                         v st	        j                  |      rt	        j                  |      nddt        j                         v st	        j                  |
      rt	        j                  |
      nddz  }t	        j                  d|
d d  d      dz   d|iz  }t        t	        j                  |            d }  y )Nr   r   r   zrun-z)r`   )is not)z%(py0)s is not %(py3)sr   rx   rs   rt   c              3    K   t        | t              r&| j                         D ]  } |      E d {     y t        | t        t        f      r| D ]  } |      E d {     y t        | t
              r|  y y 7 K7 wN)
isinstancer+   valueslisttuplestr)objv_walks     r/   r1  zLTestTokenRawZero.test_build_reconciled_entry_no_token_in_dict.<locals>._walk  s|     #t$ (A$Qx''(C$/ (A$Qx''(C%	 &	 ( (s!   /BA>-B B !B Br   r  r  sr   u/   reconciled entry value에 token prefix 노출:    z...r  rj   )rE   rR   rX   r   r   r   r   r   r   r   r   r   r  r   )r   rD   r   r{   r   r   r   r   r   r   r2  r  r   r   r1  s                 @r/   ,test_build_reconciled_entry_no_token_in_dictz=TestTokenRawZero.test_build_reconciled_entry_no_token_in_dict  sG   h'S+FG %-c7BrR  uD    uD      u   u   D       	 u 	fA( fQeeevQeeeeeeveeeveeeeeeQeeeQeeee*YZ[\_]_Z`Yaad(eeeeeeef	fr1   Nr   )r   r   r   r   r%  r4  r*   r1   r/   r  r    s    RW2fr1   r  c                      e Zd ZdZddZy)TestReconcileIntegrationug   reconcile() 호출 시 task-2528 helper가 정상 동작하고 task-2518 stuck 처리와 충돌 없음.c                    t        |      }d}d}t        |||       |dz  dz  }|| dz  j                  dd	       || d
z  j                  dd	       d$d}d dd}d$d}t        |d||||      }	t	        |dz  dz        }
|
j
                  }d}i } |||      }||v }|s<t        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  |
      rt        j                  |
      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }t        j                  d|
 d|	j                         dz   d|iz  }t        t        j                  |            d x}x}x}x}}|	j                   }|j
                  }d}i } |||      }|j
                  }d} ||      }d}||u }|s9t        j                  d|fd||f      d t        j                         v st        j                  |	      rt        j                  |	      nd t        j                  |      t        j                  |      t        j                  |      t        j                  |      t        j                  |      t        j                  |      t        j                  |      t        j                  |      t        j                  |      d!
z  }d"d#|iz  }t        t        j                  |            d x}x}x}x}x}x}x}x}x}}y )%Nr   r   r   r4   r5   z.done.ackedz{}r=   r>   z.merge-donec                    ~ ddddidS )NrU   rS   oidrV   )numberstatemergeCommitr*   r   s    r/   fake_pr_lookupzkTestReconcileIntegration.test_reconcile_finalized_task_with_no_timer_entry_gets_one.<locals>.fake_pr_lookup1  s    ! %'QR r1   )cwdc               ~    ~~ G d d      }| d d g dk(  r |       S | d d ddgk(  r |       }d|_         |S  |       S )	Nc                      e Zd ZdZdZdZy)tTestReconcileIntegration.test_reconcile_finalized_task_with_no_timer_entry_gets_one.<locals>.fake_runner.<locals>._Rr    N)r   r   r   
returncodestdoutstderrr*   r1   r/   _RrB  ;  s    
r1   rG     )gitz
merge-basez--is-ancestor   rI  z	ls-remoterC  )rE  )argsr?  _kwrG  rs        r/   fake_runnerzhTestReconcileIntegration.test_reconcile_finalized_task_with_no_timer_entry_gets_one.<locals>.fake_runner9  sV    S  BQxAAtBQxE;//D4Kr1   c                    ~ i S r)  r*   r=  s    r/   fake_timer_loaderznTestReconcileIntegration.test_reconcile_finalized_task_with_no_timer_entry_gets_one.<locals>.fake_timer_loaderG  s
    Ir1   T)ra   r`   runner	pr_lookuptimer_loaderr9   r:   rn   )zT%(py0)s in %(py10)s
{%(py10)s = %(py4)s
{%(py4)s = %(py2)s.get
}(%(py6)s, %(py8)s)
}r{   r   r   u7   reconcile() integrated path에서 entry 누락: timers=z, actions_taken=z
>assert %(py12)sr   worktree_timer_reconcilerd   re   )z%(py16)s
{%(py16)s = %(py12)s
{%(py12)s = %(py10)s
{%(py10)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.backfill_metadata
}.get
}(%(py6)s, %(py8)s)
}.get
}(%(py14)s)
} is %(py19)sreport)
ry   r   rj   rl   r   r   r   r  py16py19zassert %(py21)spy21)r   r.  returnr+   )rE   rR   rA   r   r   r   r   r   r   r   r   r   r   rb   r   r   backfill_metadata)r   rD   r   r{   rM   r5   r>  rN  rP  rU  r   r   r   r   r   r   r   r   r   @py_assert13@py_assert15@py_assert18@py_assert17@py_format20@py_format22s                            r/   :test_reconcile_finalized_task_with_no_timer_entry_gets_onezSTestReconcileIntegration.test_reconcile_finalized_task_with_no_timer_entry_gets_one%  s1   h'.S84 h)	SE%	%11$1I	SE%	%11$1I	 &* 		 $*
 #2=3E#EFjj 	
 	
" 	
j"- 	
s-- 	
 	
 	
s- 	
 	
 
6	
 	
   	
 	
 
	  	
 	
 
6	
 	
   	
 	
 
	  	
 	
 
	 ! 	
 	
 
	 ") 	
 	
 
	 +- 	
 	
 
	 . 	
 	
  FfX N#1124	
 	
 	
 	
 	
 	
 ''i'++i,Fii+,FKiKOOiP`iOP`aieiiaeiiiiiaeiiiiiiiviiiviii'iii+iii,FiiiiiiKiiiOiiiP`iiiaiiieiiiiiiiiiir1   Nr   )r   r   r   r   ra  r*   r1   r/   r6  r6  "  s    q5jr1   r6  )rY  r   )rD   r   r2   boolrY  r   )rP   r   r   r.  rM   r.  rH   r.  rI   floatrJ   r.  rY  None)r   r.  r   rb  r!   rb  r   z
str | Noner   rb  rY  r   )-r   
__future__r   builtinsr   _pytest.assertion.rewrite	assertionrewriter   rB   syspathlibr   typingr   r   __file__resolveparent_WORKTREE_ROOTr.  pathremoveinsert&utils.lifecycle_reconciliation_managerr   r   r   r	   r
   r   r   r  r0   rE   rR   rX   rZ   r   r   r   r   r   r  r6  r*   r1   r/   <module>ru     s_   #    
   h'')0077>>~#(("HHOOC'( 3~& ' 	  1%6 =A  Va/5',BESW( ;?W[/7RV",KO[l*@2 @2N4; 4;v+A +Ad9# 9#@4: 4:v*- *-b2f 2fr8j 8jr1   