+
    jHàjIV  ã                  ó>  € R t ^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	t	^ RI
t
^ RIt^ RIt^ RIHt ^ RIHtHtHtHt ^ RIHt ]P,                  ! R4      tRtRt^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RRRRRR/R R llt"RRRRRRRR/R R llt#R  R! lt$R" R# lt%R$ R% lt&]PN                  R& R' l4       t(R( t)R) R* lt*R+ R, lt+R- R. lt,R/ R0 lt-R1 R2 lt.R3 R4 lt/R5 R6 lt0R7 R8 lt1R9 R: lt2R; R< lt3R= R> lt4R? R@ lt5R# )Au  Host-scoped updateâ†’restart obligation for ``hermes update``.

Multiplex-only (Teknium ruling): exactly ONE ``hermes gateway run`` per host serves every
profile, so "this pull still owes the fleet a restart" is a property of the HOST, not of one
profile's ``HERMES_HOME``. The legacy ``$HERMES_HOME/fleet_restart_pending`` marker was
per-home: ``hermes -p coder update`` armed and cleared coder's copy while restarting the
SHARED process, and every other profile's CLI could neither see nor discharge that obligation
â€” it simply armed its own and re-killed the same host process.

The record therefore lives beside the host rendezvous record, in
:func:`gateway.host_rendezvous.host_state_dir` (``$HERMES_GATEWAY_LOCK_DIR`` else
``$XDG_STATE_HOME/hermes/gateway-locks``) â€” the one cross-profile, per-OS-user state root the
tree already has. It is written once per host and installation (keyed by the checkout), read by
every profile's CLI, and cleared once.

The same "one host process, not one per profile" identity is what
:func:`collapse_units_to_host_processes` applies to enumerated systemd units: leftover
per-profile ``hermes-gateway-<p>.service`` units on a multiplexed host all point at the same
live ``MainPID``, so restarting each one restarts the host process N times.
)ÚannotationsN)ÚPath)ÚAnyÚCallableÚIterableÚOptional)Úmake_user_dirzhermes_cli.update_cmdzhost-update-restart.jsonÚfleet_restart_pendingc               ó   € V ^8„  d   QhRR/# ©é   Úreturnr   © )Úformats   "ÚF/home/ubuntu/.hermes/hermes-agent/hermes_cli/update_host_obligation.pyÚ__annotate__r   3   s   € ÷ Uñ U˜dñ Uó    c                 ó’  € ^ RI Hp  \        P                  P	                  \        \        \        4      P                  4       P                  ^,          4      4      p\        P                  ! VP                  R4      4      P                  4       R,          p\        V ! 4       4      \        \        4      P                   RV R2,          # )aA  This installation's host obligation record.

Keyed by the checkout this module runs from: every profile of one install arms, reads and
clears the same record, while a second install of the same OS user (another checkout, a ZIP
install) owes its own, so its completed restart never erases this install's debt (review S3).
)Ú_host_state_dirúutf-8:Né   NÚ-z.json)Ú"hermes_cli.update_restart_recoveryr   ÚosÚpathÚnormcaseÚstrr   Ú__file__ÚresolveÚparentsÚhashlibÚsha256ÚencodeÚ	hexdigestÚHOST_OBLIGATION_NAMEÚstem)r   ÚinstallÚkeys      r   Úhost_obligation_pathr(   3   s‹   € õ Cä�g‰g×Ñœs¤4¬£>×#9Ñ#9Ó#;×#CÑ#CÀAÕ#FÓGÓH€GÜ
�.Š.˜Ÿ™¨Ó0Ó
1×
;Ñ
;Ó
=¸cÕ
B€CÜ‘Ó!Ó"¬Ô-AÓ(B×(GÑ(GÐ'HÈÈ#ÈÈeÐ%TÕTÐTr   c               ó   € V ^8„  d   QhRR/# r   r   )r   s   "r   r   r   D   s   € ÷ Sñ S�Dñ Sr   c                 óÜ   € \        4       p V P                  \        4      p\        P                  P                  V 4      '       g(   \        P                  P                  V4      '       d   V# T # )zQThe record a reader honours: this install's, else an older release's unkeyed one.)r(   Ú	with_namer$   r   r   Úexists)r   Úlegacys     r   Ú
_owed_pathr.   D   sH   € äÓ!€DØ�^‰^Ô0Ó1€FÜŸ™Ÿ™¨×-Ò-´"·'±'·.±.À×2HÒ2Hˆ6ÐRÈdÐRr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   ÚrecordÚdictr   ÚNoner   )r   s   "r   r   r   K   s&   € ÷ Zñ Z˜ð Z dð Z¨tñ Zr   c           	     ój   € \        V \        P                  ! V^RR7      P                  R4      4       R# )zQ``utils.atomic_json_write(path, record, mode=0o600)`` in stdlib only (see above).F©ÚindentÚensure_asciir   N)Úreplace_bytesÚjsonÚdumpsr"   )r   r0   s   &&r   Ú_write_recordr:   K   s%   € ä�$œŸ
š
 6°!À%ÔH×OÑOÐPWÓXÖYr   c               ó   € V ^8„  d   QhRR/# )r   r   úOptional[dict]r   )r   s   "r   r   r   P   s   € ÷ &ñ &˜nñ &r   c                 ó(   € \        \        4       4      # )zSThe published obligation record, or ``None`` when absent/corrupt/foreign-versioned.)Ú_read_recordr.   r   r   r   Úread_host_obligationr?   P   s   € äœ
›Ó%Ð%r   c               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   r<   r   )r   s   "r   r   r   U   s   € ÷ ñ �tð  ñ r   c                óò   €  \         P                  ! V P                  R R7      4      p\        T\        4      '       d   TP                  R4      \        8w  d   R# T#   \        \        \
        3 d     R# i ; i)z	utf-8-sig)ÚencodingNÚversion)
r8   ÚloadsÚ	read_textÚOSErrorÚUnicodeDecodeErrorÚ
ValueErrorÚ
isinstancer1   ÚgetÚ_RECORD_VERSION)r   Úpayloads   & r   r>   r>   U   sc   € ðÜ—*’*˜TŸ^™^°[˜^ÓAÓBˆô �gœt×$Ò$¨¯©°IÓ(>Ä/Ô(QÙØ€Nøô	 Ô'¬Ð4ô Úðús   ‚&A ÁA6Á5A6c               ó   € V ^8„  d   QhRR/# ©r   r   Úboolr   )r   s   "r   r   r   _   s   € ÷ 	ñ 	 ñ 	r   c                 óX   €  \        4       P                  4       #   \         d     R# i ; i)uè   True when the record FILE exists, parseable or not.

Fail-closed: a corrupt record is an obligation whose terms are unknown, never a discharged
one â€” the restart is still owed and the reader falls back to "no recorded inventory".
F)r.   Úis_filerF   r   r   r   Úhost_obligation_presentrR   _   s*   € ðÜ‹|×#Ñ#Ó%Ð%øÜô Úðús   ‚ š)¨)c               ó    € V ^8„  d   QhRRRR/# )r   Úfieldsr   r   r2   r   )r   s   "r   r   r   k   s   € ÷ Bñ B Cð B¨Dñ Br   c                 ó(   a € \        V 3R lR4       R# )zOMerge ``fields`` into the armed record (test/diagnostic surface). Never raises.c                ó&   <€ V P                  S4      # ©N)Úupdate)r0   rT   s   &€r   Ú<lambda>Ú'amend_host_obligation.<locals>.<lambda>m   s   ø€  &§-¡-°Ô"7r   ÚamendN©Ú_update_record)rT   s   lr   Úamend_host_obligationr^   k   s   ø€ äÔ7¸ÖAr   Úexpected_shaÚ ÚruntimesÚprofileÚownerc          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )	r   r_   r   ra   úOptional[list]rb   rc   r   rO   r   )r   s   "r   r   r   p   s4   € ÷ ñ ØðØ)7ðØILðØZ]ðà	ñr   c           
     óö   € \        4       p \        V4      ;_uu_ 4        \        W@WVR7      uuRRR4       #   + '       g   i     R# ; i  \         d"   p\        P                  RT4        Rp?R# Rp?ii ; i)a…  Arm the host obligation. True when it was written. Never raises.

Re-arming from a second profile for the SAME pulled SHA keeps the existing record (and its
``restarted`` proof) instead of resetting it: the host owes one restart, not one per profile.
``owner`` (an update run's commit-point token) joins the record's ``owners``: a run that
fails before its move hands back only its own stake (``release_host_obligation``), never
another run's debt for the same SHA. The first owner also stores what it found there
(``found``), which the last owner to leave puts back; an owner retargeting to another SHA
keeps that baseline (``_baseline_for``).
©r_   ra   rb   rc   Nz5Could not lock the host update-restart obligation: %sF)r(   Ú_record_mutexÚ_write_lockedrF   ÚloggerÚdebug)r_   ra   rb   rc   r   Úexcs   $$$$  r   Úwrite_host_obligationrm   p   sY   € ô  Ó!€DðÜ˜4× Õ Ü  È8ÐlqÔr÷ !× × Ó ûäô Ü�‰ÐLÈcÔRÝûðús1   ŒA Ÿ8­
A ¸A		ÁA Á	A ÁA8ÁA3Á3A8c               ó0   € V ^8„  d   QhRRRRRRRRRRR	R
/# )r   Úmarkerr   r_   r   ra   re   rb   rc   r   rO   r   )r   s   "r   r   r   †   s<   € ÷ 'ñ 'Øð'Ø#&ð'Ø7Eð'ØWZð'Øhkð'à	ñ'r   c          	     ó  € \        WW4R7      '       d   R# R\        P                  ! 4        2R\        P                  ! 4        2.V'       d   RV 2.M. ,           pVe1   VP	                  R\
        P                  ! R^R	V/4      ,           4       R
p \        V RP                  V4      R,           P                  R4      4       V'       g    \        4       '       d   RV  R2pV'       g"   \        P                  R\        4       V 4       R# \        P                  R\        4       V4       \!        R\"        P$                  R7       R#   \         d   pRT  RT R2p Rp?L�Rp?ii ; i  \         d   pRT  RT R2p Rp?LŸRp?ii ; i)aô  Arm the host record, else ``marker`` (the arming profile's per-home breadcrumb). Never raises.

The one fallback for every arm (``hermes update``'s commit point, a historical updater's
takeover). An unwritable host state dir (``HERMES_GATEWAY_LOCK_DIR`` on a read-only mount, a
container UID that does not own ``$HOME``) must never disarm the obligation (#117275), but the
marker is visible to the arming profile only: it settles the debt only on an install PROVEN to
have no other profile, and an unreadable profile roster proves nothing (review S2 residual).
Otherwise the marker is still left for this profile, the gap is said out loud, and the result
is False: the commit point refuses to move; a takeover, whose tree already moved, keeps the warning.
rg   Tzstarted=zpid=zexpected_sha=Nz
inventory=rC   ra   r`   Ú
r   z or z (Ú)z" hides it from the other profiles)zB may hide it from other profiles: the profile list is unreadable: zXHost update-restart obligation (%s) is unwritable; armed the per-home marker %s instead.zYCould not arm the update-restart obligation in %s%s; an interrupted update will not warn.zÇ  Warning: could not record the pending gateway-restart obligation where every profile sees it (state dir not writable) - restart gateways with `hermes gateway restart` if this update is interrupted.)ÚfileF)rm   Útimer   ÚgetpidÚappendr8   r9   r7   Újoinr"   rF   Ú_named_profiles_existrj   Úwarningr(   ÚerrorÚprintÚsysÚstderr)ro   r_   ra   rb   rc   ÚlinesÚhiddenrl   s   &$$$$   r   Úarm_host_obligationr€   †   sk  € ô ¨,ÐSZ×hÓhÙØœŸ	š	›�}Ð%¨¬b¯iªi«k¨]Ð';Ð<×dpÀ=ÐQ]ÐP^ÐA_Ñ@`ÐvxÕy€EØÒØ�‰�\¤D§J¢J°	¸1¸jÈ(Ð/SÓ$TÕTÔUØ€Fð)Ü�f˜tŸy™y¨Ó/°$Õ6×>Ñ>¸wÓGÔH÷ ð	kÜ$×&Ò&Ø˜f˜XÐ%GÐH�÷ Ü�‰ÐqÜ+Ó-¨vô	7áÜ
‡L�LÐlÜ%Ó'¨ô1ô 
ð ä"Ÿz™zõ+ñ øô% ô )Ø˜�x˜r #  aÐ(�ûð)ûô ô 	kØ˜&˜Ð!cÐdgÐchÐhiÐj�Fûð	kús0   Â1E ÃE( ÅE%Å	E Å E%Å(FÅ3	FÆFc               ó   € V ^8„  d   QhRR/# rN   r   )r   s   "r   r   r   °   s   € ÷ ñ ˜tñ r   c                 ó^  aa€ ^ RI Hp HpHpHp  \        V! 4       R,          P                  4       4      pT FÚ  oSP                  R8X  gV   TP                  SP                  4      '       d5   \        P                  ! \        S\        P                  4      4      '       g   Kk  \        ;QJ d     TT3R lT  4       F  '       g   K   RM	  RM! TT3R lT  4       4      pT'       g   K±  \        T! S4      \        P                   4      '       d   KÙ   R# 	  R#   \        \        3 d     R# i ; i)a}  True when the install has a live named profile beside ``default``, read from a COMPLETE roster.

``profiles.list_profile_names`` suppresses enumeration errors and reads an unreadable roster as
"default only"; here any error listing the roster or reading an entry's identity raises instead.
Same identity rule as ``hermes_constants.named_profile_is_live`` (stdlib only: see above).
)Ú_PROFILE_IDENTITY_MARKERSÚPROFILE_ID_REÚget_default_hermes_rootÚprofile_tombstone_pathÚprofilesFÚdefaultc              3  ó°   <"  € T FK  p\        SV,          \        P                  4      ;o;'       d    \        P                  ! S4      '       * x € KM  	  R # 5irW   )Ú_moder   ÚlstatÚstatÚS_ISDIR)Ú.0ÚnameÚhomeÚmodes   & €€r   Ú	<genexpr>Ú(_named_profiles_exist.<locals>.<genexpr>À   sD   øé € ð >Ù#<˜4ô !& d¨T¥k´2·8±8Ó <Ð<˜×XÐXÄdÇlÂlÐSWÓFXÔBXÔXÛ#<ùs
   ƒ.A²$AT)Úhermes_constantsrƒ   r„   r…   r†   ÚlistÚiterdirÚFileNotFoundErrorÚNotADirectoryErrorr�   ÚmatchrŒ   r�   rŠ   r   Úanyr‹   )rƒ   r„   r…   r†   ÚentriesÚidentityr�   r‘   s         @@r   rx   rx   °   sã   ù€ ÷ {ÓzðÜÑ/Ó1°JÕ>×GÑGÓIÓJˆó ˆØ�9‰9˜	Ô!¨×)<Ñ)<¸T¿Y¹Y×)GÒ)GÌtÏ|Ê|Ô\aÐbfÔhj×hoÑhoÓ\p×OqÒOqÙß“3õ >Ù#<ó>—3—3’3õ >Ù#<ó>ó >ˆç‰8œEÑ"8¸Ó">ÄÇÁ×IÔIÚñ ñ øô Ô1Ð2ô Úðús   �%D ÄD,Ä+D,c               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   Úprobez Callable[[Path], os.stat_result]r   Úintr   )r   s   "r   r   r   Ç   s"   € ÷ ñ �ð Ð=ð À#ñ r   c                óX   €  V! V 4      P                   #   \        \        3 d     ^ # i ; i)zb``probe(path).st_mode``; 0 when absent. Any other error raises: unreadable is unknown, not absent.)Úst_moder—   r˜   )r   rž   s   &&r   rŠ   rŠ   Ç   s/   € ðÙ�T‹{×"Ñ"Ð"øÜÔ1Ð2ô Úðús   ‚ ”)¨)c               ó0   € V ^8„  d   QhRRRRRRRRRRR	R
/# )r   r   r   r_   r   ra   re   rb   rc   r   rO   r   )r   s   "r   r   r   Ï   s9   € ÷ .ñ .˜ð .¨sð .¸nð .ÐWZð .Ðcfð .Ðkoñ .r   c               ó¤  € \        V 4      p V'       d   \        WEV 4      MR pTe�   T'       dˆ   TP                  R4      T8X  dr   / p\        T4      p	T'       d   YI9  d   . T	OTNTR&   T	'       g   YhR&   R^RT/p
Te   TP                  R4      T
8w  d   Y¨R&   T'       g   R	#  \        T / TCTC4       R	# R\        R\        P                  ! 4       R\        P                  ! 4       RT;'       g    RRT;'       g    R/pT'       d   T.TR&   YkR&   Te
   R^RT/TR&    \        T P                  R	R	R7       \        Y4       R	#   \         d"   p\        P	                  RT4        R p?R# R p?ii ; i  \         d"   p\        P	                  R
T4        R p?R# R p?ii ; i  \         d"   p\        P	                  RT4        R p?R# R p?ii ; i)Nz5Could not read the host update-restart obligation: %sFr_   ÚownersÚfoundrC   ra   Ú	inventoryTz2Could not amend host update-restart obligation: %sÚstartedÚpidÚarmed_by_profiler`   ©r   Úexist_okz2Could not write host update-restart obligation: %s)r>   Ú_baseline_forrF   rj   rk   rJ   Ú_ownersr:   Ú	ExceptionrK   rt   r   ru   r   Úparent)r   r_   ra   rb   rc   Úexistingr¥   rl   rT   r¤   r¦   rL   s   &$$$$       r   ri   ri   Ï   s½  € Ü˜DÓ!€Hðß8=”˜e¨tÔ4À4ˆð Ò§°·±¸nÓ1MÐQ]Ô1]ð "$ˆÜ˜Ó"ˆß�UÔ(Ø/ Ð/¨Ð/ˆF�8ÑßØ"'�w‘Ø  :¨xÐ8ˆ	ØÒ H§L¡L°Ó$=ÀÔ$JØ"+�;ÑßÙð	Ü˜$Ð 6 8Ð 6¨vÐ 6Ô7ñ à”?Ø”4—9’9“;ØŒr�yŠy‹{Ø˜GŸM˜M rØ˜×*Ð*¨ð€G÷ Ø"˜Gˆ�ÑØ �ÑØÒØ )¨1¨j¸(ÐCˆ�ÑðÜ�d—k‘k¨4¸$Õ?Ü�dÔ$ñ øôU ô Ü�‰ÐLÈcÔRÝûðûô( ô 	Ü�L‰LÐMÈsÔSÝûð	ûô& ô Ü�‰ÐIÈ3ÔOÝûðúsG   �E Â$E4 Ä #F# ÅE1ÅE,Å,E1Å4F Å?FÆF Æ#GÆ.G
Ç
Gc               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r   s   "r   r   r     s   € ÷ 
ñ 
˜ñ 
r   c              #  ó  "  € V P                   R\        \        4      P                   R2,          p\	        VRRR7       \        4       ! VR,          4      ;_uu_ 4        Rx € RRR4       R#   + '       g   i     R# ; i5i)a  Serialize every read-judge-write of the record across processes (``update_lock.marker_mutex``).

The sidecar sits in a subdirectory: the state dir is the gateway lock dir, whose ``--replace``
cleanup unlinks every top-level ``*.lock``, and a deleted sidecar splits the lock in two.
Ú.z.mutexTrª   r0   N)r¯   r   r$   r%   r   Ú_marker_mutex)r   Úguards   & r   rh   rh      sZ   é € ð �K‰K˜AœdÔ#7Ó8×=Ñ=Ð>¸fÐEÕE€EÜ�% °Õ5Ü	Œ˜ Õ)×	*Õ	*Û÷ 
+×	*×	*Ò	*üs   ‚ABÁA-Á#
BÁ-A>	Á8	Bc                 ó†  € ^ RI p ^ RIp^ RIHp \	        VR4      '       d   VP
                  # RpVP                  P                  V4      pVfj   V P                  P                  W2P                  4      pV P                  P                  V4      pWAP                  V&   VP                  P                  V4       VP
                  # )a#  ``update_lock.marker_mutex``. A historical updater imported ``update_lock`` before its pull and
then lazily imports this pulled module, so its in-memory copy predates the mutex: load the pulled
``update_lock`` (stdlib-only) beside it, which locks the same sidecar every current process does.N)Úupdate_lockÚmarker_mutexzhermes_cli._update_lock_pulled)Úimportlib.utilr|   Ú
hermes_clir·   Úhasattrr¸   ÚmodulesrJ   ÚutilÚspec_from_file_locationr   Úmodule_from_specÚloaderÚexec_module)Ú	importlibr|   r·   r�   ÚpulledÚspecs         r   r´   r´     sš   € ó Ûå&äˆ{˜N×+Ò+Ø×'Ñ'Ð'Ø+€DØ�[‰[�_‰_˜TÓ"€FØ‚~Ø�~‰~×5Ñ5°d×<PÑ<PÓQˆØ—‘×0Ñ0°Ó6ˆØ"�‰�DÑØ�‰×Ñ Ô'Ø×ÑÐr   c               ó    € V ^8„  d   QhRRRR/# )r   r0   r1   r   z	list[str]r   )r   s   "r   r   r   #  s   € ÷ Hñ H�Dð H˜Yñ Hr   c                ó”   € V P                  R 4      p\        V\        4      '       d   V Uu. uF  p\        V4      NK  	  up# . # u upi )r¤   )rJ   rI   r•   r   )r0   r¤   Úos   &  r   r­   r­   #  s=   € Ø�Z‰Z˜Ó!€FÜ'1°&¼$×'?Ò'?™FÓ#™F�qŒC�ŽF™FÑ#ÐGÀRÐGùÒ#s   ¬Ac               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   rc   r   r°   r<   r   r   r   úOptional[str]r   )r   s   "r   r   r   (  s.   € ÷ Dñ D˜ð D¨ð D¸dð DÀ}ñ Dr   c                óŠ  € \        T;'       g    / 4      pW9  d   \        V4      # V Uu. uF  qDV 8w  g   K  VNK  	  ppV'       g   T;'       g    / P                  R4      # \        P                  ! / T;'       g    / CRV/C^RR7      p\
        P                  ! VP                  R4      4      P                  R4      # u upi )a  What ``owner``'s release must put back (base64; ``None`` = absent): the record as it stood
before ``owner``'s first stake in it (review R1).

A run re-arms with one owner as it retargets (CP0 branch, origin pull, upstream fork ff), so
"the record already has owners" never means "nothing was here": a new owner keeps the bytes
there now, other owners' stakes included; an owner already in the record carries the baseline
it saved then, or, when it shared the record with others, the shared record minus its own stake.
r¥   r¤   Fr4   r   Úascii)	r­   Ú_found_fieldrJ   r8   r9   Úbase64Ú	b64encoder"   Údecode)rc   r°   r   r¤   rÇ   ÚothersÚshareds   &&&    r   r¬   r¬   (  s©   € ô �X—^�^ Ó$€FØÔÜ˜DÓ!Ð!ÙÓ.™�A¨¡:�aˆa™€FÐ.ßØ—�˜B×#Ñ# GÓ,Ð,Ü�ZŠZÐ>˜HŸN˜N¨Ð>¨X°vÑ>ÀqÐW\Ô]€FÜ×Ò˜FŸM™M¨'Ó2Ó3×:Ñ:¸7ÓCÐCùò	 /s
   «C ¸C c               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   rÉ   r   )r   s   "r   r   r   ;  s   € ÷ ñ �tð  ñ r   c                óŽ   €  \         P                  ! V P                  4       4      P                  R4      #   \         d     R# i ; i)z�The record's current bytes (base64), ``None`` when absent: what a first owner puts back.
Any other read error raises: custody is never guessed.rË   N)rÍ   rÎ   Ú
read_bytesrÏ   r—   )r   s   &r   rÌ   rÌ   ;  s<   € ðÜ×Ò §¡Ó 1Ó2×9Ñ9¸'ÓBÐBøÜô Úðús   ‚25 µAÁAc               ó    € V ^8„  d   QhRRRR/# )r   rc   r   r   r2   r   )r   s   "r   r   r   D  s   € ÷ %ñ % 3ð %¨4ñ %r   c                óê   € \        4       pV '       d$   T \        \        V4      ;'       g    / 4      9  d   R# \        V4      ;_uu_ 4        \	        W4       RRR4       R#   + '       g   i     R# ; i)a	  Hand back ``owner``'s stake in the record. When no other run still owes through it, the
record goes back to what its first owner found (``found``; absent = unlinked).

A record without ``owner`` was rewritten since (another run's newer pull): it is theirs and
stays. Raises OSError when the record cannot be rewritten (the caller keeps the debt armed).
Judged and rewritten under ``_record_mutex``, which every arm takes too, so no arm can land
between the look and the write and be undone by it (kshitijk4poor F22/N05).
N)r(   r­   r>   rh   Ú_release_locked)rc   r   s   & r   Úrelease_host_obligationrØ   D  sM   € ô  Ó!€Dß�E¤¬°dÓ);×)AÐ)A¸rÓ!BÔBÙÜ	�t×	Õ	Ü˜Ô$÷ 
×	×	Ò	ús   ÁA!Á!A2	c               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   rc   r   r   r2   r   )r   s   "r   r   r   T  s!   € ÷ #ñ #˜$ð # sð #¨tñ #r   c                ó¢  € \        V 4      pVe   \        V4      M. pW9  d   R # V Uu. uF  qDV8w  g   K  VNK  	  ppV'       d   \        V / VCRV/C4       R #  VP                  R4      e    \        P
                  ! VR,          RR7      MR pTf   T P                  RR7       R # \        Y4       R # u upi   \        \        3 d     R # i ; i)Nr¤   r¥   T)Úvalidate©Ú
missing_ok)
r>   r­   r:   rJ   rÍ   Ú	b64decodeÚ	TypeErrorrH   Úunlinkr7   )r   rc   r0   r¤   rÇ   Ú	remainingr¥   s   &&     r   r×   r×   T  sÃ   € Ü˜$Ó€FØ &Ò 2ŒW�VŒ_¸€FØÔÙÙ"Ó1™F�q¨5¡j—�™F€IÐ1ßÜ�dÐ;˜vÐ; x°Ñ;Ô<ÙðØDJÇJÁJÈwÓDWÒDc”× Ò  ¨¥¸4Õ@Ðimˆð ‚}Ø�‰˜tˆÖ$ä�dÖ"ùò 2øô ”zÐ"ô Úðús   ©B4¶B4Á4B9 Â9CÃCc               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   ÚdataÚbytesr   r2   r   )r   s   "r   r   r   g  s!   € ÷  ñ  ˜ð   Eð  ¨dñ  r   c                ó~  € \         P                  ! V P                  RV P                   R2RR7      w  r# \        P
                  ! VR4      ;_uu_ 4       pVP                  V4       VP                  4        \        P                  ! VP                  4       4       RRR4       \        P                  ! W04       \        P                   R8w  d_   \        P"                  ! T P                  \        P$                  4      p \        P                  ! T4       \        P&                  ! T4       R# R#   + '       g   i     L›; i  \         dW    \        P                  ! \        4      ;_uu_ 4        \        P                  ! T4       RRR4       h   + '       g   i     h ; ii ; i  \        P&                  ! T4       i ; i)z·``data`` at ``path`` as one durable record: a fresh 0600 ``mkstemp`` file (never written
through a planted alias), fsynced, renamed over ``path``, then the rename itself made durable.r³   Ú_z.tmp)ÚdirÚprefixÚsuffixÚwbNÚnt)ÚtempfileÚmkstempr¯   r%   r   ÚfdopenÚwriteÚflushÚfsyncÚfilenoÚreplaceÚBaseExceptionÚ
contextlibÚsuppressrF   rà   r�   ÚopenÚO_RDONLYÚclose)r   rã   ÚfdÚtmpÚhandleÚ	directorys   &&    r   r7   r7   g  s  € ô ×Ò 4§;¡;¸¸4¿9¹9¸+ÀQÐ7GÐPVÔW�G€Bð	Ü�YŠY�r˜4× Ô  FØ�L‰L˜ÔØ�L‰LŒNÜ�HŠH�V—]‘]“_Ô%÷ !ô 	�
Š
�3Ôô
 
‡w�w�$„Ü—G’G˜DŸK™K¬¯©Ó5ˆ	ð	 Ü�HŠH�YÔä�HŠH�YÖñ ÷ !× ûô
 ô Ü× Ò ¤×)Õ)Ü�IŠI�cŒN÷ *à÷ *Ö)àúðûô �HŠH�YÕúsH   µE  ÁAD-ÂE  Ã=F$ Ä-D=	Ä8E  Å -F!Å-FÆ	F!ÆFÆ	F!Æ$F<c               ó   € V ^8„  d   QhRR/# )r   r   r2   r   )r   s   "r   r   r   }  s   € ÷ Pñ P˜tñ Pr   c                 óš   €  \        4       P                  RR7       R#   \         d"   p \        P	                  RT 4        Rp ? R# Rp ? ii ; i)a!  Discharge the record this install's readers judged (``_owed_path``). Never raises.

That is this install's own record whenever it has one. An older release's unkeyed record names
no install: while this install has its own record, the unkeyed one may be another
installation's standing debt that nothing here judged, so it stays (review S3 residual). It goes
only when it was the record judged (this install armed none): it keeps its old meaning, owed by
whichever install reads it, and an upgrade's own interrupted debt must stay dischargeable.
TrÜ   z2Could not clear host update-restart obligation: %sN)r.   rà   rF   rj   rk   )rl   s    r   Úclear_host_obligationr   }  s?   € ðPÜ‹×Ñ tÐÖ,øÜô PÜ�‰ÐIÈ3×OÒOûðPús   ‚ žA
©AÁA
c               ó   € V ^8„  d   QhRR/# )r   r   zOptional[dict[str, str]]r   )r   s   "r   r   r   Œ  s   € ÷ ñ Ð3ñ r   c                 óÎ   € \        4       p V f   R# R\        V P                  R4      ;'       g    R4      /pV P                  R4      pVe   \        P                  ! V4      VR&   V# )a#  The obligation in the legacy ``key=value`` field shape, or ``None`` when unarmed.

Keeps one parser for both sources: the fields a reader needs (``expected_sha``, the
serialized ``inventory``) are identical whether they came from the host record or from an
in-flight legacy per-home marker.
Nr_   r`   r¦   )r?   r   rJ   r8   r9   )r0   rT   r¦   s      r   Úobligation_fieldsr  Œ  s`   € ô "Ó#€FØ‚~ÙØœc &§*¡*¨^Ó"<×"BÐ"BÀÓCÐD€FØ—
‘
˜;Ó'€IØÒÜ"Ÿjšj¨Ó3ˆˆ{ÑØ€Mr   c               ó    € V ^8„  d   QhRRRR/# )r   Úshar   r   r2   r   )r   s   "r   r   r   �  s   € ÷ 'ñ ' Sð '¨Tñ 'r   c                ó(   a € \        V 3R lR4       R# )zFRecord that the host process was restarted onto ``sha``. Never raises.c           	     ó”   <€ T P                  R S;'       g    RR\        P                  ! 4       R\        P                  ! 4       /R7      # )r  r`   r¨   Úat)Ú	restarted)rX   r   ru   rt   )r0   r  s   &€r   rY   Ú-mark_host_restart_completed.<locals>.<lambda>   s4   ø€ �v—}‘}°°s·y°y¸bÀ%ÌÏÊËÐVZÔ\`×\eÒ\eÓ\gÐ/h�}Ôir   zstamp restart completion onNr\   )r  s   fr   Úmark_host_restart_completedr  �  s   ø€ äÜiØ%ö'r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   ÚchangezCallable[[dict], None]Úwhatr   r   r2   r   )r   s   "r   r   r   ¤  s'   € ÷ Sñ SÐ1ð S¸ð SÀñ Sr   c                óR  € \        4       p\        V4      f   R#  \        V4      ;_uu_ 4        \        V4      pVf    RRR4       R# V ! V4       \        W#4       RRR4       R#   + '       g   i     R# ; i  \         d"   p\
        P                  RY4        Rp?R# Rp?ii ; i)z¨Read, ``change`` and rewrite an armed record under ``_record_mutex`` (an arm's new owner
stake is never overwritten by a stale copy). Never raises; no record, no write.Nz/Could not %s host update-restart obligation: %s)r.   r>   rh   r:   r®   rj   rk   )r  r  r   r0   rl   s   &&   r   r]   r]   ¤  s‰   € ô ‹<€DÜ�DÓÒ!ÙðSÜ˜4× Õ Ü! $Ó'ˆFØŠ~Ø÷ !Ñ ñ �6ŒNÜ˜$Ô'÷ !× × Ò ûô ô SÜ�‰ÐFÈ×RÒRûðSús?   ›A: ®A&¿A: Á	A&ÁA: Á&A7	Á1A: Á7A: Á:B&ÂB!Â!B&c               ó    € V ^8„  d   QhRRRR/# )r   r  rÉ   r   rO   r   )r   s   "r   r   r   µ  s   € ÷ Rñ R¨ð R¸$ñ Rr   c                óÒ   € \        4       pVe	   V '       g   R# VP                  R4      p\        V\        4      ;'       d(    \	        VP                  R4      ;'       g    R4      V 8H  # )a  True when THIS host obligation was already restarted onto ``sha``.

The guard that makes the catch-up restart idempotent per host: a second profile running
``hermes update`` must attach to the first restart's outcome, never kill the shared
multiplexer again.
Fr	  r  r`   )r?   rJ   rI   r1   r   )r  r0   r	  s   &  r   Úhost_restart_already_completedr  µ  sT   € ô "Ó#€FØ‚~ŸSÙØ—
‘
˜;Ó'€IÜ�i¤Ó&×QÐQ¬3¨y¯}©}¸UÓ/C×/IÐ/IÀrÓ+JÈcÑ+QÐQr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   ÚunitszIterable[str]Úmain_pidzCallable[[str], int]r   z tuple[list[str], dict[str, str]]r   )r   s   "r   r   r   Ã  s$   € ÷ ñ ØðØ$8ðà%ñr   c                ó  € . p/ p/ pV  Fi  p \        V! V4      ;'       g    ^ 4      pV^ 8:  d   VP                  V4       K9  VP                  V4      pVf   WTV&   VP                  V4       Ke  WsV&   Kk  	  W#3#   \         d    ^ p L`i ; i)a¬  Split enumerated units into ``(restart, {legacy_unit: covering_unit})``.

Units resolving to the same live ``MainPID`` are ONE host process; restarting each of them
restarts that process N times, which on a multiplexed host is an N-fold outage triggered by
leftover per-profile units. A unit with no readable main PID (inactive, unprivileged scope)
keeps its own restart: identity that cannot be proved is never collapsed away.
)rŸ   r®   rv   rJ   )r  r  ÚrestartÚcoveredÚowner_by_pidÚunitr¨   rc   s   &&      r   Ú collapse_units_to_host_processesr  Ã  s£   € ð €GØ €GØ#%€LÛˆð	Ü‘h˜t“n×)Ð)¨Ó*ˆCð
 �!Œ8Ø�N‰N˜4Ô ÙØ× Ñ  Ó%ˆØŠ=Ø $˜ÑØ�N‰N˜4Ö à!�D‹Mñ ð  ÐÐøô ô 	ð ŠCð	ús   �A9¡A9Á9B	ÂB	)6Ú__doc__Ú
__future__r   rÍ   rõ   r    r8   Úloggingr   rŒ   r|   rì   rt   Úpathlibr   Útypingr   r   r   r   Úhermes_dir_aclr   Ú	getLoggerrj   r$   ÚPROFILE_MARKER_NAMErK   r(   r.   r:   r?   r>   rR   r^   rm   r€   rx   rŠ   ri   Úcontextmanagerrh   r´   r­   r¬   rÌ   rØ   r×   r7   r   r  r  r]   r  r  r   r   r   Ú<module>r%     sA  ðñõ* #ã Û Û Û Û Û 	Û Û 
Û Û Ý ß 4Ó 4Ý (à	×	Ò	Ð2Ó	3€ð
 2Ð ð .Ð à€õUõ"SõZõ
&õ
õ	õBð
ØðØ:>ðØOQðØ`b÷ð,'Ø)+ð'ØHLð'Ø]_ð'Ønp÷'õTõ.õ.ðb ×Ñô
ó ð
òõ*Hõ
Dõ&õ%õ #õ& õ,Põõ"'õSõ"R÷r   