
    j\?                       d Z ddlmZ ddlZddlZddlZddlZddlmZ dZ	dZ
dZdZd	Zd
ZdZdZej"                  j%                  ed      ZdZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 d	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZddZddZddZd dZd!dZd"dZd#dZd$dZe dk(  r e        yy)%u  v3.6 Runtime Harness — Layer 3: Watchdog Suppression Gate.

chair_authorization_id=CHAIR-AUTH-TASK-2704-V36-CONTROL-PLANE-P0-MVP-260528

Contract:
- evaluate_alert(task_id, hb_age, ev_age, retry_count, max_retry,
                 has_progress_marker, ...) -> dict
  Returns {"verdict": <6-state enum>, "chat_allowed": bool,
           "alive_signals": [...], "reason": str}

- 6 states: QUIET, WATCH, ALERT_INFO, ALERT_WARN, ESCALATE_CHAIR, SUPPRESSED

- Alive OR-7 signals (any 1 true → suppress chat):
  1. dispatch marker exists
  2. closeout marker exists
  3. escalate marker exists
  4. spawn state >= WORK_STARTED
  5. bot session process active
  6. worktree mtime < 900s
  7. recent artifact mtime < 900s

- hb_age == -1 AND ev_age == -1 ALONE must NOT escalate to chat.
- chat_allowed = True ONLY when state is ESCALATE_CHAIR.
- ESCALATE_CHAIR: silently_stalled 2 cycle consecutive AND (Critical 7 OR explicit chair escalate).
- .escalate / .escalate.acked marker → immediate SUPPRESSED (task-2405 fix#A preserved).

CLI usage:
    python -m scripts.harness.v36.watchdog_suppression_gate \
        --task-id task-2703 --hb-age 900 --ev-age 900 [options]
    )annotationsN)Optionalz4CHAIR-AUTH-TASK-2704-V36-CONTROL-PLANE-P0-MVP-260528QUIETWATCH
ALERT_INFO
ALERT_WARNESCALATE_CHAIR
SUPPRESSEDz/home/jay/workspacezmemory/eventsi  c                    	 t        | |||||||||	xs t        |
||||||d      S # t        $ r}t        dg d| dcY d}~S d}~ww xY w)ul  Evaluate whether a watchdog alert should be sent.

    Args:
        task_id: Task identifier.
        hb_age: Seconds since last heartbeat (-1 if unmeasured).
        ev_age: Seconds since last events file modification (-1 if unmeasured).
        retry_count: Current retry count for this task.
        max_retry: Maximum allowed retry count.
        has_progress_marker: True if a named progress marker exists.
        consecutive_stalled_cycles: Number of consecutive cycles with stalled verdict.
        is_critical_7: True if task is classified as Critical-7.
        explicit_chair_escalate: True if an explicit chair escalate flag is set.
        events_dir: Override events directory path.
        _override_*: For testing — override individual alive signal checks.

    Returns:
        {
            "verdict": str,         # 6-state enum
            "chat_allowed": bool,   # True only when verdict == ESCALATE_CHAIR
            "alive_signals": list,  # list of signal names that are True
            "reason": str,          # human-readable reason
        }

    Never raises. Returns safe defaults on any error.
    )dispatch_markercloseout_markerescalate_markerspawn_ge_work_startedbot_session_activeworktree_mtime_lt_900artifact_mtime_lt_900)task_idhb_ageev_ageretry_count	max_retryhas_progress_markerconsecutive_stalled_cyclesis_critical_7explicit_chair_escalate
events_dir	overridesFzevaluate_alert safe-fail: verdictchat_allowedalive_signalsreasonN)_evaluate_alert_impl_EVENTS_DIR	Exceptionr   )r   r   r   r   r   r   r   r   r   r   _override_dispatch_marker_override_closeout_marker_override_escalate_marker_override_spawn_ge_work_started_override_bot_session_active_override_worktree_mtime_lt_900_override_artifact_mtime_lt_900excs                     D/home/jay/workspace/scripts/harness/v36/watchdog_suppression_gate.pyevaluate_alertr/   8   s    Z
## 3'A'$;!0[#<#<#<)H&B)H)H
 	
*  
!23%8	
 	

s   %( 	A	AA	A	c                J   t        j                          }t        | |	|
      }|rt        t        dgd      S g }t	        | |	|
      }|r|j                  d       t        | |	|
      }|r|j                  d       t        | |
      }|r|j                  d       t        |
      }|r|j                  d       t        | |
|      }|r|j                  d       t        | |	|
|      }|r|j                  d       |r|j                  d	       t        |      d
kD  }|dk(  xr
 |dk(  xr | }|dk(  xs	 |t        k\  }|dk(  xs	 |t        k\  }|rd}n| xr |xr |}|r9|dk\  r4|s|rt        t        |d| d| d| d      S t        t        |d| d      S |r|dk(  rt        t        |d      S |r7|d
kD  xr |t        dz  kD  }|rt        t         |d      S t        t"        |d      S |dk(  r|dk(  rt        t         |d      S |s|rt        t         |d      S t        t"        |d      S )Nr   uR   escalate or escalate.acked marker present — immediate suppress (task-2405 fix#A)r   r   r   r   r   r   progress_markerr   F   zsilently_stalled z! consecutive cycles + critical_7=z explicit_chair_escalate=u-    — ESCALATE_CHAIR: chat sendMessage ALLOWEDu:    cycles but not critical_7/explicit — ALERT_WARN, chat=0   u8   silently_stalled cycle 1 — ALERT_INFO log only, chat=0u3   alive signals present but ev_age elevated — WATCHu   alive signals present — QUIETuX   hb_age=-1 and ev_age=-1 alone — insufficient evidence, cannot assert stalled — WATCHu4   partial stale signals — WATCH, awaiting next cycleu   no stall signals — QUIET)time_check_escalate_marker_verdictr
   _check_dispatch_markerappend_check_closeout_marker_check_spawn_ge_work_started_check_bot_session_active_check_worktree_fresh_check_artifact_freshlen_ALIVE_THRESHOLD_SECONDSr	   r   r   r   r   )r   r   r   r   r   r   r   r   r   r   r   nowescalate_marker_existsr!   sig1sig2sig4sig5sig6sig7is_aliveonly_negative_ageshb_staleev_stalesilently_stalledev_elevateds                             r.   r#   r#      s    ))+C 4GZS`
 	
 M "':yAD./ "':yAD./ (;D45 %Y/D12 !)S9D45 !*iED45 ./=!A%H !B,H6R<HL
 |Av)AAH|Av)AAH '<AHA 6!;3'(B'C D""/0IJaIb c??	  #$>#??yz  6!;F
 	
 qjKV.F!.K%KE=2ghh}.OPP |"f
 	
 8}.deeE=*FGG    c                    | | t         k(  ||dS )Nr   )r	   )stater!   r"   s      r.   r7   r7   
  s    /&	 rO   c                    |j                  d      |d   S 	 t        j                  j                  ||  d      }t	        t        j
                  |            dkD  S # t        $ r Y yw xY w)Nr   z
.escalate*r   Fgetospathjoinr?   globr%   r   r   r   patterns       r.   r6   r6     si    }}&'3*++'',,zgYj+AB499W%&**    AA 	A('A(c                    |j                  d      |d   S 	 t        j                  j                  ||  d      }t	        t        j
                  |            dkD  S # t        $ r Y yw xY w)Nr   z.dispatched*r   FrS   rY   s       r.   r8   r8     si    }}&'3*++'',,zgYl+CD499W%&** r[   c                   |j                  d      |d   S 	 t        j                  j                  ||  d      t        j                  j                  ||  d      t        j                  j                  ||  d      t        j                  j                  ||  d      g}|D ]  }t	        j                  |      s y y# t
        $ r Y yw xY w)Nr   z.harness-mvp-active*z.*active.jsonz.done*z.completion.txtTF)rT   rU   rV   rW   rX   r%   )r   r   r   patternspats        r.   r:   r:   )  s    }}&'3*++GGLLy0D%EFGGLLy%>?GGLLy%78GGLLy%@A	
  	Cyy~	  s   B$C =C ?C 	CCc                    |j                  d      |d   S 	 ddlm}m}m}  ||       } ||d   |      S # t
        $ r Y yw xY w)Nr   r   )detect_spawn_statestate_geWORK_STARTEDrQ   F)rT   "scripts.harness.v36.spawn_detectorra   rb   rc   r%   )r   r   ra   rb   rc   results         r.   r;   r;   ;  sV    }},-9011aa#G,w66    6 	AAc                    | j                  d      | d   S 	 ddlm}m}  |       } |       D ]  }|s||k7  s y y# t        $ r Y yw xY w)Nr   r   )_list_claude_processes_get_anu_session_hexTF)rT   rd   rh   ri   r%   )r   rh   ri   anu_hexproc_hexs        r.   r<   r<   F  sd    }})*6-..c&(.0 	HH/	  s   > > > > 	A
	A
c                    |j                  d      |d   S 	 ddlm}  || |      }|d uxr	 |t        k  S # t        $ r Y yw xY w)Nr   r   )_get_worktree_mtime_secondsF)rT   rd   rm   r@   r%   )r   r   rA   rm   mtime_ss        r.   r=   r=   T  sZ    }},-9011R-gs;d"Iw1I'II rf   c                    |j                  d      |d   S 	 ddlm}  || ||      }|d uxr	 |t        k  S # t        $ r Y yw xY w)Nr   r   )_get_artifact_mtime_secondsF)rT   rd   rp   r@   r%   )r   r   r   rA   rp   rn   s         r.   r>   r>   _  s\    }},-9011R-gz3Gd"Iw1I'II s   7 	AAc                 >   dd l } dd l}| j                  d      }|j                  ddd       |j                  dt        dd	
       |j                  dt        dd
       |j                  dt        d       |j                  dt        d       |j                  ddd       |j                  dt        d       |j                  ddd       |j                  ddd       |j                  dd        |j                         }t        |j                  |j                  |j                  |j                  |j                  |j                  |j                  |j                  |j                  |j                   
      }t#        |j%                  |dd             y )Nr   u:   Evaluate watchdog suppression gate — prints JSON verdict)descriptionz	--task-idTzTask ID)requiredhelpz--hb-agez(Heartbeat age in seconds (-1 if unknown))typers   rt   z--ev-agez*Events file age in seconds (-1 if unknown)z--retry-count)ru   defaultz--max-retryr3   z--has-progress-marker
store_trueF)actionrv   z--consecutive-stalled-cyclesz--is-critical-7z--explicit-chair-escalatez--events-dir)rv   )
r   r   r   r   r   r   r   r   r   r   )ensure_asciiindent)argparsejsonArgumentParseradd_argumentint
parse_argsr/   r   r   r   r   r   r   r   r   r   r   printdumps)r{   _jsonparserargsre   s        r.   	_cli_mainr   l  s~   $$P % F dC

tBlm

tBno
c1=
C;
/eT
6S!L
),N
3LRWX
5D{{{{$$.. 44#'#B#B(( $ < <??F 
%++f5+
;<rO   __main__)r   r3   Fr   FFNNNNNNNN)$r   strr   r   r   r   r   r   r   r   r   boolr   r   r   r   r   r   r   zOptional[str]r&   Optional[bool]r'   r   r(   r   r)   r   r*   r   r+   r   r,   r   returndict)r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )rQ   r   r!   listr"   r   r   r   )r   r   r   r   r   r   r   r   )r   r   r   r   r   r   )r   r   r   r   )r   r   r   r   rA   floatr   r   )
r   r   r   r   r   r   rA   r   r   r   )r   None)!__doc__
__future__r   rX   rU   sysr5   typingr   CHAIR_AUTHORIZATION_IDr   r   r   r   r	   r
   
_WORKSPACErV   rW   r$   r@   r/   r#   r7   r6   r8   r:   r;   r<   r=   r>   r   __name__ rO   r.   <module>r      s.  < #  	 
  O  	

!
"
ggll:7   %&'$) $0404046:376:6:%I
I
I
 I
 	I

 I
 I
 !$I
 I
 "I
 I
  .I
  .I
  .I
 &4I
  #1!I
" &4#I
$ &4%I
& 
'I
\AHAHAH AH 	AH
 AH AH !$AH AH "AH AH AH 
AHH$=D zK rO   