
    + jb9                       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ZddlZddlZddlmZ ddlZ e ee      j'                         j(                  d         Zeej,                  vrej,                  j/                  de       ddlmZmZmZmZmZmZmZ ej@                  d        Z!ej@                  d        Z"ej@                  d	        Z#dd
Z$ddZ%	 	 d	 	 	 	 	 	 	 	 	 	 	 	 	 ddZ&dddZ'd Z(d Z)d Z*d Z+d Z,d Z-d Z.d Z/d Z0d Z1y)u  task-2534 회귀 테스트: data_loader 자동머지 시그널 병합 (traffic_light_fix_b)

회장 §결정(2026-05-10) 신호등 sync fix B — dashboard/data_loader.py가 task-timers.json만이
아니라 cron schedule_history + events/*.merge-queue.json 시그널을 병합해 활성/유휴 판정.

검증 포인트 (회귀 8건):
    1. timers running → 작업중 (heartbeat fresh + PID alive)
    2. cron 발사 후 timers stale → 작업중 (cron 시그널이 idle 덮어씀)
    3. merge-queue HEAD 있음 → 작업중
    4. 모든 시그널 부재 → 유휴
    5. heartbeat 가장 최근 source 우선 (cron > mq > heartbeat priority 검증)
    6. chat=6937032012 격리 (다른 chat schedule_history 무시)
    7. token raw 0 (prompt/response/schedule_id 절대 결과 dict에 노출 X)
    8. task-2528+1 / task-2530 / task-2531 fixture 회귀 (회장 발견 사례 3건 박제)
    )annotationsN)Path   )DASHBOARD_CHAT_IDHEARTBEAT_FRESH_SECONDSSIGNAL_FRESHNESS_SECONDS_PidLivenessProvider_collect_cron_schedule_signals_collect_merge_queue_signals_compute_member_statusc               #  x   K   t        j                         5 } t        |        d d d        y # 1 sw Y   y xY wwNtempfileTemporaryDirectoryr   tmps    I/home/jay/workspace/tests/dashboard/test_data_loader_signal_merge_2534.pytmp_heartbeat_dirr   .   1     		$	$	& #3i     :.	:7:c               #  x   K   t        j                         5 } t        |        d d d        y # 1 sw Y   y xY wwr   r   r   s    r   tmp_schedule_history_dirr   4   r   r   c               #  x   K   t        j                         5 } t        |        d d d        y # 1 sw Y   y xY wwr   r   r   s    r   tmp_events_dirr   :   r   r   c                0    | |z  }|j                          |S r   )touch)dir_namefs      r   _touch_freshr!   @   s    tAGGIH    c                    | |z  }|j                          t        j                         |z
  }t        j                  |||f       |S r   )r   timeosutime)r   r   age_secr    pasts        r   _touch_with_ager)   F   s;    tAGGI99; DHHQtHr"   c           	         | | dz  }|||d|dddd}|j                  t        j                  |d      d	z   d
       |dkD  r/t        j                         |z
  }t	        j
                  |||f       |S )u-   schedule_history JSONL 1라인 파일 생성.z.logz
(redacted)z2026-05-10T03:00:00.000+09:00oki  )schedule_idchat_idpromptresponseworkspace_pathtsstatusduration_msFensure_ascii
utf-8encodingr   
write_textjsondumpsr$   r%   r&   )	r   r,   r-   r.   r0   r'   r    recr(   s	            r   _write_schedule_logr?   N   s     	+d##A" (-	C LLCe4t;gLN{yy{W$
T4L!Hr"   c                    | | dz  }d|ddd}|j                  t        j                  |d      d	       |d
kD  r/t        j                         |z
  }t	        j
                  |||f       |S )Nz.merge-queue.jsonWAITING_FOR_PREDECESSORc   z2026-05-10T03:10:26Z)decisiontask_id	pr_number	timestampFr4   r7   r8   r   r:   )r   rD   r'   r    payloadr(   s         r   _write_merge_queuerH   i   sr    '+,,A-+	G LLG%87LK{yy{W$
T4L!Hr"   c                   t        | d       t               }|j                  ddi       t        dddd| |i i       }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}}y)uP   timers running + heartbeat fresh + PID alive → working (기존 경로 회귀).ztask-A.heartbeatzsched-ATztask-Arunningr2   r,   cron_signalsmerge_queue_signalsworking==z%(py0)s == %(py3)sresultpy0py3assert %(py5)spy5N)r!   r	   set_overrider   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationr   prS   @py_assert2@py_assert1@py_format4@py_format6s          r   2test_signal_merge_1_timers_running_returns_workingrh   {   s    "$67ANNIt$%#Y7	F 6Y6Y66Yr"   c                   t               }|j                  i        dt        j                         dz
  i}t        dddi| ||i       }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}}y)u   timers status=completed(또는 hb stale)이지만 cron 시그널 fresh → 작업중.

    회장 발견 사례 핵심 — cron --session으로 봇이 실 작업 중이지만 timers는 'idle'.
    task-2528+1      N@r2   	completedrL   rO   rP   rR   rS   rT   rW   rX   Nr	   rY   r$   r   rZ   r[   r\   r]   r^   r_   r`   ra   )r   rc   rM   rS   rd   re   rf   rg   s           r   /test_signal_merge_2_cron_overrides_stale_timersrn      s    
 	ANN2!499;#56L#	;	!F 6Y6Y66Yr"   c                   t               }|j                  i        dt        j                         dz
  i}t        di | |i |      }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}}y)u?   merge-queue HEAD에 fresh 진입 → 작업중 (timers 무관).	task-2530      >@rL   rO   rP   rR   rS   rT   rW   rX   Nrm   )r   rc   
mq_signalsrS   rd   re   rf   rg   s           r   4test_signal_merge_3_merge_queue_head_returns_workingrs      s    ANN2tyy{T12J#
	&F 6Y6Y66Yr"   c                   t               }|j                  i        t        dddd| |i i       }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}}y)uC   cron 부재 + merge-queue 부재 + timers stale/completed → idle.ztask-XYZrl   nonerK   rL   idlerP   rR   rS   rT   rW   rX   N)r	   rY   r   rZ   r[   r\   r]   r^   r_   r`   ra   rb   s          r   +test_signal_merge_4_no_signals_returns_idlerw      s    ANN2#v6	F 6V6V66Vr"   c           
     b   t               }|j                  ddi       t        | d       t        dddd| |dt	        j                         t
        z
  dz
  idt	        j                         t
        z
  dz
  i      }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t        dz          t        dddd| |dt	        j                         dz
  idt	        j                         t
        z
  dz
  i      }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t        dz          t        dddd| |dt	        j                         t
        z
  dz
  idt	        j                         dz
  i      }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t        dz          t        dddd| |dt	        j                         t
        z
  dz
  idt	        j                         t
        z
  dz
  i      }	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}}y) u4  priority: cron > merge-queue > timers heartbeat.

    검증 1: cron stale + mq stale + hb fresh+alive → working (heartbeat path)
    검증 2: cron fresh + 나머지 stale → working (cron path)
    검증 3: cron stale + mq fresh + hb stale → working (mq path)
    검증 4: 모두 stale → idle
    zsched-XTztask-X.heartbeatztask-XrJ   rK   <   rL   rO   rP   rR   result1rT   rW   rX   Nztask-Y.heartbeatztask-Yzsched-Yrq   result2ztask-Z.heartbeatztask-Zzsched-Zrk   result3ztask-W.heartbeatztask-Wzsched-Wrv   result4)r	   rY   r!   r   r$   r   rZ   r[   r\   r]   r^   r_   r`   ra   r)   r   )
r   rc   rz   rd   re   rf   rg   r{   r|   r}   s
             r   8test_signal_merge_5_priority_cron_over_mq_over_heartbeatr~      s    	ANNIt$% "$67$Y7			.F F KL%tyy{5M'MPR'RSG  7i7i77i %'9;RUW;WX$Y7			d 23%tyy{5M'MPR'RSG  7i7i77i %'9;RUW;WX$Y7			.F F KL%tyy{T'9:G  7i7i77i %'9;RUW;WX$Y7			.F F KL%tyy{5M'MPR'RSG 7f7f77fr"   c                   t        | dt        d       t        | ddd       t        | t              }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~   다른 chat의 schedule_history 라인은 결과에 포함 X.

    회장 §명시: chat=6937032012 외 라인은 무시.
    ABCD0001u   task-100 진행r-   r.   ABCD0002l   c(	 u   task-200 진행r-   ztask-100inz%(py1)s in %(py3)ssignalspy1rV   rW   rX   Nztask-200not inz%(py1)s not in %(py3)s)r?   r   r
   rZ   r[   r_   r\   r]   r^   r`   ra   )r   r   @py_assert0rd   rf   rg   s         r   "test_signal_merge_6_chat_isolationr     s      ! 	   	 - !G  :    :   :                $:W$$$$:W$$$:$$$$$$W$$$W$$$$$$$r"   c                   t        | dt        dd       t        | t              }d |j                         D        }t	        |      }|sddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      d	z  }t        t        j                  |            d
x}}t        j                  |      }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}}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
)uW   반환 dict에는 task_id/mtime만 — prompt/response/schedule_id raw 절대 노출 X.FF00FF00u$   task-300 비밀 토큰 ABC123 포함z&/home/jay/.cokacdir/workspace/FF00FF00r-   r.   r0   r   c              3  <   K   | ]  }t        |t                y wr   )
isinstancefloat).0vs     r   	<genexpr>z5test_signal_merge_7_token_raw_zero.<locals>.<genexpr>6  s     >z!U#>s   z,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}all)rU   py2py4NABC123r   r   
serializedr   rW   rX   u   비밀 토큰)r?   r   r
   valuesr   r\   r]   rZ   r^   r_   r`   ra   r<   r=   r[   )
r   r   re   @py_assert3@py_format5r   r   rd   rf   rg   s
             r   "test_signal_merge_7_token_raw_zeror   '  s    !5? - !G
 ?W^^-=>>3>>>>>>>>>3>>>3>>>>>>>>>>>>>>G$J%8:%%%%8:%%%8%%%%%%:%%%:%%%%%%%':Z'''':Z''':''''''Z'''Z''''''',?*,,,,?*,,,?,,,,,,*,,,*,,,,,,,r"   c                   t        |dt        dd       t        |d       t        |t              }t	        |      }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}}t               }	|	j                  i        t        di | |	||      }
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}}y)u   회장 §발견 fixture: task-2528+1 dev1 — cron+merge-queue 모두 fresh, timers entry 0.

    audit fix B 본질 — task-timers는 entry 0이지만 cron schedule_history와
    merge-queue.json이 모두 살아있음 → 'working' 판정 보장.
    B9792045z&[task-2528+1] worktree timer reconcilez-/home/jay/workspace/.worktrees/task-2528-dev1r   rj   r   r   r   rM   r   rW   rX   Nrr   rL   rO   rP   rR   rS   rT   )r?   r   rH   r
   r   rZ   r[   r_   r\   r]   r^   r`   ra   r	   rY   r   r   r   r   rM   rr   r   rd   rf   rg   rc   rS   re   s               r   0test_signal_merge_8_chairman_fixture_2528_plus_1r   A  s     !7F ~}51 !L .n=J(=L((((=L(((=((((((L(((L(((((((&=J&&&&=J&&&=&&&&&&J&&&J&&&&&&&ANN2#
	!&F 6Y6Y66Yr"   c                   t        |dt        dd       t        |t              }t        |      }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}}t               }	|	j                  i        t        dddi| |	||      }
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}}y)u   회장 §발견 fixture: task-2530 dev7 — cron 발사 26m43s 진행, timers idle 표시.

    audit fix B 본질 — cron 시그널만으로도 working 판정.
    5AFBFD82uA   [task-2530] composite ③ cross-cutting agent 페르소나 합성z-/home/jay/workspace/.worktrees/task-2530-dev7r   r   rp   r   r   rM   r   rW   rX   Nr   r   rr   r2   rl   rL   rO   rP   rR   rS   rT   r?   r   r
   r   rZ   r[   r_   r\   r]   r^   r`   ra   r	   rY   r   r   s               r   )test_signal_merge_8_chairman_fixture_2530r   h  s{     !RF 2 !L .n=J&;,&&&&;,&&&;&&&&&&,&&&,&&&&&&&(;j((((;j(((;((((((j(((j(((((((ANN2#	;	!&F 6Y6Y66Yr"   c                   t        |dt        d       t        |t              }t        |      }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}}t               }	|	j                  i        t        dddi| |	||      }
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}}y)u   회장 §발견 fixture: task-2531 dev4 — 표시=유휴(cron 미발견).

    audit fix B 본질 — cron 미발견이면 적법하게 idle 판정 (over-trigger 금지).
    이 경계 케이스를 fixture로 박제해 회귀 보호.
    298A007Bu   [task-2999] 다른 작업r   r   z	task-2531r   r   rM   r   rW   rX   Nrr   r2   rl   rL   rv   rP   rR   rS   rT   r   r   s               r   )test_signal_merge_8_chairman_fixture_2531r     sx     !*	 2 !L .n=J*;l****;l***;******l***l*******(;j((((;j(((;((((((j(((j(((((((ANN2#	;	!&F 6V6V66Vr"   )r   r   r   strreturnr   )r   r   r   r   r'   r   r   r   )         )r   r   r,   r   r-   intr.   r   r0   r   r'   r   r   r   )r   )r   r   rD   r   r'   r   r   r   )2__doc__
__future__r   builtinsr\   _pytest.assertion.rewrite	assertionrewriterZ   r<   r%   sysr   r$   pathlibr   pytestr   __file__resolveparents_PROJECT_ROOTpathinsertdashboard.data_loaderr   r   r   r	   r
   r   r   fixturer   r   r   r!   r)   r?   rH   rh   rn   rs   rw   r~   r   r   r   r   r    r"   r   <module>r      sT    #    	 
     DN**,44Q78 HHOOA}%    
  
  
 
  	
   
6$&,&$9~%>-4$N#L#r"   