+
    jHàj®  ã                  óö   € R t ^ RIHt ^ RIt^ RIt^ RIt^ RIHt ^ RIH	t	 ^ RI
HtHtHt ]P                  ! R4      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R R ltR R ltR R ltR# )zêPlumbing shared by the kanban notifier and dispatcher loops.

Thread offload, board enumeration, live-config coercers and the singleton
dispatcher lock live here so the notifier, dispatcher and mixin modules read
them from one place.
)ÚannotationsN)ÚContext)ÚPath)ÚAnyÚCallableÚOptionalzgateway.runc               ó$   € V ^8„  d   QhRRRRRR/# ©é   ÚfunczCallable[..., Any]Úargsr   Úreturn© )Úformats   "ÚC/home/ubuntu/.hermes/hermes-agent/gateway/kanban_watchers_common.pyÚ__annotate__r      s"   € ÷ &ñ &Ð 2ð &¸cð &Àcñ &ó    c               ó:   € \        4       P                  ! V .VO5!  # )aS  Run *func* in an empty ``Context`` so request-local ContextVars stay behind.

``asyncio.to_thread`` copies the caller's context; a lingering
``delegate_task`` child marker would make ``write_txn`` false-trip for
these process-owned writers. An empty Context keeps the DB guard intact
for real children without exempting dispatcher writes.
)r   Úrun©r   r   s   "*r   Ú_run_in_fresh_contextr      s   € ô ‹9�=Š=˜Ð% Ó%Ð%r   c               ó$   € V ^8„  d   QhRRRRRR/# r	   r   )r   s   "r   r   r       s(   € ÷ Gñ GÐ+=ð GÈð GÐQTñ Gr   c             ‡  óX   "  € \         P                  ! \        V .VO5!  G Rj  x€L
 #  L5i)zMOffload blocking process-service work without inheriting request ContextVars.N)ÚasyncioÚ	to_threadr   r   s   "*r   Ú_to_thread_process_servicer       s$   é € ä×"Ò"Ô#8¸$ÐFÀÓF×FÐFÑFùs   ‚!*£(¤*c               ó    € V ^8„  d   QhRRRR/# ©r
   Úkbr   r   Úlistr   )r   s   "r   r   r   %   s   € ÷ :ñ :�Sð :˜Tñ :r   c                ó‚   €  V P                  RR7      #   \         d     T P                  T P                  4      .u # i ; i)zIEnumerate live boards; fall back to the default board when listing fails.F)Úinclude_archived)Úlist_boardsÚ	ExceptionÚread_board_metadataÚDEFAULT_BOARD)r   s   &r   Ú_list_boardsr&   %   sA   € ð:Ø�~‰~¨uˆ~Ó5Ð5øÜô :Ø×&Ñ& r×'7Ñ'7Ó8Ð9Ò9ð:ús   ‚ ”'>½>c               ó    € V ^8„  d   QhRRRR/# r   r   )r   s   "r   r   r   -   s   € ÷ Iñ I�Sð I˜Tñ Ir   c                ó†   € \        V 4       Uu. uF'  qP                  R 4      ;'       g    V P                  NK)  	  up# u upi )Úslug)r&   Úgetr%   )r   Úbs   & r   Ú_board_slugsr,   -   s8   € Ü7CÀBÔ7GÓHÑ7G°!�E‰E�&‹M×-Ð-˜R×-Ñ-Ò-Ñ7GÑHÐHùÒHs   Ž>ª>c               ó$   € V ^8„  d   QhRRRRRR/# )r
   Ú
kanban_cfgÚdictÚkeyÚstrr   zOptional[int]r   )r   s   "r   r   r   1   s!   € ÷ ñ  dð °ð ¸ñ r   c                ó  € V P                  V4      pVf   R#  \        V4      pT^8  d   \        P                  RY4       R# \        P                  RY4       T#   \        \        3 d    \        P                  RY4        R# i ; i)zXParse an optional ``kanban.<key>`` int cap; None when unset or invalid (< 1 is invalid).Nz1kanban dispatcher: invalid kanban.%s=%r; ignoringz4kanban dispatcher: kanban.%s=%r is below 1; ignoringzkanban dispatcher: %s=%d)r*   ÚintÚ	TypeErrorÚ
ValueErrorÚloggerÚwarningÚinfo)r.   r0   ÚrawÚvalues   &&  r   Ú_positive_int_settingr;   1   s}   € à
�.‰.˜Ó
€CØ
‚{ÙðÜ�C“ˆð ˆq„yÜ�‰ÐMÈsÔXÙÜ
‡K�KÐ*¨CÔ7Ø€Løô ”zÐ"ô Ü�‰ÐJÈCÔUÚðús   ™A Á'BÂBc               ó    € V ^8„  d   QhRRRR/# )r
   Úload_configzCallable[[], Any]r   ztuple[bool, int]r   )r   s   "r   r   r   B   s    € ÷ Dñ DÐ2Cð DÐHXñ Dr   c                óX  €  V ! 4       p\        T\        4      '       d   TP                  R/ 4      M/ p \	        TP                  R^4      ;'       g    ^4      p\        TP                  RR4      4      \        T^4      3#   \          d    Ru # i ; i  \
        \        3 d    ^p LOi ; i)u’  Live (enabled, per_tick) auto-decompose settings, re-read every dispatcher tick.

Fails safe: a config read error returns ``(False, 3)`` rather than
re-enabling a feature the user turned off. ``per_tick`` is clamped to ``>= 1``.

Read fresh from config on every dispatcher tick (#49638) so that flipping ``kanban.auto_decompose:
false`` to STOP runaway fan-out takes effect on the next tick instead of requiring a gateway restart.
Auto-decompose is a safety toggle â€” a user who sees it create and launch tasks they didn't intend
reaches for this flag to halt it, and a stale boot-captured value silently ignoring that change is the
bug reported in #49638.
ÚkanbanÚauto_decompose_per_tickÚauto_decomposeT)Fé   )	r#   Ú
isinstancer/   r*   r3   r4   r5   ÚboolÚmax)r=   ÚcfgÚkcfgÚper_ticks   &   r   Ú _resolve_auto_decompose_settingsrI   B   s¦   € ðÙ‹mˆô %/¨s´D×$9Ò$9ˆ3�7‰7�8˜RÔ ¸r€DðÜ�t—x‘xÐ 9¸1Ó=×BÐBÀÓCˆô �—‘Ð)¨4Ó0Ó1´3°xÀÓ3CÐCÐCøô ô ØŠðûô
 ”zÐ"ô ØŠðús(   ‚B  ´B ÁB Â BÂBÂB)Â(B)c               ó   € V ^8„  d   QhRR/# )r
   r   r3   r   )r   s   "r   r   r   Z   s   € ÷ ñ ˜Cñ r   c                 óº   €  ^ RI Hp  \        V ! 4       ;'       g    / P                  R4      ;'       g    / P                  R^4      4      #   \         d     ^# i ; i)za``kanban.done_sub_retention_days`` (default 30; 0 disables), re-read per sweep; fails safe to 30.©r=   r?   Údone_sub_retention_days)Úhermes_cli.configr=   r3   r*   r#   rL   s    r   Ú_gc_retention_daysrO   Z   sS   € ðÝ1ä‘[“]×(Ð( b×-Ñ-¨hÓ7×=Ð=¸2×BÑBÐC\Ð^`ÓaÓbÐbøÜô Úðús   ‚A ›A ´A ÁAÁAc               ó   € V ^8„  d   QhRR/# )r
   r   rD   r   )r   s   "r   r   r   d   s   € ÷ 
.ñ 
. $ñ 
.r   c                 óX   €  ^ RI Hp  T ! R\        4      '       * #   \         d     R# i ; i)z×False while the global emergency stop (`hermes pause`) is engaged.

Checked every tick before spawning, so a pause applies on the next tick;
in-flight workers are never touched. Fails open if estop is unimportable.
©Úcheck_pausedTr?   )Úagent.estoprS   ÚImportErrorr6   rR   s    r   Ú_kanban_dispatch_allowedrV   d   s0   € ðÝ,ñ ˜H¤fÓ-Ô-Ð-øô ô Úðús   ‚ š)¨)c               ó   € V ^8„  d   QhRR/# )r
   r   ztuple[Optional[object], str]r   )r   s   "r   r   r   q   s   € ÷ ñ Ð*Fñ r   c                ó   €  ^ RI Hp  \        T 4      P                  P                  RRR7       \        \        T 4      RRR7      pT! T4      '       g   TP                  4        R	# TR3#   \         d    Ru # i ; i  \         d    Ru # i ; i)
am  Take the exclusive, non-blocking advisory lock for the sole dispatcher.

Only one gateway machine-wide may run the embedded dispatcher: concurrent
dispatchers double reclaim frequency and claim events, and with
``wal_autocheckpoint=0`` concurrent manual checkpoints can corrupt index
pages. ``dispatch_in_gateway`` is the primary control; this is the backstop.

Returns ``(handle, "held")`` (release via :func:`_release_singleton_lock`),
``(None, "contended")`` when another process holds it (caller must NOT
dispatch), or ``(None, "unavailable")`` when locking cannot be performed
(caller falls back to config control).
)Ú_try_acquire_file_lockT)ÚparentsÚexist_okza+zutf-8)ÚencodingÚheld)NÚunavailable)NÚ	contended)
Úgateway.statusrY   rU   r   ÚparentÚmkdirÚopenr1   ÚOSErrorÚclose)Ú	lock_pathrY   Úhandles   &  r   Ú_acquire_singleton_lockrh   q   s‘   € ð#Ý9ð#ÜˆY‹×Ñ×$Ñ$¨T¸DÐ$ÔAÜ”c˜)“n d°WÔ=ˆñ " &×)Ò)Ø�‰ŒØ Ð Ø�6ˆ>Ðøô ô #Ø"Ò"ð#ûô
 ô #Ø"Ò"ð#ús"   ‚A* ‰=A= Á*A:Á9A:Á=BÂBc               ó   € V ^8„  d   QhRR/# )r
   r   ÚNoner   )r   s   "r   r   r   �   s   € ÷ 	ñ 	 tñ 	r   c                óH  € V f   R# \         P                  ! \        4      ;_uu_ 4        ^ RIHp V! V 4       RRR4       \         P                  ! \        4      ;_uu_ 4        V P                  4        RRR4       R#   + '       g   i     LM; i  + '       g   i     R# ; i)z<Release a lock acquired via :func:`_acquire_singleton_lock`.N)Ú_release_file_lock)Ú
contextlibÚsuppressr#   r`   rl   re   )rg   rl   s   & r   Ú_release_singleton_lockro   �   sd   € à‚~ÙÜ	×	Ò	œY×	'Õ	'Ý5á˜6Ô"÷ 
(ô 
×	Ò	œY×	'Õ	'Ø�‰Œ÷ 
(Ñ	'÷	 
(×	'ú÷ 
(×	'Ð	'ús   ©A=Á"BÁ=B	ÂB!	)Ú__doc__Ú
__future__r   r   rm   ÚloggingÚcontextvarsr   Úpathlibr   Útypingr   r   r   Ú	getLoggerr6   r   r   r&   r,   r;   rI   rO   rV   rh   ro   r   r   r   Ú<module>rw      sk   ðñõ #ã Û Û Ý Ý ß *Ñ *ð 
×	Ò	˜=Ó	)€õ&õGõ
:õIõõ"Dõ0õ
.õ÷8	r   