+
    jHàjã&  ã                   ó¤   € R t ^ RIHt ^ RIt^ RIHt ^ RIt^ RIt^ RItR t	R t
R R ltR tR	 R
 ltR tR R ltRR/R R lltR R ltR tR# )zBRead-only canonical admission fences for dispatcher reclaim paths.)ÚclosingN)ÚPathc                ó  € V P                  RV34      P                  4       pVf   R#  \        P                  ! VR,          4      p\	        \
        P                  ! \        VR,          4      P                  4       R,           RR7      4      ;_uu_ 4       pVP                  R	VR
,          VR,          34      P                  4       pRRR4       Xe   V^ ,          R8X  d   V^,          R8H  # Ve   V^ ,          R9   d   R# ^ RI	H
p V! \        P                  ! VR,          4      4      VR,          8g  #   + '       g   i     Lu; i  \        \        \        \
        P                   3 d     R# \        P                    d     R# i ; i)zAMissing/ambiguous owner receipts never authorize another attempt.z¤SELECT e.payload FROM task_events e JOIN tasks t ON t.current_run_id=e.run_id WHERE t.id=? AND e.task_id=t.id AND e.kind='owner_admitted' ORDER BY e.id DESC LIMIT 1NFÚpayloadÚdbz?mode=roT)ÚurizXSELECT status,outcome FROM session_admissions WHERE target_session_id=? AND request_id=?Ú
session_idÚ
request_idÚterminalÚinterrupted©Úprocess_birthÚpidÚbirth>   ÚqueuedÚunknown)ÚexecuteÚfetchoneÚjsonÚloadsr   Úsqlite3Úconnectr   Úas_uriÚhermes_cli.process_identityr   ÚpsutilÚProcessÚOSErrorÚ
ValueErrorÚKeyErrorÚError)ÚconnÚtask_idÚmarkerÚbindingÚownerÚrowr   s   &&     ÚE/home/ubuntu/.hermes/hermes-agent/hermes_cli/kanban_owner_recovery.pyÚowner_reclaim_pausedr'   
   sC  € à�\‰\ð	aà	ˆ
ó÷ ‘H“Jð ð ‚~ÙðÜ—*’*˜V IÕ.Ó/ˆÜ”W—_’_¤T¨'°$­-Ó%8×%?Ñ%?Ó%AÀJÕ%NÐTXÔY×ZÔZÐ^cØ—-‘-Ð zØ!(¨Õ!6¸ÀÕ8MÐ NóPßPXÑPXÓPZð ÷ [ð Š?˜s 1�v¨Ô3à�q•6˜]Ñ*Ð*ØŠ;˜#˜a�&Ð$9Ô9ÙÝ=ÙœVŸ^š^¨G°E­NÓ;Ó<ÀÈÕ@PÑPÐP÷ [×Zûô ”Z¤¬7¯=©=Ð9ô ÚÜ�<‰<ô ÚðúsB   ©A+E Â1D2Ã%E Ã+E Ã?2E Ä2E	Ä=E Å%FÅ-FÆ FÆFc                óú   € V P                  RW34      P                  4       p V'       d   \        P                  ! VR,          4      M/ p\        T\        4      '       d   T# / #   \        \
        3 d    / u # i ; i)zÕThe newest ``kind`` payload on the task's CURRENT run, or ``{}``.

A payload that cannot be read is not a payload that says the attempt is over, so
callers treat ``{}`` as "no evidence" rather than as an absence.
ú•SELECT e.payload FROM task_events e JOIN tasks t ON t.current_run_id=e.run_id WHERE t.id=? AND e.task_id=t.id AND e.kind=? ORDER BY e.id DESC LIMIT 1r   )r   r   r   r   r   Ú	TypeErrorÚ
isinstanceÚdict)r    r!   Úkindr%   r   s   &&&  r&   Ú_latest_run_eventr.   $   sx   € ð �,‰,ð	Rà	ˆó÷ "™›ð ðß03”$—*’*˜S �^Ô,¸ˆô ! ¬$×/Ò/ˆ7Ð7°RÐ7øô œ	Ð"ô ØŠ	ðús   £'A$ Á$A:Á9A:c                ó$   € V ^8„  d   QhR\         /# ©é   Úreturn©Úbool)Úformats   "r&   Ú__annotate__r6   5   s   € ÷ Bñ Bœdñ Bó    c                ó„   € ^ RI Hp \        V 4      ;'       d)    \        V 4      P	                  VP                  4       4      # )uJ   True when ``lock`` was issued by this host â€” the only pids we can judge.©Ú	kanban_db)Ú
hermes_clir:   r4   ÚstrÚ
startswithÚ_host_prefix)ÚlockÚkbs   & r&   Ú_host_local_lockrA   5   s-   € å*Ü�‹:×AÐAœ#˜d›)×.Ñ.¨r¯©Ó/@ÓAÐAr7   c                ó’   € \        WR4      pV'       g   R# ^ RIHp V! VP                  R4      VP                  R4      4      '       * # )zRTrue when the owner bound an interpreter to the task's current run and it is dead.Úworker_boundF©Ú_worker_aliver   Ú
started_at)r.   Úhermes_cli.kanban_db_dispatchrE   Úget)r    r!   ÚboundrE   s   &&  r&   Úbound_interpreter_gonerJ   ;   s9   € ä˜d¨^Ó<€EßÙÝ;Ù˜UŸY™Y uÓ-¨u¯y©y¸Ó/FÓGÔGÐGr7   c                ó$   € V ^8„  d   QhR\         /# r0   r3   )r5   s   "r&   r6   r6   D   s   € ÷ 2ñ 2¤Dñ 2r7   c                óŽ  €  \        V R,          4      p^ RIHp TP                  T4      '       g   R#  ^ RIHp T! \        P                  ! T4      4      pT P                  R4      pTRJ;'       d    YE8g  #   \        \        \        3 d     R# i ; i  \        P                   d     R# \        P                   d     R# i ; i)aÆ  True when the receipt's own pid is gone, or its number now belongs to another process.

The identity rule ``owner_reclaim_paused`` already applies: a live pid whose ``birth``
differs is a recycled number, not the owner. A zombie keeps its birth until reaped but has
already exited, so liveness goes through the dispatcher's zombie-aware ``_pid_alive``. A live pid we cannot compare (no ``birth``
recorded) and an unreadable one are never proof of death.
r   Fr9   Tr   r   N)Úintr   r*   r   r;   r:   Ú
_pid_aliver   r   r   r   ÚNoSuchProcessr   rH   )Úreceiptr   r@   r   Úcurrentr   s   &     r&   Ú_owner_process_gonerR   D   s°   € ðÜ�'˜%•.Ó!ˆõ +Ø�=‰=˜×ÒÙðÝ=Ù¤§¢¨sÓ 3Ó4ˆð
 �K‰K˜Ó €EØ˜Ð×1Ð1 Ñ!1Ð1øô ”i¤Ð,ô Úðûô ×Ñô ÚÜ�<‰<ô Úðús.   ‚A7 ´"B Á7BÂBÂCÂ,CÂ?CÃCc                ó  € VR,          VR,          3.pR F@  p\        WV4      pVP                  VP                  R4      VP                  R4      34       KB  	  V UUu. uF  w  rgV'       g   K  \        V4      V3NK  	  upp# u uppi )zQEvery worker pid recorded for the task's current run, with its spawn fingerprint.Ú
worker_pidÚworker_started_atr   rF   )ÚspawnedrC   )r.   ÚappendrH   rM   )r    Útask_rowr!   Úrecordedr-   r   r   Úfingerprints   &&&     r&   Ú_run_worker_pidsr[   ^   s   € à˜,Õ'¨Ð2EÕ)FÐGÐH€HÛ+ˆÜ# D°4Ó8ˆØ�‰˜Ÿ™ UÓ+¨W¯[©[¸Ó-FÐGÖHñ ,ñ =EÔL¹HÑ(8¨ÌÔ#ŒS�‹X�{Ó#¹HÒLÐLùÓLs   ÁBÁ0Bc                ó$   € V ^8„  d   QhR\         /# r0   r3   )r5   s   "r&   r6   r6   g   s   € ÷ )Qñ )Q´$ñ )Qr7   c                ó¨  a€ ^ RI Hp ^ RIHo \	        WR4      '       d   R# V P                  RV34      P                  4       pVe   \        VR,          4      '       g   R# \        \        P                  ! 4       4      pVP                  VR,          4      pVe   WT8¼  d   R# VP                  VR,          4      pVe   WF,
          VP                  8:  d   R# \        \	        WR	4      4      '       g   R# \        WV4      pV'       g   R# \        ;QJ d)    V3R
 lV 4       F  '       g   K   R'       * # 	  R'       * # ! V3R
 lV 4       4      '       * # )už  True when an unresolved owner receipt cannot still be executing this attempt.

``owner_reclaim_paused`` is about effects: an admission that may still be in flight is
never repeated. It must not also mean the run keeps its claim forever. The attempts it
pauses in practice are ones whose owner was killed (restart, OOM), and such a run may
never have written a ``worker_bound`` event at all, so ``bound_interpreter_gone`` cannot
speak for it â€” 76 ``owner_admitted`` against 74 ``worker_bound`` rows on one board means
runs in exactly that shape. This asks the question that matters â€” can anything still be
executing this attempt? â€” from evidence the board already keeps:

  * the claim is host-local and EXPIRED (a live claim is the owner still working);
  * no heartbeat inside ``DEFAULT_CLAIM_HEARTBEAT_MAX_STALE_SECONDS``;
  * the receipt's own process is gone or recycled;
  * every worker pid recorded for the run (``tasks.worker_pid``, its ``spawned`` and
    ``worker_bound`` events) is dead or recycled.

Missing evidence is never proof: no recorded worker pid, a live pid whose identity
cannot be compared, or an unreadable clock all return False and the pause holds.
r9   rD   rC   FzhSELECT claim_lock, claim_expires, worker_pid, worker_started_at, last_heartbeat_at FROM tasks WHERE id=?Ú
claim_lockÚclaim_expiresÚlast_heartbeat_atÚowner_admittedc              3   ó8   <"  € T F  w  rS! W4      x € K  	  R # 5i)N© )Ú.0r   rZ   rE   s   &  €r&   Ú	<genexpr>Ú.owner_attempt_provably_over.<locals>.<genexpr>�   s   øé € ÐPÉ4Ñ7G°s‘= ×2Ð2Ë4ùs   ƒT)r;   r:   rG   rE   r.   r   r   rA   rM   ÚtimeÚ	_to_epochÚ)DEFAULT_CLAIM_HEARTBEAT_MAX_STALE_SECONDSrR   r[   Úany)	r    r!   r@   r%   ÚnowÚexpiresÚ	heartbeatÚpidsrE   s	   &&      @r&   Úowner_attempt_provably_overro   g   s  ø€ õ( +Ý;Ü˜¨×7Ò7ÙØ
�,‰,ð	 Ø") ó-ç-5©X«Zð ð ‚{Ô*¨3¨|Õ+<×=Ò=ÙÜ
Œd�iŠi‹kÓ
€CØ�l‰l˜3˜Õ/Ó0€GØ‚˜'œ.ÙØ—‘˜SÐ!4Õ5Ó6€IØÒ ¥°B×4`Ñ4`Ô!`ÙÜÔ0°Ð@PÓQ×RÒRÙÜ˜D wÓ/€DßÙß‹sÔPÉ4ÓP�sŒsÔPÐPŠsÔPÐPˆsÔPÉ4ÓPÓPÔPÐPr7   Úcrash_sweepFc                ó$   € V ^8„  d   QhR\         /# r0   r3   )r5   s   "r&   r6   r6   “   s   € ÷ :ñ :Ä$ñ :r7   c               ó~   € \        W4      '       g   R# V'       d   \        W4      '       d   R# \        W4      '       * # )a�  True when a reclaim path must leave this task alone.

The pause protects the effects of an attempt that may still be in flight, and stops
holding the claim as soon as nothing can still be executing it. A run whose bound
interpreter is dead stays the crash sweep's alone (``crash_sweep=True``), so it is booked
``crashed`` and re-queued once rather than ``reclaimed`` by whichever path ticks first. A
run that never bound one is released by any path once ``owner_attempt_provably_over``:
the shape that used to be unreclaimable forever, because ``bound_interpreter_gone`` was the
only override and it needs the ``worker_bound`` event that run never wrote.
F)r'   rJ   ro   )r    r!   rp   s   &&$r&   Úowner_reclaim_blockedrs   “   s1   € ô   ×.Ò.ÙßÔ-¨d×<Ò<ÙÜ*¨4Ó9Ô9Ð9r7   c                ó0   € V ^8„  d   QhR\         R\         /# )r1   Ú	signalledr2   r3   )r5   s   "r&   r6   r6   ¥   s   € ÷ 
Gñ 
GÄ$ð 
GÌ4ñ 
Gr7   c               ó   € \        WVR7      # )a`  The under-lock re-check for a reclaim path, after its termination step.

When this host just signalled the run's worker (``signalled``), killing it takes its bound
interpreter down too (``_watch_submitter``) and the owner settles the admission ``unknown``:
a pause this path caused itself, which would leave a timed-out run for the crash sweep to
misbook ``crashed``. Only an interpreter still LIVE (one that bound after the first look)
keeps the claim. Without a local signal (a foreign-host claim) the bound interpreter's pid
means nothing here, so a "dead" reading is no proof and the plain gate applies.
)rp   )rs   )r    r!   ru   s   &&$r&   Ú owner_reclaim_blocked_after_killrw   ¥   s   € ô ! ¸IÔFÐFr7   c                ó
  a a€ V V3R lpV! R4      V! R4      reV0VP                  R4      V8X  d   VP                  R4      .M. mpVP                  R4      V8X  d(   VP                  R4      V9   d   VP                  R4      # R# )uN  The exit code a managed run's authority recorded for the task's CURRENT run, else None.

Managed interpreters are children of the authority, not the dispatcher, so their result is
read from the run's ``worker_result`` event (it survives a dispatcher restart and cannot be
reaped here). It speaks for the run only under the same claim and from a pid of this run:
the dispatcher's own worker, or the owner-side interpreter ``worker_bound`` recorded under
that claim â€” ``session_kanban._record_worker_result`` writes the interpreter's pid, never the
spawned submitter's ``tasks.worker_pid``.c                 óò   <€ SP                  R SV 34      P                  4       p V'       d   \        P                  ! VR,          4      M/ p\        T\        4      '       d   T# / #   \         d    / u # i ; i)r)   r   )r   r   r   r   r   r+   r,   )r-   r%   r   r    r!   s   &  €€r&   ÚlatestÚ%managed_run_exit_code.<locals>.latest»   sw   ø€ Ø�l‰lðVØX_ÐaeÐWfóhçhpÑhpÓhrð 	ð	ß47”d—j’j  Y¥Ô0¸RˆGô % W¬d×3Ò3ˆwÐ;¸Ð;øô ô 	ØŠIð	ús   ¥A& ­A& Á&A6Á5A6Úworker_resultrC   r^   r   Ú	exit_codeN)rH   )r    r!   rT   r^   rz   ÚresultrI   rn   s   ff&&    r&   Úmanaged_run_exit_coder   ²   s�   ù€ ö<ñ ˜?Ó+©V°NÓ-CˆEØÐ_°·	±	¸,Ó0GÈ:Ô0U˜5Ÿ9™9 UÓ+Ñ,Ð[]Ð_€DØ&,§j¡j°Ó&>À*Ô&LÐQW×Q[ÑQ[Ð\aÓQbÐfjÔQjˆ6�:‰:�kÓ"ÐtÐptÐtr7   )Ú__doc__Ú
contextlibr   r   Úpathlibr   r   rg   r   r'   r.   rA   rJ   rR   r[   ro   rs   rw   r   rc   r7   r&   Ú<module>rƒ      sZ   ðÙ HÝ Û Ý Û Û Û òò48õ"BòHõ2ò4Mõ)QðX:¸÷ :õ$
Gôur7   