+
    jHàj~ž  ã                  ó
  a € 0 t $ R t^ RIHt ^ RIHtHtHt ^ RIH	t	H
t
HtHt ^ RIt^ RItRdtR R lt] ! R R4      4       t] ! R	 R
4      4       tReR ltR R ltR R ltR R ltReR R lltR R ltR R ltRR/R R lltR R ltR R ltR  R! ltR" R# ltR$ R% ltR& R' lt R( R) lt!]
]	]"]	,          ]"]	,          ]#]$.]"],          3,          t%R* R+ lt&R, R- lt'R. R/ lt(R0 R1 lt)R2 R3 lt*R4RfRRg/t+R5 R6 lt,R7 R8 lt-R9 R: lt.R;R<R=R>R?R@/t/RA RB lt0RC RD lt1RE RF lt2RG RH lt3RI RJ lt4RK RL lt5RM RN lt6RO RP lt7]*],]-]0]1]2]3]4]5]6]7.t8RQ]9RR&   RS^RT^RU^RV^RWRhRXRi/t:RY RZ lt;R[ R\ lt<R] R^ lt=R_RR`RRaR/Rb Rc llt>R# )juü  Kanban diagnostics â€” structured, actionable distress signals for tasks.

A ``Diagnostic`` carries a **kind** (canonical code the UI/tests match on), a
**severity**, title/detail text, and **actions** the dashboard renders as
buttons and the CLI as hints. Rules are stateless and read-only over
(task, events, runs, optional graph); callers compute on demand. Only
operator-fixable signals (not a one-off provider 502); every diagnostic has a
recovery action and auto-clears when the failure mode resolves.
)Úannotations)ÚasdictÚ	dataclassÚfield)ÚAnyÚCallableÚIterableÚOptionalNc               ó$   € V ^8„  d   QhRRRRRR/# )é   ÚseverityzOptional[str]Ú	thresholdÚreturnÚbool© )Úformats   "ÚB/home/ubuntu/.hermes/hermes-agent/hermes_cli/kanban_diagnostics.pyÚ__annotate__r      s&   € ÷ Mñ M =ð M¸]ð MÈtñ Mó    c                ó”   € Vf   R# V \         9  g   V\         9  d   R# \         P                  V 4      \         P                  V4      8¬  # )z=Return True when ``severity`` meets or exceeds ``threshold``.TF)ÚSEVERITY_ORDERÚindex)r   r   s   &&r   Úseverity_at_or_abover      s>   € àÒÙØ”~Ô%¨¼.Ô)HÙÜ×Ñ Ó)¬^×-AÑ-AÀ)Ó-LÑLÐLr   c                  óf   € ] tR t^ t$ RtR]R&   R]R&   ]! ]R7      tR]R&   Rt	R	]R
&   R R lt
RtR# )ÚDiagnosticActiona  A recovery action. ``kind`` drives rendering: ``reclaim``/``reassign``
POST to /tasks/:id/*; ``unblock`` PATCHes status to ready; ``cli_hint``
shows ``payload.command``; ``open_docs`` links ``payload.url``; ``comment``
nudges the operator. ``suggested=True`` = recommended first step.ÚstrÚkindÚlabel©Údefault_factoryÚdictÚpayloadFr   Ú	suggestedc               ó   € V ^8„  d   QhRR/# ©r   r   r    r   )r   s   "r   r   ÚDiagnosticAction.__annotate__,   ó   € ÷ ñ ˜ñ r   c                	ó   € \        V 4      # ©N©r   ©Úselfs   &r   Úto_dictÚDiagnosticAction.to_dict,   ó   € Ü�d‹|Ðr   r   N)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú__annotations__r   r    r!   r"   r,   Ú__static_attributes__r   r   r   r   r       s7   ‡ ñIð
 ƒIØƒJÙ¨$Ô/€GˆTÓ/Ø€IˆtÓ÷ñ r   r   c                  óÀ   € ] tR t^0t$ RtR]R&   R]R&   R]R&   R]R&   ]! ]R7      tR]R	&   ^ t	R
]R&   ^ t
R
]R&   ^tR
]R&   RtR]R&   ]! ]R7      tR]R&   R R ltRtR# )Ú
Diagnosticz%One active distress signal on a task.r   r   r   ÚtitleÚdetailr   úlist[DiagnosticAction]ÚactionsÚintÚfirst_seen_atÚlast_seen_atÚcountNúOptional[int]Úrun_idr    Údatac               ó   € V ^8„  d   QhRR/# r$   r   )r   s   "r   r   ÚDiagnostic.__annotate__?   r&   r   c                	ó   € \        V 4      # r(   r)   r*   s   &r   r,   ÚDiagnostic.to_dict?   r.   r   r   )r/   r0   r1   r2   r3   r4   r   Úlistr;   r=   r>   r?   rA   r    rB   r,   r5   r   r   r   r7   r7   0   si   ‡ á/à
ƒIØƒMØƒJØƒKÙ&+¸DÔ&A€GÐ#ÓAØ€M�3ÓØ€L�#ÓØ€Eˆ3ƒNØ €FˆMÓ Ù tÔ,€Dˆ$Ó,÷ñ r   r7   c                óô   € V f   V#  \        V R4      '       d   WP                  4       9   d	   W,          # \        T \        4      '       d   T P                  Y4      # \        YT4      #   \         d     L@i ; i)zGRead a field from a sqlite3.Row, a kanban_db.Task dataclass, or a dict.Úkeys)ÚhasattrrI   Ú	ExceptionÚ
isinstancer    ÚgetÚgetattr)ÚtaskÚnameÚdefaults   &&&r   Ú_task_fieldrR   E   sp   € à‚|ØˆðÜ�4˜× Ò  T¯Y©Y«[Ô%8Ø•:Ðô �$œ×ÒØ�x‰x˜Ó&Ð&Ü�4˜wÓ'Ð'øô	 ô Ùðús   ˆ-A) Á)A7Á6A7c               ó   € V ^8„  d   QhRR/# r$   r   )r   s   "r   r   r   S   s   € ÷ 
ñ 
˜$ñ 
r   c                óä   € \        V RR4      p\        V\        4      '       d   V# \        V\        4      '       d"    \        P
                  ! V4      ;'       g    / # / #   \         d    / u # i ; i)z<Tolerate event.payload being either a dict or a JSON string.r!   N)rR   rL   r    r   ÚjsonÚloadsrK   )ÚevÚps   & r   Ú_parse_payloadrY   S   sg   € ä�B˜	 4Ó(€AÜ�!”T×ÒØˆÜ�!”S×Òð	Ü—:’:˜a“=×&Ð& BÐ&ð €Iøô ô 	ØŠIð	ús   ½A ÁA ÁA/Á.A/c               ó   € V ^8„  d   QhRR/# ©r   r   r   r   )r   s   "r   r   r   `   s   € ÷ -ñ -�sñ -r   c                ó0   € \        V R R4      ;'       g    R# )r   Ú ©rR   ©rW   s   &r   Ú_event_kindr`   `   s   € Ü�r˜6 2Ó&×,Ð,¨"Ð,r   c               ó   € V ^8„  d   QhRR/# )r   r   r<   r   )r   s   "r   r   r   d   s   € ÷ 6ñ 6�Sñ 6r   c                óB   € \        \        V R ^ 4      ;'       g    ^ 4      # )Ú
created_at)r<   rR   r_   s   &r   Ú	_event_tsrd   d   s   € ÜŒ{˜2˜|¨QÓ/×4Ð4°1Ó5Ð5r   c               ó    € V ^8„  d   QhRRRR/# )r   Úprimaryr   Úlegacyr   )r   s   "r   r   r   h   s   € ÷ Fñ F ð F¨Sñ Fr   c                ó>   € \        WR4      pVe   V# \        WV4      # )zI``task[primary]`` unless it is None, else ``task[legacy]`` (old DB rows).Nr^   )rO   rf   rg   rQ   Úvs   &&&& r   Ú_first_fieldrj   h   s$   € ä�D 4Ó(€AØ’ˆ1ÐE¤;¨t¸WÓ#EÐEr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   ÚeventsúIterable[Any]Úkindszset[str]r   r<   r   )r   s   "r   r   r   n   s&   € ÷ Sñ S˜]ð S°8ð SÀñ Sr   c                ó4   a€ \        ^ .V3R lV  4       O4      # )zFMax ``created_at`` over events whose kind is in ``kinds`` (0 if none).c              3  ó^   <"  € T F"  p\        V4      S9   g   K  \        V4      x € K$  	  R # 5ir(   ©r`   rd   )Ú.0rW   rn   s   & €r   Ú	<genexpr>Ú#_latest_event_ts.<locals>.<genexpr>p   s$   øé € ÐP©V r´{À2³È%Ñ7O”]”Y˜r—]�]«Vùs   ƒ-š-)Úmax)rl   rn   s   &fr   Ú_latest_event_tsrv   n   s   ø€ ä�ÐQÔP©VÓPÐQÓRÐRr   c               ó    € V ^8„  d   QhRRRR/# )r   rl   rm   r   r   r   )r   s   "r   r   r   s   s   € ÷ 	ñ 	°ð 	À4ñ 	r   c                ó¾   € \        \        V 4      4       FD  p\        V4      pVR8X  d&   \        \	        V4      P                  R4      4      u # VR9   g   KC   R# 	  R# )z�True when the most recent breaker trip was a terminal provider error (credential
revoked, model gone) and nothing has resumed the task since.Úgave_upÚterminal_providerF>   ÚclaimedÚpromotedÚ	completedÚ	unblocked)ÚreversedrG   r`   r   rY   rM   )rl   rW   r   s   &  r   Ú$_latest_gave_up_is_terminal_providerr€   s   sU   € ô ”t˜F“|Ö$ˆÜ˜2‹ˆØ�9ÔÜœ rÓ*×.Ñ.Ð/BÓCÓDÒDØÐDÖDÚñ %ñ r   r"   Fc               ó(   € V ^8„  d   QhRRRRRRRR/# )r   r   r   Úcommandr"   r   r   r   r   )r   s   "r   r   r      s)   € ÷ 1ñ 1�Sð 1 3ð 1°dð 1ÐGWñ 1r   c               ó$   € \        R V RV/VR7      # )Úcli_hintr‚   )r   r   r!   r"   )r   )r   r‚   r"   s   &&$r   Ú	_cli_hintr…      s   € Ü °5À9ÈgÐBVØ&/ô1ð 1r   c               ó    € V ^8„  d   QhRRRR/# )r   Útask_idr   r   r   r   )r   s   "r   r   r   „   s   € ÷ @ñ @˜cð @Ð&6ñ @r   c                ó.   € R V  2p\        RV 2VRR7      # )zhermes kanban log zCheck logs: T©r"   )r…   )r‡   Úcmds   & r   Ú_log_hint_actionr‹   „   s$   € Ø˜w˜iÐ
(€CÜ�| C 5Ð)¨3¸$Ô?Ð?r   c               ó   € V ^8„  d   QhRR/# r[   r   )r   s   "r   r   r   ‰   s   € ÷ Wñ W ñ Wr   c                ó®   € V '       d   T ;'       g    RP                  4       MRpV'       d)   VR,          \        V4      R8”  d	   R,           # R,           # R# )z@First 500 chars of the error (with ellipsis), or "" when absent.r]   :Niô  Niô  u   â€¦)ÚstripÚlen)Úlast_errÚerr_texts   & r   Ú_error_snippetr’   ‰   sD   € ç+3�—�˜B×%Ñ%Ô'¸€HßFNˆ8�D�>¤c¨(£m°cÔ&9˜UÕBÐV¸rÕBÐVÐTVÐVr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   rl   rm   r   r   r   ú	list[Any]r   )r   s   "r   r   r   �   s!   € ÷ 
ñ 
¨ð 
¸cð 
Àiñ 
r   c                ó–   € . pV  F@  p\        V4      pVR9   d   VP                  4        K'  WA8X  g   K/  VP                  V4       KB  	  V# )z�Events of ``kind`` with no ``completed``/``edited`` event strictly after
them. Requires id-sorted (arrival-order) input, which the DB provides.>   Úeditedr}   )r`   ÚclearÚappend)rl   r   ÚactiverW   Úks   &&   r   Ú_active_hallucination_eventsr›   �   sF   € ð €FÛˆÜ˜‹OˆØÐ'Ô'Ø�L‰LŽNØŽYØ�M‰M˜"Öñ ð €Mr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úhitsr”   Úkeyr   r   z	list[str]r   )r   s   "r   r   r   œ   s!   € ÷ ñ ˜ið ¨cð °iñ r   c                ó¦   € . pV  FH  p\        V4      P                  V. 4      ;'       g    .  F  pWB9  g   K  VP                  V4       K  	  KJ  	  V# )z@Ordered, de-duplicated ``payload[key]`` entries across ``hits``.)rY   rM   r˜   )r�   rž   ÚoutrW   Úpids   &&   r   Ú_unique_payload_idsr¢   œ   sO   € à€CÛˆÜ! "Ó%×)Ñ)¨#¨rÓ2×8Ð8°bÒ8ˆCØŽ~Ø—
‘
˜3–ó 9ñ ð €Jr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   rO   r   Úrunningr   r   r:   r   )r   s   "r   r   r   ¦   s"   € ÷ ñ  Cð °Tð Ð>Tñ r   c               ó’   € . pV'       d   VP                  \        RR/ R7      4       VP                  \        RRRV/R7      4       V# )z?Baseline recovery primitives every diagnostic can fall back on.ÚreclaimzReclaim task©r   r   r!   ÚreassignzReassign to different profileÚreclaim_first)r˜   r   )rO   r¤   r    s   &$ r   Ú_generic_recovery_actionsrª   ¦   sJ   € à"$€CßØ�
‰
Ô#¨¸.ÐRTÔUÔVØ‡J�JÔØÐ>ÈÐZaÐHbôô ð €Jr   c               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r   s   "r   r   r   ±   s   € ÷ 4ñ 4˜ñ 4r   c                ó    € \        V R 4      R8H  # )Ústatusr¤   r^   )rO   s   &r   Ú_is_runningr®   ±   s   € Ü�t˜XÓ&¨)Ñ3Ð3r   c               ó   € V ^8„  d   QhRR/# )r   r   r”   r   )r   s   "r   r   r   µ   s   € ÷ Oñ O 	ñ Or   c           	     óB   € \        \        \        V R  R7      4      4      # )c                ó   € \        V R ^ 4      # )Úidr^   )Úrs   &r   Ú<lambda>Ú$_runs_newest_first.<locals>.<lambda>¸   s   € ´KÀÀ4ÈÔ4Kr   ©rž   )rG   r   Úsorted)Úrunss   &r   Ú_runs_newest_firstr¹   µ   s   € ô ”œ Ñ*KÔLÓMÓNÐNr   c               ó    € V ^8„  d   QhRRRR/# )r   Úslotr   r   r   r   )r   s   "r   r   r   Ã   s   € ÷ 	]ñ 	]˜Sð 	] Tñ 	]r   c                óJ  a € \        S \        4      '       g   R# \        S P                  R4      ;'       g    R4      P	                  4       P                  4       pV'       d
   VR8w  d   R# \        ;QJ d    V 3R lR 4       F  '       g   K   R# 	  R# ! V 3R lR 4       4      # )z™True if the aux slot was user-configured: provider other than "auto",
or any of model/base_url/api_key set (the default falls through to the
main model).FÚproviderr]   ÚautoTc              3  óˆ   <"  € T F7  p\        SP                  V4      ;'       g    R 4      P                  4       x € K9  	  R# 5i)r]   N)r   rM   rŽ   )rr   rž   r»   s   & €r   rs   Ú%_aux_slot_explicit.<locals>.<genexpr>Ì   s4   øé € Ð\Ñ;[°CŒs�4—8‘8˜C“=×&Ð& BÓ'×-Ñ-×/Ð/Ó;[ùs
   ƒ!A¥A)ÚmodelÚbase_urlÚapi_key)rL   r    r   rM   rŽ   ÚlowerÚany)r»   r½   s   f r   Ú_aux_slot_explicitrÆ   Ã   sz   ø€ ô �dœD×!Ò!ÙÜ�4—8‘8˜JÓ'×-Ð-¨2Ó.×4Ñ4Ó6×<Ñ<Ó>€Hß�H Ô&Ùß‹3Ô\Ñ;[Ó\�3Œ3Ð\Š3Ð\ˆ3Ô\Ñ;[Ó\Ó\Ð\r   c               ó    € V ^8„  d   QhRRRR/# )r   Ú
raw_configr   r   r   r   )r   s   "r   r   r   Ï   s   € ÷ .ñ . Cð .¨Dñ .r   c                ó.  € \        V \        4      '       g   R# V P                  R4      p\        V\        4      '       d¬   \        VP                  R4      ;'       g    R4      P	                  4       p\        VP                  R4      ;'       g5    VP                  R4      ;'       g    VP                  R4      ;'       g    R4      P	                  4       p\        T;'       d    T4      # \        \        T;'       g    R4      P	                  4       4      # )z¼Best-effort "a main model is configured" from the raw config dict (the
dashboard process may not share CLI runtime state). Unprovable => False,
which errs toward NOT firing the diagnostic.FrÁ   r½   r]   rQ   rP   )rL   r    rM   r   rŽ   r   )rÈ   Ú	model_cfgr½   rÁ   s   &   r   Ú_main_model_visiblerË   Ï   sÓ   € ô �j¤$×'Ò'ÙØ—‘˜wÓ'€IÜ�)œT×"Ò"Ü�y—}‘} ZÓ0×6Ð6°BÓ7×=Ñ=Ó?ˆÜØ�M‰M˜)Ó$×]Ð]¨	¯©°gÓ(>×]Ð]À)Ç-Á-ÐPVÓBW×]Ð]Ð[]ó
ç
‰%‹'ð 	ô �H×&Ð& Ó'Ð'Ü”�I—O�O Ó$×*Ñ*Ó,Ó-Ð-r   c               ó    € V ^8„  d   QhRRRR/# )r   ÚconfigúOptional[dict]r   r   )r   s   "r   r   r   ß   s   € ÷ ñ ˜nð °ñ r   c           
     óP  € \        V \        4      '       g   R# V P                  R4      p\        V\        4      '       d   V# V P                  R4      p\        V P                  R4      \        4      '       d   V P                  R4      M/ p\        V\        4      '       g   V'       g
   RV 9  d   R# \        V\        4      '       d   TM/ pRRV9   d   \        VR,          4      MRR\	        VP                  R	4      4      R
\	        VP                  R4      4      R\        V 4      /# )zÚReport whether the triage aux paths look configured: ``{auto_decompose,
decomposer_explicit, specifier_explicit, main_model_visible}``. ``None``
when no config context is present (keeps low-level callers/tests silent).NÚtriage_aux_statusÚ	auxiliaryÚkanbanrÁ   Úauto_decomposeTÚdecomposer_explicitÚkanban_decomposerÚspecifier_explicitÚtriage_specifierÚmain_model_visible)rL   r    rM   r   rÆ   rË   )rÍ   ÚexplicitÚauxÚ
kanban_cfgs   &   r   rÐ   rÐ   ß   sò   € ô �fœd×#Ò#ÙØ�z‰zÐ-Ó.€HÜ�(œD×!Ò!Øˆà
�*‰*�[Ó
!€CÜ)3°F·J±J¸xÓ4HÌ$×)OÒ)O�—‘˜HÔ%ÐUW€Jä�cœ4× Ò ¯¸ÀvÔ8MÙÜ˜C¤×&Ò&‰#¨B€Cð 	Ð@PÐT^Ô@^œ$˜zÐ*:Õ;Ô<ÐdhØÔ1°#·'±'Ð:MÓ2NÓOØÔ0°·±Ð9KÓ1LÓMØÔ1°&Ó9ðð r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úvaluer   rQ   r<   r   r   )r   s   "r   r   r   ø   s!   € ÷ .ñ .˜ð . sð .¨sñ .r   c                ób   €  \        V 4      pT^8¼  d   T# T#   \        \        3 d    Tu # i ; i)é   )r<   Ú	TypeErrorÚ
ValueError)rÝ   rQ   Úparseds   && r   Ú_positive_intrã   ø   s>   € ðÜ�U“ˆð ˜q”[ˆ6Ð- gÐ-øô ”zÐ"ô ØŠðús   ‚ ˜.­.c               ó   € V ^8„  d   QhRR/# ©r   r   úlist[Diagnostic]r   )r   s   "r   r   r      s   € ÷ ñ Ð>Nñ r   c                ó  € \        VR4      pV'       g   . # \        RRRR7      .pV\        V \        V 4      R7      ,          p\	        RRR	R
V\        V^ ,          4      \        VR,          4      \        V4      R\        VR4      /R7	      .# )z‘A worker's kanban_complete named created_cards that don't exist / weren't
its own; the completion was blocked. Clears on a later completion/edit.Ú completion_blocked_hallucinationÚcommentz#Add a comment explaining what to doF©r   r   r"   ©r¤   Úhallucinated_cardsÚerrorz%Worker claimed cards that don't exista  The completing worker declared created_cards that either didn't exist or weren't created by its profile. The completion was blocked and the task stayed in its prior state. Usually means the worker hallucinated ids instead of capturing return values from kanban_create.Úphantom_idsÚphantom_cards©	r   r   r8   r9   r;   r=   r>   r?   rB   éÿÿÿÿ)r›   r   rª   r®   r7   rd   r�   r¢   )rO   rl   r¸   ÚnowÚcfgr�   r;   s   &&&&&  r   Ú_rule_hallucinated_cardsrô      s›   € ô (¨Ð0RÓS€DßØˆ	Ü YÐ6[Ø*/ô1ð 2€GàÔ(¨´{À4Ó7HÔIÕI€GÜØ!¨GØ5ð%ð Ü  Q¥Ó(´yÀÀbÅÓ7JÔRUÐVZÓR[ØÔ0°°ÓGÐHô
ð 
ð 
r   Tc               ó   € V ^8„  d   QhRR/# rå   r   )r   s   "r   r   r   %  s   € ÷ 0ñ 0ÐBRñ 0r   c                óØ  € \        V R4      R8w  d   . # \        V4      pVf   . # \        VP                  R4      4      p\        VP                  R4      4      p\        VP                  R4      4      p\        VP                  R4      4      p	\        V,          w  r«rÍV'       d   W‰3MW˜3w  rïV'       g	   V'       d   . # \        V R4      ;'       g    Rp\        R	V
 2R
V
 R2RR7      .pV'       g+   V'       g#   VP                  \        RV 2R
V R24      4       V'       g$   RV 2pVP                  \        RV 2V4      4       \        RRRV R2RV R2VW3^RVRVRV
RV/R7	      .# )a„  A triage task can't leave triage without a usable aux model. With
auto-decompose on the primary slot is ``auxiliary.kanban_decomposer``
(specifier as fallback); off, it is ``auxiliary.triage_specifier``. The
default ``provider: auto`` falls back to the main model, so this fires only
when the slot isn't explicit AND no main model is visible. Requires config
context ({} keeps it silent).r­   ÚtriagerÓ   rØ   rÔ   rÖ   r²   z	<task_id>z
Configure zhermes config set z.provider autoTr‰   zOr configure fallback zhermes kanban specify zSpecify manually: Útriage_aux_unavailableÚwarningzTriage z has no usable modelzZThis task is still in triage and no working auxiliary model is visible to the dispatcher. zÇ The default slot uses `provider: auto` which falls back to the main model, but no main model is configured either. Configure the slot directly or set a main model so the auto fallback can take over.r‡   Úprimary_slotrð   )rR   rÐ   r   rM   Ú_TRIAGE_SLOTSr…   r˜   r7   )rO   rl   r¸   rò   ró   r­   rÓ   Úmain_visiblerÔ   rÖ   rú   Úfallback_slotÚprimary_descÚdetail_pathÚprimary_explicitÚfallback_explicitr‡   r;   rŠ   s   &&&&&              r   Ú_rule_triage_aux_unavailabler  %  s§  € ô �4˜Ó" hÔ.Øˆ	Ü˜sÓ#€FØ‚~Øˆ	ä˜&Ÿ*™*Ð%5Ó6Ó7€NÜ˜Ÿ
™
Ð#7Ó8Ó9€LÜ˜vŸz™zÐ*?Ó@ÓAÐÜ˜fŸj™jÐ)=Ó>Ó?ÐÜ=JÈ>Õ=ZÑ:€L ç5CÐ	Ñ1Ø Ð6ñ (Ð÷ Ÿ<Øˆ	ä˜$ Ó%×4Ð4¨€GÜØ
�\�NÐ#Ð'9¸,¸À~Ð%VÐbfôð €G÷ §\Ø�‰”yØ$ ] OÐ4Ð8JÈ=È/ÐYgÐ6hó
ô 	÷ Ø& w iÐ0ˆØ�‰”yÐ#5°c°UÐ!;¸SÓAÔBäØ%°	Ø˜�~Ð%9Ð:ðØ)˜]ð +IðJð Ø°1Ø˜Ð"2°NØ˜lÐ,@À,ðPôð ð r   c               ó   € V ^8„  d   QhRR/# rå   r   )r   s   "r   r   r   X  s   € ÷ ñ Ð>Nñ r   c                óê   € \        VR4      pV'       g   . # \        RRRR\        V \        V 4      R7      \	        V^ ,          4      \	        VR	,          4      \        V4      R\        VR4      /R7	      .# )
zAdvisory: the completion summary mentions ``t_<hex>`` ids that don't
resolve. Warning only; clears on a later clean completion.Ú!suspected_hallucinated_referencesÚprose_phantom_refsrù   z.Completion summary references unknown task idszÏThe completion summary mentions task ids that don't resolve in this board's database. The completion itself succeeded, but downstream consumers parsing the summary may be pointed at cards that never existed.rë   Úphantom_refsrð   rñ   )r›   r7   rª   r®   rd   r�   r¢   )rO   rl   r¸   rò   ró   r�   s   &&&&& r   Ú_rule_prose_phantom_refsr  X  sy   € ô (¨Ð0SÓT€DßØˆ	ÜØ!¨IØ>ð6ô *¨$¼ÀDÓ8IÔJÜ  Q¥Ó(´yÀÀbÅÓ7JÔRUÐVZÓR[ØÔ1°$¸ÓGÐHô	ð 	ð 	r   c               ó    € V ^8„  d   QhRRRR/# )r   ró   r    r   r   r   )r   s   "r   r   r   j  s   € ÷ Oñ O˜Dð O Sñ Or   c                óF   € V P                  RV P                  R^4      4      # )zH``failure_threshold`` with the legacy ``spawn_failure_threshold`` alias.Úfailure_thresholdÚspawn_failure_threshold)rM   ©ró   s   &r   Ú_failure_thresholdr  j  s    € à�7‰7Ð&¨¯©Ð0IÈ1Ó(MÓNÐNr   Úspawn_failedÚspawnÚ	timed_outÚtimeoutÚcrashedÚcrashc               ó   € V ^8„  d   QhRR/# rå   r   )r   s   "r   r   r   r  s   € ÷ Oñ OÐ=Mñ Or   c                óh  € \        V R4      R.9   d   . # \        \        V4      ^4      p\        VP                  R4      V4      p\	        V RR^ 4      p\        V4      pV'       g   Ve   Wu8  d   . # T;'       g    ^ p\	        V RR4      p	\        V R4      p
\        R	 R
 \        V4       4        4       R4      p. pVR8X  dZ   V
'       dR   V
R8w  dK   RV
 R2RV
 R2ríVP                  \        RV 2VRR7      4       VP                  \        RV 2V4      4       M5VR/9   d/   \        V R4      pV'       d   VP                  \        V4      4       VP                  \        V \        V 4      R7      4       Wu^,          8¼  d   RMRp\        V	4      p\        P                  T;'       g    RR4      pV'       d   RpRV RV R2pMQV'       d8   RV RV R VP!                  4       ^ ,          R!,           2pR"V R#V R$V R%V R&2	pMRV RV R'2pR"V R#V R(2p\#        R)VVVVW3VRVR*VR+V	R,VRV/R-7	      .# )0uÅ  ``consecutive_failures`` >= cfg["failure_threshold"] (legacy key
``spawn_failure_threshold``), regardless of failure mode â€” the kernel keeps
retrying and the operator must intervene. Runtime callers derive the
threshold from ``kanban.failure_limit`` so it doesn't lag the breaker.

Exempt: done/archived (a manual done ends no run, so the streak is history)
and running (a retry in flight must not read as a current failure; re-fires
if it fails too).r­   Úfailure_limitÚconsecutive_failuresÚspawn_failuresNÚlast_failure_errorÚlast_spawn_errorÚassigneec              3  ó8   "  € T F  pVR9   g   K  Vx € K  	  R# 5i)r  N>   r  r  r  r   )rr   Úocs   & r   rs   Ú*_rule_repeated_failures.<locals>.<genexpr>‹  s"   é € ð 	<ÑS�ØÐ:Ñ:÷ 
ŠÓSùs   ‚	�
c              3  ó:   "  € T F  p\        VR 4      x € K  	  R# 5i)ÚoutcomeNr^   )rr   r³   s   & r   rs   r  ‹  s   é € ÐSÑ:R°Q”{ 1 i×0Ð0Ó:Rùs   ‚r  rQ   z
hermes -p z doctorz authzVerify profile: Tr‰   zFix profile auth: r²   rë   Úcriticalrí   r]   ÚfailureuR   Provider rejected this profile's credential or model â€” blocked after one attemptz­The worker's provider call failed with an error a retry cannot fix (revoked or invalid API key, model not found), so the dispatcher blocked the task instead of spending the z/-attempt retry budget on it. Full last error:

zO

Fix the assignee profile's provider credentials/model, then unblock the task.zAgent z xz: ºNé    NzThis task has failed z times in a row (most recent: z). Full last error:

z3

The dispatcher circuit breaker is configured for z_ consecutive non-success attempts. Fix the root cause and reclaim or unblock the task to retry.z (no error recorded)zP) but no error text was captured. Check the suggested command or the worker log.Úrepeated_failuresÚmost_recent_outcomeÚ
last_errorr  rð   ©ÚdoneÚarchivedr¤   >   r  r  )rR   rã   r  rM   rj   r€   Únextr¹   r˜   r…   r‹   Úextendrª   r®   r’   Ú_OUTCOME_LABELSÚ
splitlinesr7   )rO   rl   r¸   rò   ró   r   r  ÚfailuresÚterminal_tripr�   r  r'  r;   ÚdoctorÚauthr‡   r   Úerr_snippetÚoutcome_labelr8   r9   s   &&&&&                r   Ú_rule_repeated_failuresr6  r  sª  € ô �4˜Ó"Ð&EÔEØˆ	ÜÔ0°Ó5°qÓ9€IÜ! #§'¡'¨/Ó":¸IÓF€MÜ˜DÐ"8Ð:JÈAÓN€Hô 9¸Ó@€Mß˜hÒ.°(Ô2FØˆ	Ø�}ˆ}˜1€HÜ˜DÐ"6Ð8JÓK€HÜ˜4 Ó,€Hô ñ	<ÑSÔ:LÈTÔ:RÔSó 	<àóÐð ')€GØ˜nÔ,·¸hÈ)Ô>Sà# H :¨WÐ5¸ÀHÀ:ÈUÐ7S�Ø�‰”yÐ#3°F°8Ð!<¸fÐPTÔUÔVØ�‰”yÐ#5°d°VÐ!<¸dÓCÕDØ	Ð 8Ô	8ä˜d DÓ)ˆßØ�N‰NÔ+¨GÓ4Ô5Ø‡N�NÔ,¨T¼;ÀtÓ;LÔMÔNà%°Q­Ô6‰z¸G€HÜ  Ó*€KÜ#×'Ñ'Ð(;×(AÐ(A¸rÀ9ÓM€MßØdˆðeàˆoÐNÈ{Èmð \\ð]ñ 	÷ 
Ø˜˜ r¨(¨°2°k×6LÑ6LÓ6NÈqÕ6QÐRVÕ6WÐ5XÐYˆà# H :Ð-KÈMÈ?ð [Ø)˜]Ð*_Øˆoð )ð*ñ 	ð ˜˜ r¨(¨Ð3GÐHˆà# H :Ð-KÈMÈ?ð [Vð Wð 	ô Ø ¨8Ø˜F¨GØ°8à" HØ!Ð#6Ø˜(Ø Ø˜]ð
ô	ð ð r   c               ó   € V ^8„  d   QhRR/# rå   r   )r   s   "r   r   r   Ä  s   € ÷ 7ñ 7Ð<Lñ 7r   c                óþ  € \        V R4      R9   d   . # \        V R^ 4      ;'       g    ^ \        \        V4      4      8¼  d   . # \        VP                  R^4      4      p^ pRp\	        V4       F<  p\        VR4      p	V	R8X  d   V^,          pVf   \        VR4      pK1  K3  V	R9   g   K<   M	  We8  d   . # \        V R4      p
. pV
'       d   VP                  \        V
4      4       VP                  \        V \        V 4      R	7      4       We^,          8¼  d   R
MRp\        V4      pV'       d.   RV RVP                  4       ^ ,          R,           2pRV RV 2pMRV R2pRV R2p\        RVWïVW3VRVRV/R7	      .# )a·  Trailing run outcomes show >= cfg["crash_threshold"] (default 2)
consecutive ``crashed`` with no ``completed``/``reclaimed`` between. Fires
earlier than ``repeated_failures`` for a crash-specific heads-up and
suppresses itself when the unified rule is about to fire.

Exempt: done/archived (a manual done appends no completed run, so the
streak would be permanent) and running (an in-flight run has no outcome
and wouldn't break the scan).r­   r  Úcrash_thresholdNr!  r  rí   r²   rë   r"  zAgent crashed zx: r$  z	The last z4 runs ended with outcome=crashed. Full last error:

zx (no error recorded)z_ runs ended with outcome=crashed but no error text was captured. Check the worker log for more.Úrepeated_crashesÚconsecutive_crashesr(  rð   r)  >   r}   Ú	reclaimed)rR   r<   r  rM   r¹   r˜   r‹   r-  rª   r®   r’   r/  r7   )rO   rl   r¸   rò   ró   r   Úconsecutiver�   r³   r!  r‡   r;   r   r4  r8   r9   s   &&&&&           r   Ú_rule_repeated_crashesr>  Ä  s¸  € ô �4˜Ó"Ð&EÔEØˆ	ä�DÐ0°!Ó4×9Ð9¸¼cÔBTÐUXÓBYÓ>ZÔZØˆ	ä�C—G‘GÐ-¨qÓ1Ó2€Ið €KØ€HÜ Ö%ˆÜ˜a Ó+ˆØ�iÔØ˜1ÕˆKØÒÜ& q¨'Ó2’ñ  àÐ2Ö2Ùñ &ð ÔØˆ	Ü˜$ Ó%€GØ&(€GßØ�‰Ô'¨Ó0Ô1Ø‡N�NÔ,¨T¼;ÀtÓ;LÔMÔNØ(¸­MÔ9‰z¸w€Hä  Ó*€KßØ   ¨S°×1GÑ1GÓ1IÈ!Õ1LÈTÕ1RÐ0SÐTˆà˜�}ð %#Ø#. -ð1ñ 	ð
 !  Ð-BÐCˆà˜�}ð %Ið Jð 	ô Ø¨(Ø¨GØ°;Ø# [°,ÀÐIô	ð ð r   c               ó   € V ^8„  d   QhRR/# rå   r   )r   s   "r   r   r   þ  s   € ÷ /ñ /ÐFVñ /r   c                ó~  € \        V R4      R8w  d   . # \        R \        \        V4      4       4       R4      pVf   . # \	        \        V4      P                  R4      ;'       g    R4      P                  4       pVP                  4       P                  R4      '       g   . # VP                  R4      p\        V\        4      '       g   . # VP                  R	4      ;'       g    .  Uu. uF5  p\        V\        4      '       g   K  VP                  R4      R
8X  g   K3  VNK7  	  p	pV	'       g   . # \	        \        V R4      ;'       g    R4      p
V	 Uu. uF5  qˆP                  R4      '       g   K  \	        VP                  R4      4      NK7  	  pp. pV
'       d!   VP                  \        RRV
 2RR7      4       V
'       d1   V'       d)   VP                  \        RRV
 RV^ ,           24      4       \        V4      ;'       g    Tp\        RRR\!        V4       R2RVWÝ\!        V4      RV
RVRV/R7	      .# u upi u upi )zËLegacy review handoff starving children: the implementation is
sticky-blocked with a ``review-required:`` reason while todo children wait
for it to be terminal. Graph-aware; deliberately mutates nothing.r­   Úblockedc              3  óJ   "  € T F  p\        V4      R 8X  g   K  Vx € K  	  R# 5i)rA  N)r`   )rr   rW   s   & r   rs   Ú3_rule_review_dependency_deadlock.<locals>.<genexpr>  s    é € Ð]Ñ&< ÄÈBÃÐS\Ñ@\ŸšÓ&<ùs   ‚#™
#NÚreasonr]   zreview-required:Ú_graphÚchildrenÚtodor²   z*Complete the finished implementation phasezhermes kanban complete Tr‰   z(Or unlink the incorrectly gated reviewerúhermes kanban unlink Ú Úreview_dependency_deadlockrí   zReview handoff blocks z dependent task(s)a  This implementation is sticky-blocked for review while its downstream task(s) require the implementation to be done or archived before they can run. Complete the finished phase, unlink the incorrect dependency, or migrate this workflow to the first-class review lifecycle.Úblocked_parent_idÚwaiting_child_idsÚblock_reasonrð   )rR   r,  r   rG   r   rY   rM   rŽ   rÄ   Ú
startswithrL   r    r˜   r…   rd   r7   r�   )rO   rl   r¸   rò   ró   Úlatest_blockrD  ÚgraphÚchildÚwaiting_childrenr‡   Ú	child_idsr;   Ú
blocked_ats   &&&&&         r   Ú _rule_review_dependency_deadlockrU  þ  s   € ô �4˜Ó" iÔ/Øˆ	ÜÑ]¤h¬t°F«|Ô&<Ó]Ð_cÓd€LØÒØˆ	Ü” Ó-×1Ñ1°(Ó;×AÐA¸rÓB×HÑHÓJ€FØ�<‰<‹>×$Ñ$Ð%7×8Ò8Øˆ	à�G‰G�HÓ€EÜ�eœT×"Ò"Øˆ	à!ŸI™I jÓ1×7Ð7°RÐ7óÙ7�%Ü�eœT×"ô 	Ø',§y¡y°Ó':¸fÑ'D÷ 	ˆÑ7ð ð ÷ Øˆ	ä”+˜d DÓ)×/Ð/¨RÓ0€GÙ3CÓWÑ3C¨%ÇyÁyÐQUÇÔ%”�U—Y‘Y˜t“_Ö%Ñ3C€IÐWØ&(€GßØ�‰”yØ8Ð<SÐT[ÐS\Ð:]Øô
ô 	÷ —9Ø�‰”yØ6Ð:OÐPWÈyÐXYÐZcÐdeÕZfÐYgÐ8hó
ô 	ô ˜<Ó(×/Ð/¨C€JÜØ)°GØ&¤s¨9£~Ð&6Ð6HÐIð#ð Ø ÄÀYÃØ! 7Ð,?ÀÈNÐ\bÐcô
ð 
ð 
ùò+ùò Xs   Ã'H5ÄH5ÄH5ÅH:Å.H:c               ó   € V ^8„  d   QhRR/# rå   r   )r   s   "r   r   r   0  s   € ÷ ñ ÐEUñ r   c                óD  € \        V R4      R8w  d   . # VP                  R4      p\        V\        4      '       g   . # VP                  R4      ;'       g    .  Uu. uFN  p\        V\        4      '       g   K  VP                  R4      '       g   K4  VP                  R4      R9  g   KL  VNKP  	  ppV'       g   . # \	        \        V R4      ;'       g    R4      pV Uu. uF  p\	        VR,          4      NK  	  p	p\        \        V R^ R7      ;'       g    ^ 4      ;'       g    Tp
\        R	R
R\        V	4       R2R\        RRV	^ ,           RV 24      .Y£\        V	4      RV Uu. uF  pRVR,          RVP                  R4      /NK!  	  up/R7	      .# u upi u upi u upi )a  A ``running`` card with a direct parent that is not ``done``/``archived``:
the dependency gate is not holding it (the parent reopened mid-run, or the
edge predates the running-child refusal) and ``kanban_complete`` will be
refused until the parents finish. Graph-aware; mutates nothing.r­   r¤   rE  Úparentsr²   r]   Ú
started_at©rQ   Úrunning_with_open_parentsrù   zRunning while z parent(s) are not donea  This card is running concurrently with a parent it declares a dependency on, so the parent's work is not serialised ahead of it and completion will be refused until every parent is done or archived. Finish the parent, or unlink the edge if it was never meant to gate this run.z/Unlink the parent that should not gate this runrH  rI  Úopen_parentsrð   )r*  r+  )	rR   rM   rL   r    r   r<   r7   r�   r…   )rO   rl   r¸   rò   ró   rP  Úparentr\  r‡   Ú
parent_idsÚseen_atrX   s   &&&&&       r   Ú_rule_running_with_open_parentsr`  0  s¢  € ô
 �4˜Ó" iÔ/Øˆ	Ø�G‰G�HÓ€EÜ�eœT×"Ò"Øˆ	à#Ÿi™i¨	Ó2×8Ð8°bÐ8óÙ8�6Ü�fœd×#ô 	Ø(.¯
©
°4×(8ô 	à�J‰J�xÓ Ð(<Ñ<÷ 	ˆÑ8ð ð ÷
 Øˆ	Ü”+˜d DÓ)×/Ð/¨RÓ0€GÙ2>Ó?±,¨”#�f˜T•lÖ#±,€JÐ?Ü”+˜d L¸!Ô<×AÐAÀÓB×IÐIÀc€GÜØ(°9Øœs :›Ð/Ð/FÐGð#ô ÐLØ2°:¸aµ=°/ÀÀ7À)ÐLóNð Oà´s¸:³ØÑS_Ó`ÑS_Èa  a¨¥g¨x¸¿¹¸x»ÓIÑS_Ñ`Ðaôð ð ùòùò @ùò as$   ÁFÁ7FÂFÂ(FÃFÅ$%Fc               ó   € V ^8„  d   QhRR/# rå   r   )r   s   "r   r   r   R  s   € ÷ ñ Ð<Lñ r   c                óÔ  a€ \        VP                  R^4      4      p\        V R4      R8w  d   . # \        VR04      oS^ 8X  d   . # VS,
          R,          pWe8  d   . # \        ;QJ d    V3R lV 4       F  '       g   K   RM	  RM! V3R lV 4       4      '       d   . # \        RR	R
\        V4       R2R\        V4       R2\        RRRR7      .SS^RSR\        V^4      /R7	      .# )zsBlocked for >= cfg["blocked_stale_hours"] (default 24) with no comment
or unblock since the last ``blocked`` event.Úblocked_stale_hoursr­   rA  g      ¬@c              3  ól   <"  € T F)  p\        V4      R9   ;'       d    \        V4      S8„  x € K+  	  R# 5i)Ú	commentedN>   re  r~   rq   )rr   rW   Úlast_blocked_tss   & €r   rs   Ú)_rule_stuck_in_blocked.<locals>.<genexpr>_  s6   øé € ð Ùˆrô �r‹?Ð8Ñ8×\Ð\¼YÀr»]È_Ñ=\Ô\Ûùs   ƒ14TFÚstuck_in_blockedrù   zTask has been blocked for Úhz"This task transitioned to blocked u¸   h ago and has had no comments or unblock attempts since. Blocked tasks are waiting for human input â€” check the block reason and either unblock with feedback or answer with a comment.ré   z Add a comment / unblock the taskrê   rT  Ú	age_hoursrð   )	ÚfloatrM   rR   rv   rÅ   r7   r<   r   Úround)rO   rl   r¸   rò   ró   Úhoursrj  rf  s   &&&&&  @r   Ú_rule_stuck_in_blockedrn  R  s
  ø€ ô �#—'‘'Ð/°Ó4Ó5€EÜ�4˜Ó" iÔ/Øˆ	Ü& v°	¨{Ó;€OØ˜!ÔØˆ	Ø�Õ&¨&Õ0€IØÔØˆ	ç
ƒsô Ùó‡s‡s‚sô Ùó÷ ò àˆ	ÜØ¨)Ø*¬3¨y«>Ð*:¸!Ð<Ø3´C¸	³NÐ3Cð DSð Tô " yÐ8ZØ,0ô2ð 3à%°OÈ1Ø˜O¨[¼%À	È1Ó:MÐNô
ð 
ð 
r   c               ó   € V ^8„  d   QhRR/# rå   r   )r   s   "r   r   r   o  s   € ÷ 8ñ 8ÐAQñ 8r   c                ó´  € \        VP                  R4      ^4      p\        VP                  RR4      4      pW6,
          p^ pRp	^ p
^ pV FU  p\        V4      pW×8  d   K  \	        V4      pVR8X  d#   V
^ 8X  d   Tp
V	'       d   V^,          pTpRp	KH  KJ  VR8X  g   KS  Rp	KW  	  W…8  d   . # \        V R4      p. pV'       d&   R	V 2pVP                  \        R
V 2VRR7      4       \        RRRV R\        VR,          4       R2RV R2TV
'       d   \        V
4      M
\        V4      V'       d   \        V4      M
\        V4      VRVR\        V4      /R7	      .# )u  >= cfg["block_cycle_threshold"] (default 3) blocked-after-unblocked
cycles within cfg["block_cycle_window_seconds"] (default 24h). Complements
``_rule_stuck_in_blocked``, whose timer any unblock resets, so fast cyclers
are invisible to it.

``_rule_stuck_in_blocked`` resets its timer on any ``commented`` / ``unblocked`` event, so a task that
cycles every few minutes is invisible to it regardless of how many times it cycles (#29747 gap 1). This
rule complements that one by counting blockâ†’unblock cycles in a sliding window.
Úblock_cycle_thresholdÚblock_cycle_window_secondsé  FrA  r~   Tr²   zhermes kanban events zCheck block reasons: r‰   Úblock_unblock_cyclingrù   u   Task blockâ†’unblock cycled zx in ri  zThis task has been blocked zú times after being unblocked, suggesting the unblock is not addressing the root cause and the worker keeps hitting the same wall. Review the block reasons in the event history; a different intervention (reassign, change scope, archive) may be needed.ÚcyclesÚwindow_secondsrð   i€Q )
rã   rM   rk  rd   r`   rR   r˜   r…   r7   r<   )rO   rl   r¸   rò   ró   r   rv  Úcycle_cutoffru  Úseen_unblock_since_last_cycleÚinitial_blocked_tsÚlast_cycle_blocked_tsrW   Útsr   r‡   r;   rŠ   s   &&&&&             r   Ú_rule_block_unblock_cyclingr|  o  s†  € ô ˜cŸg™gÐ&=Ó>ÀÓB€IÜ˜3Ÿ7™7Ð#?ÀÓKÓL€NØÕ'€Lð
 €FØ$)Ð!ØÐØÐÛˆÜ�r‹]ˆØÔÙÜ˜2‹ˆØ�9ÔØ! QÔ&Ø%'Ð"ß,Ø˜!•�Ø(*Ð%Ø05Ò-ñ -ð �[Ö Ø,0Ò)ñ ð ÔØˆ	ä˜$ Ó%€GØ&(€GßØ% g YÐ/ˆØ�‰”yÐ#8¸¸Ð!>ÀÈtÔTÔUÜØ$¨yØ,¨V¨H°E¼#¸nÈTÕ>QÓ:RÐ9SÐSTÐUØ,¨V¨Hð 57ð 8ð ß1C”cÐ,Ô-ÌÈSËß3H”SÐ.Ô/ÌcÐRUËhØØ˜Ð 0´#°nÓ2EÐFôð ð r   c               ó   € V ^8„  d   QhRR/# rå   r   )r   s   "r   r   r   ª  s   € ÷ 6ñ 6Ð=Mñ 6r   c                óÂ  € \        VP                  RR 4      4      p\        V R4      R8w  d   . # \        V R4      '       d   . # \        V R4      ;'       g    RpVP                  4       '       g   . # \	        V0 R!m4      pV^ 8X  d"   \        \        V R^ R7      ;'       g    ^ 4      pV^ 8X  d   . # W7,
          pW…8  d   . # VR	8¼  d   VR	,          R
 R2M\        V^<,          4       R2p	W…^,          8¼  d   Rp
MW…^,          8¼  d   Rp
MRp
\        RRRV/R7      \        RR4      .p\        RV
RV	 R2RV	 RV: R2VWw^RVR\        V4      RVR\        V4      /R7	      .# )"uZ  Assigned, unclaimed, ``ready`` for >= cfg["stranded_threshold_seconds"]
(default 30 min). Deliberately age-based and identity-agnostic so it
catches typo'd assignees, deleted profiles, and down external worker
pools alike without a registry to curate. Unassigned tasks are excluded â€”
the dispatcher's ``skipped_unassigned`` already covers them.Ústranded_threshold_secondsr­   ÚreadyÚ
claim_lockr  r]   rc   rZ  rs  ú.1fri  Úmr"  rí   rù   r¨   zReassign to a different workerÚcurrent_assigneer§   zCheck dispatcher statuszhermes kanban diagnosticsÚstranded_in_readyz
Ready for z with no workerzThis task has been ready for z5 but nothing has claimed it. Common causes: assignee z© is misspelled, the profile was deleted, or the external worker pool for this lane is down. Confirm the assignee is correct and that a worker is actually polling for it.Úready_sinceÚage_secondsÚthreshold_secondsrð   é  >   Úcreatedr|   r<  r~   )	rk  rM   rR   rŽ   rv   r<   r   r…   r7   )rO   rl   r¸   rò   ró   rˆ  r  Úlast_ready_tsr‡  Úage_strr   r;   s   &&&&&       r   Ú_rule_stranded_in_readyr�  ª  s¨  € ô ˜cŸg™gÐ&BÀGÓLÓMÐÜ�4˜Ó" gÔ-Øˆ	ä�4˜×&Ò&Øˆ	Ü˜4 Ó,×2Ð2°€HØ�>‰>×ÒØˆ	ô
 % VÒ-^Ó_€MØ˜ÔÜœK¨¨lÀAÔF×KÐKÈ!ÓLˆØ˜ÔØˆ	àÕ%€KØÔ&Øˆ	à.9¸TÔ.A�˜tÕ# CÐ(¨Ñ*Ì#ÈkÐ\^ÕN^ÓJ_ÐI`Ð`aÐGb€Gà¨!Õ+Ô+Ø‰Ø	¨AÕ-Ô	-Ø‰àˆô 	˜jÐ0PØ"4°hÐ!?ô	AäÐ+Ð-HÓIð€Gô
 Ø ¨8Ø˜7˜) ?Ð3Ø.¨w¨ið 8Ø#™,ð '*ð+ð Ø#ÀqØ˜]¨M¼3¸{Ó;KØ˜(Ð$7¼Ð=NÓ9OðQôð ð r   c               ó   € V ^8„  d   QhRR/# rå   r   )r   s   "r   r   r   ã  s   € ÷ Dñ DÐ9Iñ Dr   c                óÌ  a€ \        VP                  RR4      4      p\        V R4      R8w  d   . # ^ RIHp VP                  \        V RR4      4      pVe   W7,
          V8  d   . # \        \        V RR4      ;'       g    R4      pVP                  VP                  4       4      '       g   . # VP                  \        V R	R4      4      p	V	e   W9,
          VP                  8:  d   . # ^ R
I
Ho \        V RR4      \        V RR4      3.p
\        V RR4      pV Fi  pVe   \        VRR4      V8w  d   K  \        V4      R.9   g   K-  \        V4      pV
P                  VP                  R4      VP                  R4      34       Kk  	  \        V
 UUu0 uF  w  rïV'       g   K  \!        V4      kK  	  upp4      p\"        ;QJ d    V3R lV
 4       F  '       g   K   RM	  RM! V3R lV
 4       4      '       d   . # W7,
          pVR8¼  d   VR,          R R2M\!        V^<,          4       R2pVV^,          8¼  d   RpMVV^,          8¼  d   RpMRp\        \        V R4      ;'       g    R4      p\%        RTRV R2RT R R!P'                  R" V 4       4      ;'       g    R# R$2\)        R%R&RR'7      \+        R(R)V 24      .Ww^RVR*\!        V4      R+VR	V	R,\!        V4      /R-7	      .# u uppi )/u…  A ``running`` card whose claim expired >= cfg["stale_running_grace_seconds"]
(default 1h) ago with no live recorded worker.

Nothing in the dispatcher is going to free this card â€” its reclaim paths can decline a
card whose owner receipt never resolved, and until they could not, the failure was
invisible: an operator saw only the cards queued behind the profile's worker cap. So the
jam itself is the signal. Silent in every shape that can still progress: a live claim
(the owner is still working), a heartbeat inside the dispatcher's max-stale window, a
foreign host's lock (its pids say nothing here), or a live recorded worker. Read-only.
Ústale_running_grace_secondsr­   r¤   )Ú	kanban_dbÚclaim_expiresNr�  r]   Úlast_heartbeat_at)Ú_worker_aliveÚ
worker_pidÚworker_started_atÚcurrent_run_idrA   r¡   rY  c              3  ó`   <"  € T F#  w  rV'       g   K  S! \        V4      V4      x € K%  	  R # 5ir(   )r<   )rr   r¡   rY  r”  s   &  €r   rs   Ú&_rule_stale_running.<locals>.<genexpr>	  s'   øé € Ð
VÁh±?°3ÔRUÔ.‰=œ˜S› :×.Ð.Ãhùs   ƒ.”.TFrs  r‚  ri  rƒ  r"  rí   rù   r²   Ústale_runningzRunning z+ past its expired claim with no live workerzONothing is executing this card and nothing has reclaimed it: the claim expired zR ago, no heartbeat arrived inside the last hour, and no pid recorded for the run (z, c              3  ó8   "  € T F  p\        V4      x € K  	  R # 5ir(   )r   )rr   rX   s   & r   rs   r™    s   é € Ð$:±T°¤S¨§V V³Tùs   ‚Únonezï) is alive. The dispatcher declines a reclaim when the run's owner receipt never resolved, so the card can hold its profile's worker slot indefinitely while every card queued behind it stays deferred. Check the card's log, then reclaim it.r¦   zReclaim the dead claimrê   zReclaim from the CLIzhermes kanban reclaim Úexpired_secondsÚrecorded_pidsÚgrace_secondsrð   >   ÚspawnedÚworker_bound)rk  rM   rR   Ú
hermes_clir‘  Ú	_to_epochr   rN  Ú_host_prefixÚ)DEFAULT_CLAIM_HEARTBEAT_MAX_STALE_SECONDSÚhermes_cli.kanban_db_dispatchr”  r`   rY   r˜   r·   r<   rÅ   r7   Újoinr   r…   )rO   rl   r¸   rò   ró   rŸ  ÚkbÚexpiresÚlockÚhbÚrecordedrA   rW   r!   r¡   Ú_Úpidsr�  rŒ  r   r‡   r”  s   &&&&&                @r   Ú_rule_stale_runningr¯  ã  sÙ  ø€ ô ˜#Ÿ'™'Ð"?ÀÓIÓJ€MÜ�4˜Ó" iÔ/Øˆ	Ý*Ø�l‰lœ; t¨_¸dÓCÓD€GØ‚˜3�=¨MÔ9Øˆ	ÜŒ{˜4 ¨rÓ2×8Ð8°bÓ9€DØ�?‰?˜2Ÿ?™?Ó,×-Ò-Øˆ	Ø	�‰”k $Ð(;¸TÓBÓ	C€BØ	‚~˜3�8¨×(TÑ(TÔTØˆ	å;Ü˜T <°Ó6Ü˜TÐ#6¸Ó=ð?ð @€Hô ˜Ð/°Ó6€FÛˆØÒ¤+¨b°(¸DÓ"AÀVÔ"KÙÜ�r‹?Ð9Ö9Ü$ RÓ(ˆGØ�O‰O˜WŸ[™[¨Ó/°·±¸\Ó1JÐKÖLñ ô ©(Ô:©(¡ ´c”8”3�s–8©(Ò:Ó;€Dß
ƒsÔ
VÁhÓ
V‡s‡s‚sÔ
VÁhÓ
V×VÒVØˆ	à•m€OØ3BÀdÔ3J�/ DÕ(¨Ð-¨QÑ/Ü˜?¨RÕ/Ó0Ð1°Ð3ð ð ˜-¨!Õ+Ô+Ø‰Ø	˜M¨AÕ-Ô	-Ø‰àˆä”+˜d DÓ)×/Ð/¨RÓ0€GÜØ xØ˜˜	Ð!LÐMØ`Ø�ð ØŸ9™9Ñ$:±TÓ$:Ó:×DÐD¸fÐEð FHðIô " yÐ8PÐ\`ÔaÜÐ1Ð5KÈGÈ9Ð3UÓVðXà¸1Ø˜wÐ(9¼3¸Ó;OØ˜tÐ%8¸"Øœs =Ó1ð3ôð ð ùó! ;s   ÆK 
ÆK 
zlist[RuleFn]Ú_RULESr  r  r9  rc  r  r�  c               ó    € V ^8„  d   QhRRRR/# )r   ró   r    r   r   r   )r   s   "r   r   r   L  s   € ÷ Jñ J ð J¨$ñ Jr   c                ó&   € R V 9   ;'       g    RV 9   # )r  r  r   r  s   &r   Ú_has_explicit_thresholdr³  L  s   € Ø #Ñ%×IÐIÐ)BÀcÑ)IÐIr   c               ó    € V ^8„  d   QhRRRR/# )r   rÛ   rÎ   r   r    r   )r   s   "r   r   r   P  s   € ÷ ñ ¨.ð ¸Tñ r   c                óú   € T ;'       g    / p \        V P                  R4      ;'       g    / 4      pVP                  RV P                  R\        R,          4      4       \	        V4      '       g   VR,          VR&   V# )zÕDiagnostics config from the ``kanban`` section. ``kanban.diagnostics.
failure_threshold`` is an explicit override; otherwise the threshold is
``kanban.failure_limit`` so diagnostics match the dispatcher's breaker.Údiagnosticsr  r  )r    rM   Ú
setdefaultÚDEFAULT_CONFIGr³  )rÛ   Údiag_cfgs   & r   Úconfig_from_kanban_configrº  P  sr   € ð ×!Ð!˜r€JÜ�J—N‘N =Ó1×7Ð7°RÓ8€HØ×ÑØ˜Ÿ™¨¼ÐH[Õ9\Ó]ôô # 8×,Ò,Ø(0°Õ(AˆÐ$Ñ%Ø€Or   c               ó    € V ^8„  d   QhRRRR/# )r   rÈ   rÎ   r   r    r   )r   s   "r   r   r   ^  s   € ÷ ñ ¨>ð ¸dñ r   c                ó$  € T ;'       g    / p \        V \        4      '       g   / # / pV P                  R4      p\        V\        4      '       d   VP                  \	        V4      4       W!R&   R F  pV P                  V4      pVf   K  WAV&   K   	  V# )zºDiagnostics config from the full runtime config: folds ``kanban`` through
``config_from_kanban_config`` and carries ``kanban``/``auxiliary``/``model``
through for the triage-aware rules.rÒ   )rÑ   rÁ   )rL   r    rM   Úupdaterº  )rÈ   ró   rÛ   rž   rÝ   s   &    r   Úconfig_from_runtime_configr¾  ^  sˆ   € ð ×!Ð!˜r€JÜ�j¤$×'Ò'Øˆ	Ø€CØ—‘ Ó)€JÜ�*œd×#Ò#Ø�
‰
Ô,¨ZÓ8Ô9Ø"ˆH‰Û%ˆØ—‘˜sÓ#ˆØÔØ�‹Hñ &ð €Jr   rò   rÍ   rP  c               ó0   € V ^8„  d   QhRRRRRRRRRRR	R
/# )r   rl   rG   r¸   rò   r@   rÍ   rÎ   rP  r   ræ   r   )r   s   "r   r   r   q  sF   € ÷ ñ àðð ðð
 
ðð ðð ðð ñr   c          
     ó  a€ \        Ve   TM\        P                  ! 4       4      pT;'       g    / p/ \        CVCpVe   WWR&   \        V4      '       g1   RV9   d*   \	        VP                  R4      \        R,          4      VR&   . p\         F  p	 VP                  V	! WW&V4      4       K  	  \        \        4       U
Uu/ uF  w  r«WºbK	  	  upp
oVP                  V3R lR7       V#   \         d     Kj  i ; iu upp
i )znRun every rule for one task; critical first, then error, warning; ties
broken by most-recent ``last_seen_at``.rE  r  r  c                ól   <€ SP                  V P                  R4      ) V P                  ;'       g    ^ ) 3# )rß   rñ   )rM   r   r>   )ÚdÚseverity_idxs   &€r   r´   Ú*compute_task_diagnostics.<locals>.<lambda>�  s.   ø€ ˜\×-Ñ-¨a¯j©j¸"Ó=Ð=ÀÇÁ×ATÐATÐSTÐ?UÑVr   r¶   )r<   Útimer¸  r³  rã   rM   r°  r-  rK   Ú	enumerater   Úsort)rO   rl   r¸   rò   rÍ   rP  Únow_tsró   r    ÚruleÚiÚsrÃ  s   &&&$$$      @r   Úcompute_task_diagnosticsrÌ  q  sñ   ø€ ô ˜š‘¬T¯YªY«[Ó9€FØ�\ˆ\�r€FØ
&Œ^Ð
&˜vÐ
&€CØÒØˆH‰Ü" 6×*Ò*¨À&Ô/HÜ#0Ø�J‰J�Ó'¬Ð8KÕ)Ló$
ˆÐÑ ð €Cßˆð	Ø�J‰J‘t˜D¨$¸Ó<Ö=ñ ô &/¬~Ô%>Ô?Ñ%>™T˜Q�A’DÑ%>Ò?€LØ‡H�HÔV€HÔWØ€Jøô ô 	âð	üó @s   ÂC)Ã C;Ã)C8Ã7C8)rù   rí   r"  r(   )úauxiliary.kanban_decomposerúauxiliary.triage_specifierÚ
decomposerz�Auto-decompose is on, so the dispatcher needs auxiliary.kanban_decomposer (with auxiliary.triage_specifier as a fallback for non-fan-out tasks).)rÎ  rÍ  Ú	specifierzkAuto-decompose is off, so triage tasks need `hermes kanban specify`, which uses auxiliary.triage_specifier.r‰  rs  )?Ú__conditional_annotations__r3   Ú
__future__r   Údataclassesr   r   r   Útypingr   r   r   r	   rU   rÅ  r   r   r   r7   rR   rY   r`   rd   rj   rv   r€   r…   r‹   r’   r›   r¢   rª   r®   r¹   rG   r<   r    ÚRuleFnrÆ   rË   rÐ   rã   rô   rû   r  r  r  r.  r6  r>  rU  r`  rn  r|  r�  r¯  r°  r4   r¸  r³  rº  r¾  rÌ  )rÑ  s   @r   Ú<module>rÖ     s   øðôõ #ç 0Ñ 0ß 4Ó 4Û Û ð 2€õMð ÷ð ó ðð ÷ð ó ðô((õ
õ-õ6÷FõSõ
	ð1¸U÷ 1õ
@õ
Wõ
õõõ4õOð 
�3˜˜S�	 4¨¥9¨c°4Ð8¸$¸zÕ:JÐJÕ	K€õ	]õ.õ õ2.õð0 	ð ð
 
ð ð€õ0õfõ$Oð
 " 7¨K¸ÀIÈwÐW€õOõd7õt/õdõDõ:8õv6õrDðR Ø ØØØØ$Ø#ØØØØð€ˆó ð$ ˜à˜qØ�qØ˜2ð ! 'ð " 7ð€õ$Jõõð&ð
 ðð "ðð !÷ñ r   