
    " j2W                    6   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mZmZ ddlZ ee      j'                         j(                  j(                  j(                  Z ee      e
j.                  v r!e
j.                  j1                   ee             e
j.                  j3                  d ee             ddlmZmZ ddlmZmZm Z m!Z!m"Z"m#Z# dZ$d	Z%d
Z&dZ'dZ(dZ)d!dZ*e&fd"dZ+ejX                  d#d       Z-ejX                  d#d       Z.	 	 	 	 	 	 d$dZ/	 	 	 	 	 	 d$dZ0ejb                  je                  dg d      	 	 	 	 	 	 	 	 d%d       Z3	 	 	 	 	 	 d$dZ4	 	 	 	 	 	 d$dZ5	 	 	 	 	 	 	 	 d&dZ6d'dZ7	 	 	 	 	 	 d$dZ8	 	 	 	 	 	 d$dZ9d(dZ:d(dZ;d(dZ<d(d Z=y))u  tests/regression/test_cron_timers_upsert_2533.py

회귀 테스트 — task-2533 cron --session timers upsert hook (신호등 sync fix A).

회장 §본질 (2026-05-10 신호등 sync fix A):
  cron ``--session`` 발사 직후 ``memory/task-timers.json`` 에 task entry 가 자동 갱신되지
  않아 대시보드 신호등 100% gap. 본 hook 은 cron 발사 직후 timers entry 를 atomic+idempotent
  하게 upsert. 본 회귀는 7개 시나리오를 박제하여 동일 사고가 다시 발생하지 못하도록 강제한다.

회귀 7 (정확히 7개):
  1. cron 발사 성공 → timers entry 신규 생성 (status=running, schedule_id 명시)
  2. 동일 task 재발사 → entry 1건만 유지 (idempotent — 중복 X)
  3. opt-out prompt(read_only/analysis_only/report_only) 도 upsert 호출 시 entry 생성
     (활성 표시 필수)
  4. cron 발사 실패 (rc!=0) → timers entry 미생성 (atomic)
  5. team_id 추출 정확성 — task md / explicit / prompt fallback 우선순위
  6. schedule_id 발사에서 description 에 raw token / raw uuid / raw hex key 누수 0
  7. chat=6937032012 격리 — entry 에 chat_id 명시 저장 (다른 chat entry 와 충돌 X)

회장 §보안:
  - production timers JSON 절대 건드리지 않음 (모든 테스트가 ``tmp_path`` 사용)
  - subprocess 실행 X (runner 가 stub 으로 주입)
  - raw cron prompt 가 description 에 그대로 저장되지 않음을 정적 검사
    )annotationsN)Path)ListSequenceTuple)&parse_schedule_id_from_cokacdir_stdoutrun_cron_with_timers_upsert)DEFAULT_TIMERS_PATHDESCRIPTION_MAX_LENTIMERS_TASK_KEYextract_task_id_from_promptextract_team_id_from_task_mdsanitize_description
6937032012
9999999999	5C9995CCB0b94683120a691cfz$5eee7634-b0be-4594-b84e-311ae64e557b(ghp_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc                $     g d fd}|fS )uZ   지정 rc/stdout 을 반환하는 stub runner 와 호출 기록 list 를 함께 만든다.c                @    j                  t        |              fS )N)appendtuple)argvcallsrcstdouts    D/home/jay/workspace/tests/regression/test_cron_timers_upsert_2533.py_runnerz_make_runner.<locals>._runnerH   s    U4[!6z    )r   zSequence[str]returnzTuple[int, str] )r   r   r   r   s   `` @r   _make_runnerr"   D   s    !#E '>r   c                :    t        j                  d| dddd      S )u*   cokacdir cron 등록 정상 응답 stdout.okz...z	0 9 * * *)statusidpromptscheduleFensure_ascii)jsondumps)schedule_ids    r   
_ok_stdoutr.   O   s)    ::#		
  r   c                h    | dz  }|j                  t        j                  i dddd      d       |S )	u<   비어있는 task-timers.json fixture (production 오염 X).task-timers.jsonr   )taskscounterF   )r*   indentutf-8encoding)
write_textr+   r,   )tmp_pathps     r   empty_timersr;   \   s?     	%%ALL

RA.U1M   Hr   c                0    | dz  }|j                          |S )u)   비어있는 tasks/ 디렉토리 fixture.r1   )mkdir)r9   ds     r   empty_tasks_dirr?   g   s     	7AGGIHr   c           	     x   |dz  }|j                  dd       d}t        dt                     \  }}t        dd	|d
t        f|t        d| ||      \  }}}d}	||	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}|	|k(  }|st        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  }t        j                  d      dz   d|iz  }t        t        j                  |            dx}	x}}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}
}	|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(   }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        k(  }	|	st        j                  d|	fd+|t        f      t        j                  |      d,t        j                         v st        j                  t              rt        j                  t              nd,d-z  }dd|iz  }t        t        j                  |            dx}}	|d.   }|t        k(  }	|	st        j                  d|	fd+|t        f      t        j                  |      d/t        j                         v st        j                  t              rt        j                  t              nd/d-z  }dd|iz  }t        t        j                  |            dx}}	|d0   }d}||u}	|	slt        j                  d|	fd1||f      t        j                  |      t        j                  |      d#z  }d$d%|iz  }t        t        j                  |            dx}x}	}|d2   }d}||u }	|	slt        j                  d3|	fd4||f      t        j                  |      t        j                  |      d#z  }d$d%|iz  }t        t        j                  |            dx}x}	}t!        j"                  | j%                  d            }d!}|t&           }||v }	|	slt        j                  d5|	fd6||f      t        j                  |      t        j                  |      d#z  }d$d%|iz  }t        t        j                  |            dx}x}	}|t&           d!   }|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        k(  }	|	st        j                  d|	fd+|t        f      t        j                  |      d,t        j                         v st        j                  t              rt        j                  t              nd,d-z  }dd|iz  }t        t        j                  |            dx}}	y)7u<   cron 발사 성공 → tasks[task-2533] entry 신규 생성.task-2533.mduH   # task-2533 — cron timers upsert hook

dev1-team 헤르메스 단독.
r5   r6   uP   task-2533 진행. 본질=cron --session 발사 hook 이 task-timers.json upsert.r   r   r   /usr/local/bin/cokacdirz--cronz--chatNr   r'   chatexplicit_teamtimers_path	tasks_dirrunner==z%(py0)s == %(py3)sr   py0py3assert %(py5)spy5   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenr   rN   py1rO   py6u0   runner 는 정확히 1번 호출되어야 한다
>assert %(py8)spy8is notz%(py0)s is not %(py3)sentryu:   성공한 dispatch 직후 entry 가 반환되어야 한다
>assert %(py5)stask_id	task-2533z%(py1)s == %(py4)srV   py4assert %(py6)srW   team_id	dev1-teamr%   runningr-   z%(py1)s == %(py3)sSCHEDULE_IDrV   rO   chat_id
CHAIR_CHAT
start_time)z%(py1)s is not %(py4)send_timeis)z%(py1)s is %(py4)sinz%(py1)s in %(py4)s)r8   r"   r.   r	   rl   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationrT   _format_assertmsgri   r+   loads	read_textr   )r;   r?   md_pathr'   r   rI   r   _r]   @py_assert2@py_assert1@py_format4@py_format6@py_assert5@py_assert4@py_format7@py_format9@py_assert0@py_assert3@py_format5	persistedsaveds                         r   1test_2533_new_cron_dispatch_creates_running_entryr   s   s1   
 .GU  
 `F Ajl;ME6.'68ZP !LB5 N27NNN2NNNNNN2NNN2NNNNNNNNNNu:NN:?NNN:NNNNNN3NNN3NNNNNNuNNNuNNN:NNNNNNNNNNNNNNZ5ZZZ5ZZZZZZ5ZZZ5ZZZZZZZZZZZZZ*{*{****{******{********{*{****{******{*******?'i'?i''''?i'''?'''i'''''''.;....;.........;...;.......)z))))z)))))))))z)))z)))))))*d*d****d******d*******$$$$$$$$$$$$$$$$$$$

<1171CDI4)O44;44444;4444;44444444444o&{3E?'i'?i''''?i'''?'''i'''''''.;....;.........;...;.......r   c                   |dz  }|j                  dd       d}t        dt        d            \  }}t        d	|t        | ||
      \  }}}g }d}	||	k(  }
|
}|
rd}||u}|}|sXt        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  }|j                  |       |
rt        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  }|j                  |       t        j                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}
x}	x}}|d   }t        dt        d            \  }}t        d	|t        | ||
      \  }}}g }d}	||	k(  }
|
}|
rd}||u}|}|sXt        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  }|j                  |       |
rt        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  }|j                  |       t        j                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}
x}	x}}t        j                   | j#                  d            }|t$           }t'        |      }d}||k(  }	|	s&t        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  }t        j(                  d#t+        |j-                                      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}}|d&   }|d,   }d-}
||
k(  }|slt        j                  d|fd.||
f      t        j                  |      t        j                  |
      d/z  } d0d1| iz  }t        t        j                  |            dx}x}}
|d2   }d}
||
k(  }|slt        j                  d|fd.||
f      t        j                  |      t        j                  |
      d/z  } d0d1| iz  }t        t        j                  |            dx}x}}
|d   }||k(  }|st        j                  d|fd3||f      t        j                  |      d4t        j                         v st        j                  |      rt        j                  |      nd4d)z  }d*d+|iz  }t        t        j                  |            dx}}|d5   }||k\  }|st        j                  d6|fd7||f      t        j                  |      d4t        j                         v st        j                  |      rt        j                  |      nd4d)z  }d*d+|iz  }t        t        j                  |            dx}}y)8ud   동일 task_id 두 번 발사 → tasks dict 길이 1 유지, start_time 보존, schedule_id 갱신.rA   u   # task-2533

dev1-team 단독.
r5   r6   u   task-2533 진행. retry.r   FIRST_IDrB   rC   r   r'   rE   rG   rH   rI   NrJ   )z%(py2)s == %(py5)src1py2rQ   %(py7)spy7rZ   )z%(py9)s is not %(py12)sentry1)py9py12z%(py14)spy14zassert %(py17)spy17rm   	SECOND_IDrc2entry2rR   rS   rT   r1   rU   u(   중복 entry 생기면 안 된다, got: rX   rY   r`   rq   )z%(py1)s in %(py3)srj   rP   rQ   r%   rg   ra   rb   rd   rW   r-   rh   first_startlast_dispatch_at)>=)z%(py1)s >= %(py3)s)r8   r"   r.   r	   rl   rt   ru   rv   rw   rx   ry   r   _format_booloprz   r{   r+   r}   r~   r   rT   r|   listkeys)!r;   r?   r   r'   r   runner1r   r   r   r   r   r   @py_assert11@py_assert10r   @py_format8@py_format13@py_format15@py_format16@py_format18r   runner2r   r   r   r1   r   r   r   r   r   r   r   s!                                    r   ,test_2533_redispatch_same_task_is_idempotentr      s    .G;gN'F :j+ABJAw0) !NCF +!*3!8*d*d*****3!******3***3***!*******d************d**************&K :k+BCJAw0) !NCF +!*3!8*d*d*****3!******3***3***!*******d************d**************

<1171CDIo&Eu:[[:?[[[:[[[[[[3[[[3[[[[[[u[[[u[[[:[[[[[[FtEJJLGYFZ[[[[[[[[;%;%;%%+E?'i'?i''''?i'''?'''i'''''''.;.;....;......;.......-+----+---------+---+-------#$3$3333$333$3333333333333333r   opt_out_token)zread_only: truezanalysis_only: truezreport_only: truezfinalize_policy: no_prc                   |dz  }|j                  dd       d| }t        dt                     \  }}t        d|t        | ||	      \  }}}d}	||	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}
}	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      dz   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}	}t        j                  | j!                  d            }d}|t"           }||v }	|	slt        j                  d|	fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}	}y) uh   opt-out 토큰이 prompt 에 있어도 timers upsert 는 호출되어 활성 봇 표시가 보장된다.rA   
dev1-team
r5   r6   ztask-2533 audit only. r   rB   r   r   rJ   rL   r   rM   rP   rQ   NrZ   r\   r]   u   opt-out 토큰 (uM   ) 이 있어도 timers upsert 는 활성 표시를 위해 호출돼야 한다r^   r%   rg   ra   rb   rd   rW   r`   rq   rs   )r8   r"   r.   r	   rl   rt   ru   rv   rw   rx   ry   rz   r{   r|   r+   r}   r~   r   )r;   r?   r   r   r'   r   rI   r   r]   r   r   r   r   r   r   r   r   r   s                     r   %test_2533_optout_prompt_still_upsertsr      s    .G}w7%m_5F*,7IAv.) !LB5 N27NNN2NNNNNN2NNN2NNNNNNNNNN 5  5                  =/)vw     ?'i'?i''''?i'''?'''i'''''''

<1171CDI4)O44;44444;4444;44444444444r   c                .   |dz  }|j                  dd       | j                  d      }t        dd      \  }}t        dd	t        | ||
      \  }}}d}||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}	}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}	}| j                  d      }||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  }t        j                  d      dz   d|iz  }t        t        j                  |            d}	y)u<   runner rc != 0 → upsert hook skip. timers 파일 변경 0.rA   r   r5   r6   r3   zboom: dispatch failedrB   r      task-2533 진행.r   rJ   rL   r   rM   rP   rQ   Nro   z%(py0)s is %(py3)sr]   )z%(py0)s == %(py2)sbeforeafterrN   r   uI   dispatch 실패 시 timers JSON 은 한 바이트도 바뀌면 안 된다
>assert %(py4)src   )r8   r~   r"   r	   rl   rt   ru   rv   rw   rx   ry   rz   r{   r|   )r;   r?   r   r   r   rI   r   r]   r   r   r   r   r   @py_format3r   s                  r   )test_2533_failed_dispatch_does_not_upsertr     s    .G}w7##W#5F*ABIAv.)" !LB5 N27NNN2NNNNNN2NNN2NNNNNNNNNN5D=5D55D""G"4EU?ggg6Ugggggg6ggg6ggggggUgggUgggggggggggr   c                   |dz  }|j                  dd       | j                  d      }t        j                  ddid      }t	        d	|
      \  }}t        ddt        | ||      \  }}}d	}	||	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}
}	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}
}	| j                  }
d} |
|      }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |
      t        j                  |      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}
x}x}}y)u^   rc=0 인데 stdout 에 id 가 없으면 skip (atomic — schedule_id 없는 entry 안 만듦).rA   r   r5   r6   r%   r$   Fr)   r   rB   r   r   r   rJ   rL   r   rM   rP   rQ   Nro   r   r]   )zX%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.read_text
}(encoding=%(py4)s)
} == %(py8)sr;   r   )rN   r   rc   rW   rY   assert %(py10)spy10)r8   r~   r+   r,   r"   r	   rl   rt   ru   rv   rw   rx   ry   rz   r{   )r;   r?   r   r   
bad_stdoutr   rI   r   r]   r   r   r   r   r   r   @py_assert7r   @py_format11s                     r   5test_2533_dispatch_ok_but_no_schedule_id_skips_upsertr     s    .G}w7##W#5FXt,5AJ*5IAv.)" !LB5 N27NNN2NNNNNN2NNN2NNNNNNNNNN5D=5D55D!!=7=!73=3v====3v======<===<===!===7===3======v===v========r   c           	     B
   |dz  }|j                  dd       t        dt                     \  }}t        ddt        d	| ||
      \  }}}g }d}||u}	|	}
|	r|d   }d	}||k(  }|}
|
s+t        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  }|j                  |       |	r_t        j                  dfdf      t        j                  |      t        j                  |      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}x}}|dz  }|j                  t        j                   di i      d       t        dt                     \  }}t        ddt        d|||
      \  }}}g }d}||u}	|	}
|	r|d   }d}||k(  }|}
|
s+t        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  }|j                  |       |	r_t        j                  d|fd||f      t        j                  |      t        j                  |      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}x}}|dz  }|j#                          |dz  }|j                  t        j                   di i      d       t        dt                     \  }}t        ddt        d|||
      \  }}}g }d}||u}	|	}
|	r|d   }d }||k(  }|}
|
s+t        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  }|j                  |       |	r_t        j                  d|fd||f      t        j                  |      t        j                  |      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}x}}y)!uk   우선순위:
       1. ``--team`` (explicit) > 2. task md 의 ``devN-team`` > 3. prompt body fallback
    rA   z# task-2533

dev2-team
r5   r6   r   rB   r   u   task-2533 진행 dev9-teamrf   rD   Nre   rZ   )z%(py2)s is not %(py5)sr]   r   r   r   rJ   )z%(py10)s == %(py13)s)r   py13z%(py15)spy15zassert %(py18)spy18zt2.jsonr1   z	dev2-teamtasks2zt3.jsonu'   task-2533 진행 dev7-team 헤르메스z	dev7-team)r8   r"   r.   r	   rl   rt   ru   rv   rw   rx   ry   r   r   rz   r{   r+   r,   r=   )r;   r?   r9   mdr   rI   r]   r   r   r   r   @py_assert9@py_assert12r   r   r   @py_format14r   @py_format17@py_format19timers2empty_tasks_dir2timers3s                          r   %test_2533_team_id_extraction_priorityr   7  sv    
>	)BMM.MA*,7IAv-)+! !KAq% A@5@y!1@[@!1[!@@@@@5@@@@@@5@@@5@@@@@@@@@@!1[@@@!1@@@[@@@@@@@@@@@@@@ "Gtzz7B-07C*,7IAv-)+!KAq% A@5@y!1@[@!1[!@@@@@5@@@@@@5@@@5@@@@@@@@@@!1[@@@!1@@@[@@@@@@@@@@@@@@  (*"Gtzz7B-07C*,7IAv-)8"KAq% A@5@y!1@[@!1[!@@@@@5@@@@@@5@@@5@@@@@@@@@@!1[@@@!1@@@[@@@@@@@@@@@@@@r   c                `   | dz  }|j                  d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}}|j                  d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}}|j                  d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}}d}t        ||      }d}||k(  }|s
t        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                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}x}}| dz  }
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}}y)u/   ``extract_team_id_from_task_md`` 단위 동작.rA   u   dev1-team 단독
r5   r6   rf   rJ   rS   r   r   rU   assert %(py8)srY   Nu   dev3 헤르메스만
z	dev3-teamu   아무 단어도 없음
zunknown-teamcustom)fallback)zB%(py5)s
{%(py5)s = %(py0)s(%(py1)s, fallback=%(py3)s)
} == %(py8)s)rN   rV   rO   rQ   rY   r   r   z
no-such.mdmissing)
r8   r   rt   ru   rv   rw   rx   ry   rz   r{   )r9   r   r   r   r   r   r   r   @py_assert6r   r   s              r   -test_2533_extract_team_id_from_task_md_helperr   m  s>   	N	"B MM&M9'+:{:+{::::+{::::::':::'::::::::::::+:::{::::::: MM*WM='+:{:+{::::+{::::::':::'::::::::::::+:::{::::::: MM-M@'+=~=+~====+~======'==='============+===~=======5=J'X>J(J>(JJJJ>(JJJJJJ'JJJ'JJJJJJJJJJJJXJJJ>JJJ(JJJJJJJ %G'0BNB0NBBBB0NBBBBBB'BBB'BBBBBBBBBBBB0BBBNBBBBBBBr   c           	        |dz  }|j                  dd       dt         dt         dt         }t	        dt               	      \  }}t        d
|t        | ||      \  }}}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}}| j!                  d      }t        |v}|st        j                  d|fdt        |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dz  }t        j"                  d      dz   d|iz  }t        t        j                  |            d}t        |v}|st        j                  d|fdt        |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dz  }t        j"                  d      dz   d|iz  }t        t        j                  |            d}t        |v}|st        j                  d|fdt        |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dz  }t        j"                  d      dz   d|iz  }t        t        j                  |            d}|d   }t        |v}|st        j                  d|fdt        |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 dz  }d!d|iz  }t        t        j                  |            d}t        |v}|st        j                  d|fdt        |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 dz  }d!d|iz  }t        t        j                  |            d}t        |v}|st        j                  d|fdt        |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 dz  }d!d|iz  }t        t        j                  |            d}t%        |      }|t&        k  }|s/t        j                  d"|fd#|t&        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                  |      d%t        j                         v st        j                  t&              rt        j                  t&              nd%d&z  }
d'd(|
iz  }t        t        j                  |            dx}}t)        |      }g }d)}||v }|}|st%        |      }t%        |      }||k  }|}|s@t        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+                  |       |sst        j                  d/fd0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                  |      d$t        j                         v st        j                  t$              rt        j                  t$              nd$d1t        j                         v st        j                  |      rt        j                  |      nd1t        j                  |      d2z  }d3d4|iz  }|j+                  |       t        j,                  |d5      i z  }d6d7|iz  }t        t        j                  |            dx}x}x}x}x}x}}t        |v}|st        j                  d|fdt        |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,dz  }d!d|iz  }t        t        j                  |            d}t        |v}|st        j                  d|fdt        |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,dz  }d!d|iz  }t        t        j                  |            d}t        |v}|st        j                  d|fdt        |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,dz  }d!d|iz  }t        t        j                  |            d}y)8u   raw token / raw uuid / raw hex key 가 prompt 에 있어도 description 에 그대로 저장 X.

    redact 마커 (``<redacted-...>``) 만 남고 raw 값은 timers JSON 어디에도 등장하면 안 된다.
    rA   r   r5   r6   u   task-2533 진행. token=z key=z	 session=r   rB   r   r   NrZ   r\   r]   rM   rP   rQ   not in)z%(py0)s not in %(py2)sRAW_GHP_TOKENpersisted_textr   u6   raw GHP token 이 timers JSON 에 있으면 안 된다r   rc   RAW_HEX_KEYu4   raw hex key 가 timers JSON 에 있으면 안 된다RAW_UUIDu1   raw UUID 가 timers JSON 에 있으면 안 된다descriptiondesczassert %(py4)s)<=)z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} <= %(py5)srT   r   )rN   rV   rO   rQ   assert %(py7)sr   z<redacted-token>rq   )z%(py3)s in %(py5)s	sanitized)rO   rQ   r   )<)zT%(py12)s
{%(py12)s = %(py9)s(%(py10)s)
} < %(py17)s
{%(py17)s = %(py14)s(%(py15)s)
}r'   )r   r   r   r   r   r   z%(py19)spy19rR   zassert %(py22)spy22)r8   r   r   r   r"   r.   r	   rl   rt   ru   rv   rw   rx   ry   rz   r{   r~   r|   rT   r   r   r   r   )r;   r?   r   r'   r   rI   r]   r   r   r   r   r   r   r   r   r   r   r   r   r   @py_assert16@py_assert13r   @py_format20@py_format21@py_format23s                             r   7test_2533_no_raw_token_or_uuid_in_persisted_descriptionr     s    
>	)BMM-'M2 #=/{m9XJW  *,7IAv-) !KAq% 5555!++W+=N .hhh=hhhhhh=hhh=hhhhhhhhhhhhh0hhhhhhhn,ddd;ndddddd;ddd;ddddddndddndddd.ddddddd>)^^^8>^^^^^^8^^^8^^^^^^>^^^>^^^^+^^^^^^^ D$$$$=$$$$$$=$$$=$$$$$$$$$$$$$$$$d"""";d"""""";""";""""""d"""d"""""""4848844t9+9+++++9+++++++3+++3++++++t+++t+++9++++++++++++++++++ %V,IJJ*Jc)nJs6{Jn{.JJJJJJJJJJJJJJJJJJJJJJJJJn{JJJJJJcJJJcJJJJJJ)JJJ)JJJnJJJJJJsJJJsJJJJJJ6JJJ6JJJ{JJJJJJJJJJJJJJ	))))=	))))))=)))=))))))	)))	)))))))i'''';i'''''';''';''''''i'''i'''''''9$$$$89$$$$$$8$$$8$$$$$$9$$$9$$$$$$$r   c                H	   |dz  }|j                  dd       t        dt        d            \  }}t        dd	t        | ||
      \  }}}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   }
|
t        k(  }|st        j                  d|fd|
t        f      t        j                  |
      dt        j                         v st        j                  t              rt        j                  t              nddz  }dd|iz  }	t        t        j                  |	            dx}
}t        dt        d            \  }}t        dd	t        | ||
      \  }}}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   }
|
t        k(  }|st        j                  d|fd|
t        f      t        j                  |
      dt        j                         v st        j                  t              rt        j                  t              nddz  }dd|iz  }	t        t        j                  |	            dx}
}t        j                  | j!                  d            }|t"           d   }|d   }
|
t        k(  }|st        j                  d|fd|
t        f      t        j                  |
      dt        j                         v st        j                  t              rt        j                  t              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}}y) u   chat=6937032012 entry 와 다른 chat entry 가 같은 task_id 라도 chat_id 가 명시되어
    구분 가능해야 한다 (현재 schema 는 task_id 단일 key — 본 테스트는 chat_id 가 entry 내부에
    명시 저장됨을 검증).rA   r   r5   r6   r   CHAIR_SCHEDrB   r   u   task-2533 진행 dev1-teamr   NrZ   r\   entry_chairrM   rP   rQ   rk   rJ   rh   rl   rj   OTHER_SCHEDentry_other
OTHER_CHATr`   r-   ra   rb   rd   rW   )r8   r"   r.   r	   rl   rt   ru   rv   rw   rx   ry   rz   r{   r   r+   r}   r~   r   )r;   r?   r   r   rI   r   r   r   r   r   r   r   r   r   r   r   r   s                    r   .test_2533_chat_isolation_and_chat_id_persistedr     s    
>	)BMM-'M2 *]*CDIAv3)+ !Aq+ #";d"""";d"""""";""";"""d"""""""y!/!Z////!Z///!//////Z///Z/////// *]*CDIAv3)+ !Aq+ #";d"""";d"""""";""";"""d"""""""y!/!Z////!Z///!//////Z///Z///////

<1171CDIo&{3E)z))))z)))))))))z)))z)))))))0=0=0000=000000=0000000r   c                 8   d} t        |       }d}||k(  }|st        j                  d|fd||f      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}}d
} t        |       }d}||k(  }|st        j                  d|fd||f      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}}d} t        |       }d	}||u }|st        j                  d|fd||f      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}}d} t        |       }d	}||u }|st        j                  d|fd||f      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}}y	)uF   task_id 추출 — 정상 / 누락 / 합성 task (task-NNNN+M) 모두.u   task-2533 진행r`   rJ   )z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)sr   rN   r   rc   r   assert %(py9)sr   Nzfoo bar task-100+1 bazz
task-100+1zno task herero   z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} is %(py7)s )	r   rt   ru   rv   rw   rx   ry   rz   r{   )r   r   r   r   r   @py_format10s         r   .test_2533_extract_task_id_from_prompt_variantsr     s   '9I&'9:IkI:kIIII:kIIIIII&III&III'9III:IIIkIIIIIII'?P&'?@PLP@LPPPP@LPPPPPP&PPP&PPP'?PPP@PPPLPPPPPPP'5>&~6>$>6$>>>>6$>>>>>>&>>>&>>>~>>>6>>>$>>>>>>>')2&r*2d2*d2222*d222222&222&222r222*222d2222222r   c            
     V   d} t        |       }t        |      }d}||k(  }|s&t        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                  t               rt        j                  t               ndt        j                  |       t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x} x}x}x}}d
}t        |      }d	}||u }	|	st        j                  d|	fd||f      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}	}d}t        |      }d	}||u }	|	st        j                  d|	fd||f      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}	}t        j                  } ddd} | |      }t        |      }d	}||u }|s:t        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                  t              rt        j                  t              nd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}}t        j                  } ddi} | |      }t        |      }d	}||u }|s:t        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                  t              rt        j                  t              nd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}}dt        d      z   }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}}	y	)"uV   parse_schedule_id_from_cokacdir_stdout — JSON 노이즈 / 손상 / 실패 케이스.SID1rJ   )zO%(py7)s
{%(py7)s = %(py0)s(%(py5)s
{%(py5)s = %(py1)s(%(py3)s)
})
} == %(py10)sr   r.   )rN   rV   rO   rQ   r   r   zassert %(py12)sr   Nr   ro   r   r   r   r   znot jsonerrorx)r%   r&   )zj%(py9)s
{%(py9)s = %(py0)s(%(py7)s
{%(py7)s = %(py3)s
{%(py3)s = %(py1)s.dumps
}(%(py5)s)
})
} is %(py12)sr+   )rN   rV   rO   rQ   r   r   r   zassert %(py14)sr   r%   r$   z[info] starting...

WITH_NOISErS   noisyrU   r   rY   )r.   r   rt   ru   rv   rw   rx   ry   rz   r{   r+   r,   )r   r   r   r   @py_assert8r   r   r   r   r   r   r   r   r   r   r  r   r   s                     r   5test_2533_parse_schedule_id_handles_noise_and_invalidr    s$    >DO*V2DO12DEOOEOOOOEOOOOOO1OOO1OOOOOO*OOO*OOOVOOO2DOOOEOOOOOOOOOO24=1"5==5====5======1===1==="===5==========2<E1*=EE=EEEE=EEEEEE1EEE1EEE*EEE=EEEEEEEEEE 	

%S11212 	 	    	 v   2  I 2  v   	 I 	 I 	 I 2 I 	3 I  I      
 	

d##$1$ 	 	    	 v   2  I 2  v   	 I 	 I 	 I $ I 	% I  I       #Z%==E1%8HLH8LHHHH8LHHHHHH1HHH1HHHHHH%HHH%HHH8HHHLHHHHHHHr   c                    t         j                  } d}| |k(  }|st        j                  d|fd| |f      dt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |       t        j                  |      dz  }dd|iz  }t        t        j                  |            dx} x}}t         j                  } | j                  }d	}||k(  }|st        j                  d|fd
||f      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}}y)u   DEFAULT_TIMERS_PATH 가 worktree memory/task-timers.json 으로 결정되어야 한다.

    production 환경 정합 — task-2528 / task-2470 entry 가 같은 파일에 있어야 신호등이
    한 단일 source 를 본다.
    r0   rJ   )z,%(py2)s
{%(py2)s = %(py0)s.name
} == %(py5)sr
   )rN   r   rQ   r   r   Nmemory)zH%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.parent
}.name
} == %(py7)sr   r   r   )r
   namert   ru   rv   rw   rx   ry   rz   r{   parent)r   r   r   r   r   r   r   r   s           r   .test_2533_default_timers_path_points_to_memoryr	  	  s    ##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9999999%%6%**6h6*h6666*h666666666666%666*666h6666666r   c                    t         dz  dz  } | j                         st        j                  d       | 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  }t        j                  d      dz   d|iz  }t        t        j                  |            dx}}y)u   본 PR 시점 production timers JSON 에 raw GHP/UUID 패턴 0 — task-2533 hook 이
    description sanitize 를 적용한 결과로 새 entry 를 박을 때 누수 0임을 회귀로 박제.r  r0   z)production timers not present in worktreer5   r6   ghp_r   )z%(py1)s not in %(py3)stextrj   u:   GHP PAT prefix 가 timers JSON 에 등장하면 안 된다r^   rQ   Nghs_u:   GHS PAT prefix 가 timers JSON 에 등장하면 안 된다ghu_u:   GHU PAT prefix 가 timers JSON 에 등장하면 안 된다)_WORKTREE_ROOTexistspytestskipr~   rt   ru   ry   rv   rw   rx   r|   rz   r{   )timersr  r   r   r   r   s         r   7test_2533_production_timers_json_no_raw_secrets_residuer    sn    h&);;F==??@W-D [6[[[6[[[6[[[[[[[[[[[[[[[[[[[[[6[[[6[[[6[[[[[[[[[[[[[[[[[[[[[6[[[6[[[6[[[[[[[[[[[[[[[[[[[[r   )r   intr   strr    z$'Tuple[List[Sequence[str]], object]')r-   r  r    r  )r9   r   r    r   )r;   r   r?   r   r    None)r;   r   r?   r   r   r  r    r  )r;   r   r?   r   r9   r   r    r  )r9   r   r    r  )r    r  )>__doc__
__future__r   builtinsrv   _pytest.assertion.rewrite	assertionrewritert   r+   syspathlibr   typingr   r   r   r  __file__resolver  r  r  pathremoveinsertscripts.safe_cron_dispatchr   r	   utils.cron_timers_upsertr
   r   r   r   r   r   rl   r   ri   r   r   r   r"   r.   fixturer;   r?   r   r   markparametrizer   r   r   r   r   r   r   r   r  r	  r  r!   r   r   <module>r+     sC  0 #    
  ( ( 
 h'')0077>>~#(("HHOOC'( 3~& '  

 1: #. 
    '/'/)-'/	'/\/4/4)-/4	/4l 555 5 
	55Fhh)-h	h0>>)->	>83A3A)-3A9=3A	3AlC4,%,%)-,%	,%f)1)1)-)1	)1d3I*7\r   