+
    …WÝjµ/  ã                  ó€  a € 0 t $ R t^ RIHt ^ RIt^ RIHt ^ RIt^ RIt^ RI	t	^ RI
t
^ RIt^ RIHtHtHt ^ RIHt ^ RIHtHt ^t]P*                  ! RRR7      tR	]R
&   ]P*                  ! RRR7      tR]R&   ]P*                  ! RRR7      tR]R&   ]P*                  ! RRR7      tR	]R&   ]R R l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"RAR# R$ llt#RAR% R& llt$R' R( lt%R) R* lt&R+ R, lt'R- R. lt(RAR/ R0 llt)R1 R2 lt*RBR3 R4 llt+R5R6/R7 R8 llt,R9 R: lt-R; R< lt.R= R> lt/R? R@ lt0R# )Cus  pm sync receipts: the machine-readable surface for venv operations.

Every pm venv sync â€” startup, plugin install, update rebuild â€” writes a
receipt with the SAME schema the updater's receipts use
(hermes_cli.update_receipt), into the same
``<HERMES_HOME>/logs/update_receipts/`` dir with a ``kind`` field
separating kinds. One reader (``hermes pm status``, desktop IPC) serves
every surface: a failed venv rebuild is as reportable as a failed
update.

The in-flight receipt lives in a ContextVar, not a module global: the
sync cadence and dependency preparation can overlap across threads, and a
shared global lets one run's begin/finalize clobber another's record.

Two hazards of ContextVar state are handled explicitly:
- Copied contexts (``contextvars.copy_context()``, asyncio.to_thread)
  share the SAME dict object â€” so every record_* COPY-ON-WRITES: it
  deep-copies, mutates the copy, and re-sets it in the current context.
  A child task records into its own copy; the parent's receipt is
  untouched.
- A nested ``begin`` in the same context would silently drop the outer
  receipt. ``begin`` therefore returns the ContextVar token; passing it
  to ``finalize(..., token=...)`` restores the OUTER receipt instead of
  discarding it. The ambient no-token beginâ†’finalize stays as-is (the
  existing linear consumers â€” pm sync, the plugin-check cadence).

Correlation: ``begin`` stamps the receipt with the ambient update
correlation id (derived from the open update receipt's own identity in
the same context). ``finalize`` files the finished receipt under that
id in a per-context map, and the updater embeds only the entry carrying
ITS OWN id (``last_for_update``), never latest.json â€” a sync that
finished before the update began, a standalone sync, or one from a
concurrent thread cannot be misattributed to it, and a nested update's
sync never displaces the outer update's entry.
)ÚannotationsN)Úcontextmanager)ÚdatetimeÚtimezoneÚUTC)ÚPath)ÚAnyÚOptionalÚpm_receipt_current)Údefaultz0contextvars.ContextVar[Optional[dict[str, Any]]]Ú_currentÚpm_receipt_completed_by_updatez;contextvars.ContextVar[Optional[dict[str, dict[str, Any]]]]Ú_completed_by_updateÚpm_worker_updatez3contextvars.ContextVar[tuple[Optional[str]] | None]Ú_worker_updateÚpm_last_completedÚ_last_completedc               ó   € V ^8„  d   QhRR/# )é   Ú	update_idúOptional[str]© )Úformats   "Ú>C:\Users\johnn\AppData\Local\hermes\hermes-agent\pm\receipt.pyÚ__annotate__r   N   s   € ÷ $ñ $˜mñ $ó    c              #  ó   "  € \         P                  V 34      p\        P                  R4      p Rx € \        P                  V4       \         P                  V4       R#   \        P                  T4       \         P                  T4       i ; i5i)zGCarry correlation across the worker seam without consulting disk state.N)r   Úsetr   Úreset)r   ÚtokenÚ	completeds   &  r   Úworker_contextr!   M   sl   é € ô ×Ñ 	˜|Ó,€EÜ×#Ñ# DÓ)€Ið$Ûä×Ñ˜iÔ(Ü×Ñ˜UÖ#øô 	×Ñ˜iÔ(Ü×Ñ˜UÕ#üs   ‚,B¯A ³,BÁ,BÂBc               ó   € V ^8„  d   QhRR/# ©r   ÚreturnúOptional[dict[str, Any]]r   )r   s   "r   r   r   Y   s   € ÷ 0ñ 0Ð0ñ 0r   c                 óR   € \         P                  ! \        P                  4       4      # ©N)ÚcopyÚdeepcopyr   Úgetr   r   r   Úlast_completedr+   Y   s   € Ü�=Š=œ×,Ñ,Ó.Ó/Ð/r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Údatar%   r   r   r$   ÚNoner   )r   s   "r   r   r   ]   s#   € ÷ ,ñ ,Ð 8ð ,À]ð ,ÐW[ñ ,r   c                ó  € V f   R # V P                  R4      V8w  d   \        R4      hV'       dW   \        \        P                  4       ;'       g    / 4      p\        P
                  ! V 4      W!&   \        P                  V4       R # R # )Nr   z&PM worker receipt correlation mismatch)r*   Ú
ValueErrorÚdictr   r(   r)   r   )r-   r   r    s   && r   Úaccept_worker_receiptr2   ]   sj   € Ø‚|ÙØ‡x�x�Ó 	Ô)ÜÐAÓBÐBßÜÔ-×1Ñ1Ó3×9Ð9°rÓ:ˆ	Ü#Ÿ}š}¨TÓ2ˆ	ÑÜ× Ñ  Ö+ñ r   c               ó   € V ^8„  d   QhRR/# )r   r$   r   r   )r   s   "r   r   r   h   s   € ÷ ñ ˜Mñ r   c                 ó„   € \         P                  4       p V e
   V ^ ,          #  ^ RIHp V! 4       #   \         d     R# i ; i)zÁThe update correlation id in force in this context, or None.

Lazy import: hermes_cli.update_receipt imports pm.receipt at embed
time, so this direction must stay function-scoped. Never raises.N)Úcurrent_correlation_id)r   r*   Úhermes_cli.update_receiptr5   Ú	Exception)Úworkerr5   s     r   Ú_ambient_update_idr9   h   sE   € ô
 ×ÑÓ!€FØÒØ�a�yÐðÝDá%Ó'Ð'øÜô Úðús   £0 °?¾?c               ó   € V ^8„  d   QhRR/# )r   r$   Ústrr   )r   s   "r   r   r   x   s   € ÷ )ñ )�cñ )r   c                 óR   € \         P                  ! \        4      P                  4       # r'   )r   Únowr   Ú	isoformatr   r   r   Ú_utc_now_isor?   x   s   € Ü�<Š<œÓ×&Ñ&Ó(Ð(r   c               ó   € V ^8„  d   QhRR/# )r   r$   r   r   )r   s   "r   r   r   |   s   € ÷ :ñ :�dñ :r   c                 ó8   € ^ RI Hp  V ! 4       R,          R,          # )um   The receipts dir â€” a pure path computation, NO mkdir side
effect: readers (latest()) must not create state.©Úget_hermes_homeÚlogsÚupdate_receipts)Úhermes_constantsrC   rB   s    r   Ú_receipt_dirrG   |   s   € õ 1áÓ˜vÕ%Ð(9Õ9Ð9r   c               ó    € V ^8„  d   QhRRRR/# )r   Úkindr;   r$   zcontextvars.Tokenr   )r   s   "r   r   r   „   s   € ÷ ñ �ð Ð)ñ r   c                óx   € \         P                  R^RV R\        4       R\        4       R. RRRRR	RR
RR. RR/4      # )u¿  Start recording a sync. ``kind``: 'sync' | 'update' | 'plugin-check'.
Returns the ContextVar token â€” pass it to ``finalize(token=...)`` when
this begin nests inside an outer begin in the same context, so the
outer receipt survives the inner finalize.

The receipt is stamped with the ambient update correlation id (the
``hermes update`` this sync belongs to), or None for a standalone
sync â€” the id is what makes the updater's embed selective.ÚschemarI   r   Ú
started_atÚstepsÚvenv_rebuildNÚfeature_listÚplatformÚoutcomeÚwarningsÚrefusal)r   r   r9   r?   )rI   s   &r   ÚbeginrT   „   sW   € ô �<‰<à�aØ�DØÔ+Ó-Øœ,›.Ø�RØ˜DØ˜DØ˜Ø�tØ˜Ø�tð	
óð r   c               ó   € V ^8„  d   QhRR/# )r   r$   r.   r   )r   s   "r   r   r   ž   s   € ÷ )ñ )�tñ )r   c                ó²   € \         P                  4       pVf   R# \        V4      pV ! V4       \         P                  \        P
                  ! V4      4       R# )z‚Copy-on-write record: the ContextVar may be shared with copied
contexts, so mutate a deep copy and re-set it in THIS context only.N)r   r*   r1   r   r(   r)   )ÚmutateÚcurrentÚupdateds   &  r   Ú_recordrZ   ž   s<   € ô �l‰l‹n€GØ‚ÙÜ�7‹m€GÙ
ˆ7„OÜ‡L�L”—’˜wÓ'Ö(r   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   Únamer;   ÚokÚboolÚdetailr$   r.   r   )r   s   "r   r   r   ©   s.   € ÷ Lñ L�cð L˜tð L¨Sð L¸$ñ Lr   c                ó.   a aa€ \        VV V3R  l4       R# )c                ó^   <€ V P                  . V R ,          ORSRSRSR\        4       /NR7      # )rM   r\   r]   r_   Úat)rM   )Úupdater?   )Úrr_   r\   r]   s   &€€€r   Ú<lambda>Úrecord_step.<locals>.<lambda>ª   s?   ø€ �a—h‘hð &J q¨¥zð &JØ	��t˜R ¨6°4¼»ÐHð&J�hô Kr   N©rZ   )r\   r]   r_   s   fffr   Úrecord_steprh   ©   s   ú€ Üö Kö Lr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r]   r^   Úreasonr;   r$   r.   r   )r   s   "r   r   r   ®   s&   € ÷ Sñ S˜Dð S¨#ð S°tñ Sr   c                ó*   a a€ \        V V3R  l4       R# )c                ó0   <€ V P                  R RSRS/4      # )rN   r]   rj   ©Ú__setitem__)rd   r]   rj   s   &€€r   re   Ú%record_venv_rebuild.<locals>.<lambda>¯   s   ø€ �a—m‘m N°T¸2¸xÈÐ4PÔQr   Nrg   )r]   rj   s   ffr   Úrecord_venv_rebuildrp   ®   s   ù€ ÜÕQÖRr   c               ó    € V ^8„  d   QhRRRR/# )r   ÚextraszOptional[list[str]]r$   r.   r   )r   s   "r   r   r   ²   s   € ÷ =ñ =Ð 3ð =¸ñ =r   c                ó&   a € \        V 3R  l4       R# )c                ó(   <€ V P                  R S4      # )rO   rm   )rd   rr   s   &€r   re   Ú%record_feature_list.<locals>.<lambda>³   s   ø€ �a—m‘m N°FÔ;r   Nrg   )rr   s   fr   Úrecord_feature_listrv   ²   s   ø€ ÜÔ;Ö<r   c               ó    € V ^8„  d   QhRRRR/# )r   Úplatform_idr;   r$   r.   r   )r   s   "r   r   r   ¶   s   € ÷ >ñ > ð >¨ñ >r   c                ó&   a € \        V 3R  l4       R# )c                ó(   <€ V P                  R S4      # )rP   rm   )rd   rx   s   &€r   re   Ú!record_platform.<locals>.<lambda>·   s   ø€ �a—m‘m J°Ô<r   Nrg   )rx   s   fr   Úrecord_platformr|   ¶   s   ø€ ÜÔ<Ö=r   c               ó    € V ^8„  d   QhRRRR/# )r   ÚresultsÚlistr$   r.   r   )r   s   "r   r   r   º   s   € ÷ ñ  $ð ¨4ñ r   c                ó&   a € \        V 3R l4       R# )zwPlugin update-check results (the cadence's receipt section).
Each item is a plugins_updates.CheckResult.to_json() dict.c           
     ó–   <€ T P                  R S U u. uF'  p \        V R4      '       d   V P                  4       MT NK)  	  up 4      # u up i )Úplugin_checksÚto_json)rn   Úhasattrrƒ   )rd   r~   s   &€r   re   Ú&record_plugin_checks.<locals>.<lambda>¾   s?   ø€ �!—-‘-ØÑSZÓ[ÑSZÈa¬W°Q¸	×-BÒ-B˜aŸi™iœkÈÒIÑSZÑ[ô
ùÚ[s   ’-A
Nrg   )r~   s   fr   Úrecord_plugin_checksr†   º   s   ø€ ô ô	
ör   c               ó    € V ^8„  d   QhRRRR/# )r   Úmessager;   r$   r.   r   )r   s   "r   r   r   Ä   s   € ÷ ñ ˜Cð  Dñ r   c                ó&   a € \        V 3R l4       R# )u�   Record a warning surfaced to the user during this sync â€” a warning
that reached the operator's eyes must reach the receipt too.c                óz   <€ V P                  . V P                  R . 4      OR\        S4      R\        4       /NR7      # )rR   rˆ   rb   )rR   )rc   r*   r;   r?   )rd   rˆ   s   &€r   re   Ú record_warning.<locals>.<lambda>È   sD   ø€ �!—(‘(ðG�q—u‘u˜Z¨Ó,ð GØ ¤# g£,°´l³nÐEðGð ô 
r   Nrg   )rˆ   s   fr   Úrecord_warningrŒ   Ä   s   ø€ ô ô	
ör   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úcoder;   r_   r$   r.   r   )r   s   "r   r   r   Ï   s!   € ÷ ñ ˜ð  cð °4ñ r   c                ó*   a a€ \        V V3R l4       R# )uÄ   Record WHY this sync refused to act (e.g. the lazy-install policy).
``outcome`` stays whatever ``finalize`` is given (refusals finalize as
``failed``/``refused``) â€” this names the refusal class.c           
     óh   <€ V P                  R R\        S4      R\        S4      R\        4       /4      # )rS   rŽ   r_   rb   )rn   r;   r?   )rd   rŽ   r_   s   &€€r   re   Ú record_refusal.<locals>.<lambda>Ô   s*   ø€ �!—-‘-Ø˜¤ D£	¨8´S¸³[À$ÌËÐWô
r   Nrg   )rŽ   r_   s   ffr   Úrecord_refusalr’   Ï   s   ù€ ô õ	
ör   c               ó   € V ^8„  d   QhRR/# r#   r   )r   s   "r   r   r   Ú   s   € ÷ Cñ CÐ*ñ Cr   c                 ób   € \         P                  4       p V e   \        P                  ! V 4      # R# )uÔ   The in-flight receipt data â€” for the updater to EMBED its sync
sections into its own receipt (one schema, one directory). A deep
COPY: the authoritative in-flight dict is never exposed for the
caller to mutate.N)r   r*   r(   r)   )rX   s    r   Úsnapshotr•   Ú   s(   € ô
 �l‰l‹n€GØ%,Ò%8Œ4�=Š=˜Ó!ÐB¸dÐBr   c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   rQ   r;   Ú	exit_codeÚintr   zOptional[contextvars.Token]r$   zOptional[Path]r   )r   s   "r   r   r   ã   s,   € ÷ ñ ØðØ ðØ-Hðàñr   c                ó  € \        4       pVf   R# Ve   \        P                  V4       M\        P                  R4       WR&   WR&   \	        4       VR&   \
        P                  \        P                  ! V4      4       VP                  R4      pV'       dU   \        \        P                  4       ;'       g    / 4      p\        P                  ! V4      WT&   \        P                  V4        \        V4      pV#   \         d     R# i ; i)a,  Write the receipt (``outcome``: ok | refused | failed | bisected)
and rotate. Returns its path; None when nothing was begun.

With ``token`` (from the matching ``begin``): pops only this begin's
receipt and restores the outer one. Without: pops whatever is
current (the ambient linear-consumer form).NrQ   r—   Úfinished_atr   )r•   r   r   r   r?   r   r(   r)   r*   r1   r   Ú_write_rotatedÚOSError)rQ   r—   r   rX   r   r    Úpaths   &&&    r   Úfinalizerž   ã   sÜ   € ô ‹j€GØ‚ÙØÒÜ�‰�uÕä�‰�TÔØ ˆIÑØ$ˆKÑÜ)›^€GˆMÑÜ×ÑœŸš gÓ.Ô/ð —‘˜KÓ(€IßÜÔ-×1Ñ1Ó3×9Ð9°rÓ:ˆ	Ü#Ÿ}š}¨WÓ5ˆ	ÑÜ× Ñ  Ô+ðÜ˜gÓ&ˆð €Køô ô Úðús   Ã,C9 Ã9DÄDÚconsumeFc               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   rŸ   r^   r$   r%   r   )r   s   "r   r   r     s'   € ÷ Gñ G˜}ð G¸$ð GÐKcñ Gr   c               ó  € V '       g   R# \        \        P                  4       ;'       g    / 4      pVP                  V 4      pV'       d(   VP                  V R4       \        P	                  V4       Ve   \
        P                  ! V4      # R# )u}  The last sync receipt completed in THIS context under ``update_id``
â€” the correlation surface for an invoking update's embed. A sync from
before this update, a standalone sync, or one from a concurrent
context cannot be returned; a nested update's sync is filed under the
nested id and never displaces the outer update's entry. Deep copy;
None when no matching completion exists.N)r1   r   r*   Úpopr   r(   r)   )r   rŸ   Úentriesr    s   &$  r   Úlast_for_updater¤     sl   € ÷ ÙÜÔ'×+Ñ+Ó-×3Ð3°Ó4€GØ—‘˜IÓ&€IßØ�‰�I˜tÔ$Ü× Ñ  Ô)Ø'0Ò'<Œ4�=Š=˜Ó#ÐFÀ$ÐFr   c               ó   € V ^8„  d   QhRR/# r#   r   )r   s   "r   r   r     s   € ÷ 
ñ 
Ð(ñ 
r   c                 óÔ   €  \        4       R,          p V P                  4       '       d'   \        P                  ! V P	                  RR7      4      #  R#   \
        \        3 d     R# i ; i)u‡   The newest receipt (any kind) â€” the reader surface for
``hermes pm status`` + the desktop. Pure read: never creates the
receipts dir.úlatest.jsonz	utf-8-sig)ÚencodingN)rG   Úis_fileÚjsonÚloadsÚ	read_textrœ   r0   )Úpoints    r   Úlatestr®     s[   € ðÜ“ Õ.ˆØ�=‰=�?Š?Ü—:’:˜eŸo™o°{˜oÓCÓDÐDð ñ øô ”ZÐ ô Úðús   ‚AA ÁA'Á&A'c               ó    € V ^8„  d   QhRRRR/# )r   r-   údict[str, Any]r$   r;   r   )r   s   "r   r   r   #  s   € ÷ Fñ F˜ð F¨3ñ Fr   c           	     ó
  € \         P                  ! R\         P                  ! 4       4      pV P                  R4      ;'       g    RpRV RV R\        P
                  ! 4        R\        P                  ! 4       P                   R2	# )z{Unique name for concurrent writers: stamp + pid + full random
uuid4 (same pid+random convention as the updater's receipts).z%Y%m%dT%H%M%SZrI   ÚsyncÚpm_Ú-z.json)	ÚtimeÚstrftimeÚgmtimer*   ÚosÚgetpidÚuuidÚuuid4Úhex)r-   ÚstamprI   s   &  r   Ú_receipt_namer¾   #  se   € ô �MŠMÐ*¬D¯KªK«MÓ:€EØ�8‰8�FÓ×%Ð%˜v€DØ���q˜˜˜a¤§	¢	£˜}¨A¬d¯jªj«l×.>Ñ.>Ð-?¸uÐEÐEr   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                ól  € ^ RI Hp ^ RIHp \	        4       pVP                  RRR7       V\        V 4      ,          pVR,          P                  R4      ;_uu_ 4       pV! VP                  4       RR7       V! W@4       V! VR,          V 4       \        V4       R	R	R	4       V#   + '       g   i     T# ; i)
z?Use the same stdlib-only atomic writer as PM's installed facts.)Úlock_fd)Ú_writeT)ÚparentsÚexist_okz.pm-write.lockza+b)Úwaitr§   N)
Úpm.filesystemrÁ   Úpm.lockrÂ   rG   Úmkdirr¾   ÚopenÚfilenoÚ_rotate)r-   rÁ   rÂ   Údr�   Úlocks   &     r   r›   r›   +  s‘   € å%Ýä‹€AØ‡G�G�D 4€GÔ(ØŒ}˜TÓ"Õ"€Dà
ÐÕ
×	$Ñ	$ U×	+Ô	+¨tÙ�—‘“ DÕ)ÙˆtÔÙˆq�=Õ  $Ô'Ü�Œ
÷	 
,ð
 €K÷ 
,Ö	+ð
 €Kús   Á<B"Â"B3	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                ó¶   € \        R V P                  R4       4       R R7      pVR\        )   F  p VP                  4        K  	  R#   \         d     K(  i ; i)u³   Keep the newest _RECEIPT_KEEP PM receipts. The dir also holds the
updater's ``update_*.json`` receipts â€” this rotates ONLY pm's own
(``pm_*.json``); the updater rotates its own.c              3  óT   "  € T F  qP                  4       '       g   K  Vx € K   	  R # 5ir'   )r©   )Ú.0Úps   & r   Ú	<genexpr>Ú_rotate.<locals>.<genexpr>A  s   é € Ð7Ñ'ˆq¯9©9¯;�ŠÓ'ùs   ‚(ž
(z	pm_*.jsonc                ó   € V P                   # r'   )r\   )rÒ   s   &r   re   Ú_rotate.<locals>.<lambda>B  s   € �a—f’fr   )ÚkeyN)ÚsortedÚglobÚ_RECEIPT_KEEPÚunlinkrœ   )rÌ   ÚreceiptsÚstales   &  r   rË   rË   <  sY   € ô Ù7�A—F‘F˜;Ô'Ó7Ùô€Hð ˜/œM˜>Ó*ˆð	Ø�L‰LŽNó +øô ô 	Úð	ús   ³A	Á	AÁA)Ú )é    N)1Ú__conditional_annotations__Ú__doc__Ú
__future__r   ÚcontextvarsÚ
contextlibr   r(   rª   r¸   rµ   rº   r   r   r   Úpathlibr   Útypingr   r	   rÚ   Ú
ContextVarr   Ú__annotations__r   r   r   r!   r+   r2   r9   r?   rG   rT   rZ   rh   rp   rv   r|   r†   rŒ   r’   r•   rž   r¤   r®   r¾   r›   rË   )rà   s   @r   Ú<module>ré      sF  øðô"õH #ã Ý %Û Û Û 	Û Û ß ,Ñ ,Ý ß  à€ð >I×=SÒ=SØ $ô>€Ð
:ó ð ×ÒÐ;ÀTÔJð ÐQó ð
 GR×F\ÒF\Ø ôG€ÐCó ð EP×DZÒDZØ ôE€ÐAó ð
 ô$ó ð$õ0õ,õõ )õ:õõ4)÷L÷
Sõ=õ>õõ÷õC÷ðDGÀ÷ Gõ"
õFõ÷"r   