
    $!jg                      % S r SSKJr  SSKrSSKrSSKrSSKrSSKrSSKrSSK	r	SSK
r
SSKrSSKrSSKrSSKJr  SSKJrJrJr  SSKJr  SSKJrJrJrJr  \" \5      R7                  5       R8                  R8                  r\S-  r\S	-  r\S
-  r \S-  S-  r!Sr"Sr#Sr$SS0r%Sr&Sr'Sr(Sr)SSSSSSSS.r*SSSSSSS.r+S S!1r,S"r-S# r.S$ r/S%r0S}S& jr1S~S' jr2SS( jr3SS) jr4SSS* jjr5S+S,.SS- jjr6SS. jr7S/ r8S0 r9Sq:S1 r;\Rx                  \#4SS2 jj5       r=SS3 jr>\#S4.SS5 jjr?S6 r@S7 rAS8 rBS9 rCSS: jrDSS; jrE\	R                  " S<\	R                  5      rHSS= jrI\J" 1 S>k5      rKSS? jrLS@rMSArNSBrO\	R                  " SC5      rP\J" 1 SDk5      rQSSE jrRSSF jrSSSG jrTSSH jrU\M\NSI.     SSJ jjrVSK\M\NSL.       SSM jjrW\O\MSN.   SSO jjrXSSP jrYSQ rZSR r[SS r\ST r]SU r^SSV jr_SSW jr`SX raSY rbSSZ jrcS[ rdS\ reS] rfS^rgS_rhSS` jriSa rjSb rkSc rlSdSeSfSgShSiS.rmSj\nSk'   1 SlkroSmrpSn rqSSo jrrSp rsSSq jrtSr ruSs rvSt rwSu rxSv rySw rzSx r{Sy r|Sz r}SS{ jr~\S|:X  a  \GR                   " \~" 5       5        gg)u=   notice — small stdlib CLI for Mission Control Notice Board.    )annotationsN)Counter)datetime	timedeltatimezone)Path)urlparse
urlunparse	parse_qsl	urlencodez
brain.jsonzinbox.mdz	render.pyscriptszvalidate_brain.pyz.notice-board.lockg      >@)LearningResearchReadPersonal AdminIdeasDab ImprovementsUncategorizedzResearch / Readr   )InboxExploreDone)	completeddroppedstalearchived	graduatedread)
unexploredexplainer_readyr   r   r   )linkidealearningprojectadminnoter    r!   r"   r$   r%   )r   r   r   r   r   r   r   r   r   r   r   promotedneeds_review)	parent_ofchild_offorked_from
related_tomerged_intoartifact_ofc                 h    [         R                  " [        R                  5      R	                  S5      $ )Nz%Y-%m-%dT%H:%M:%SZr   nowr   utcstrftime     tools/notice.pyr0   r0   8   s!    <<%../CDDr4   c                 h    [         R                  " [        R                  5      R	                  S5      $ )N%Y-%m-%dr/   r3   r4   r5   todayr8   <   s     <<%..z::r4   u   
  <a class="nb-backlink" href="./" style="display:inline-block;margin:16px 0 0 16px;color:#ffcf75;text-decoration:none;font:600 0.95rem system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;">← Item page</a>
c                T    SU ;   =(       d    SU ;   =(       d    SU ;   =(       d    SU ;   $ )zKReturn true when a standalone explainer can navigate back to its item page.z	href="./"z	href='./'u   ← Item pagezBack to itemr3   )htmls    r5   _explainer_has_backlinkr;   C   s:     	t 	"$	"d"	" T!	r4   c                6    U R                  SS9n[        U5      (       a  gUR                  5       nUR	                  S5      nUS:X  a  gUR	                  SU5      nUS:X  a  gUSUS-    [
        -   XS-   S -   nU R                  USS9  g	! [         a     gf = f)
zInject a compact item-page backlink into explainer HTML if it is missing.

The backlink is intentionally inline-styled so it works across the varied
hand-authored/agent-authored standalone explainer styles.
utf-8encodingFz<body>N   T)	read_textUnicodeDecodeErrorr;   lowerfindEXPLAINER_BACKLINK_HTML
write_text)pathr:   rE   idxcloseupdateds         r5   ensure_explainer_backlinkrM   M   s    ~~w~/ t$$JJLE
**W
C
byIIc3E{:EAI!884	
;KKGOOGgO.  s   B 
BBc                d    U =(       d    SR                  5       R                  5       nUS;   a  U$ S$ )Nr   inboxexploredonerP   )striprE   )statusss     r5   lc_phaserV   j   s1    		7!!#))+A111>w>r4   c                (    [        5       nXS'   XS'   g)z:Bump v3.0 last_modified_at + v2.1 updated_at in one place.
updated_atlast_modified_atN)r0   )itemtss     r5   stamp_modifiedr\   o   s    	B!	r4   c                &   U R                  S/ 5      nU=(       d    SU=(       d    U R                  SS5      [        5       S.nU(       a7  US   R                  S5      US   :X  a  US   R                  S5      US   :X  a  g	UR                  U5        g	)
u   Record a new entry on ``item.sources`` (v3.0 audit list).

No-op if an identical (via, raw) pair already sits at the tail —
avoids ballooning the list on duplicate re-captures.
sourcesunknowntitle viarawatr@   rc   rd   N)
setdefaultgetr0   append)rZ   rc   rd   r^   entrys        r5   append_sourcerj   v   s~     ooi,G$9S-IDHHWb4IQTQVWE72;??5)U5\9gbkooe>TX]^cXd>dNN5r4   legacy)default_viac                  U R                  S[        R                  U R                  S5      S5      5        [        U R                  S5      5      U S'   U R                  S5      (       d=  U R                  S5      =(       d"    U R                  S5      =(       d
    [	        5       U S'   U R                  S	5      (       dZ  U R                  S
5      =(       d    UU R                  S5      =(       d    SU R                  S5      =(       d
    [	        5       S./U S	'   U R                  S/ 5        U R                  S5      nU(       a	  SU S3U S'   U $ )zBackfill any missing v3.0 fields on an item dict.

Items minted before plan 03 may still lack these fields on the
transition path; ensure they're present before validation. Plan 04
adds ``folder_path`` (every item owns ``items/<slug>/``).
typecategoryr%   rT   phaserY   rX   
created_atr^   sourcer`   ra   rb   tagsslugitems//folder_path)rf   CATEGORY_TO_TYPErg   rV   r0   )rZ   rl   rt   s      r5   ensure_v3_fieldsry      s    	OOF,00*1EvNOTXXh/0DM88&''#'88L#9#\TXXl=S#\WZW\ 88I88H%488G$*((<(1CE
 Y
 	OOFB88FD &tfA.]Kr4   c                >    [         S-  U -  nUR                  SSS9  U$ )zCreate ``items/<slug>/`` if missing and return the Path.

Capture commands call this so a freshly-minted item always has its
folder on disk, even before the next ``render.py`` run.
itemsTparentsexist_ok)REPOmkdir)rt   folders     r5   ensure_item_folderr      s'     G^d"F
LLL-Mr4   c                 P    [         R                  " [        R                  SS95      $ )Nr=   r>   )jsonloadsBRAINrC   r3   r4   r5   loadr      s    ::eoowo788r4   c                t    [        5       U S'   [        R                  [        R                  " U SSS9S-   SS9  g)	u   Direct write — DO NOT call from mutating commands. Use ``transaction``.

Retained as a low-level building block for transaction() and any one-off
administrative script that already holds the lock.
rX      Findentensure_ascii
r=   r>   N)r0   r   rH   r   dumps)bs    r5   saver      s5     eAlO	TZZ!%@4GRYZr4   c                     [         cN  [        [        S-  5      n U [        R                  ;  a   [        R                  R                  SU 5        SSKJn  Uq [         $ )zEImport scripts.validate_brain.validate_brain on demand (stdlib-only).r   r   )validate_brain)_validate_brain_fnstrr   sysrI   insertr   )scripts_dirvbs     r5   _load_validatorr      sF     !$*+chh&HHOOA{+7r4   c              #  t  #    [         R                  [        -  n[        R                  " [        U5      [        R                  [        R                  -  S5      n [        R                  " 5       U -   n  [        R                  " U[        R                  [        R                  -  5         Sv    [        R                  " U[        R"                  5        [        R$                  " U5        g! [         aE    [        R                  " 5       U:  a  [        SU SU S S35      e[        R                   " S5         Of = fM  ! [        R$                  " U5        f = f!  [        R                  " U[        R"                  5        [        R$                  " U5        f ! [        R$                  " U5        f = f= f7f)a  Cross-process exclusive lock via fcntl.flock on ``.notice-board.lock``.

The lock file sits next to ``brain.json`` so test sandboxes (which override
``BRAIN``) get their own lock and do not contend with the live CLI.

Linux ``flock`` releases automatically on process death, so stale lock
files left behind by a crash do not need cleanup. We poll non-blockingly
so we can enforce a soft timeout instead of hanging forever.
i  znotice: could not acquire z within z.0fz*s; another notice CLI write is in progressg?N)r   parent	LOCK_NAMEosopenr   O_RDWRO_CREATtime	monotonicfcntlflockLOCK_EXLOCK_NBBlockingIOError
SystemExitsleepLOCK_UNrK   )timeout	lock_pathfddeadlines       r5   
_file_lockr      s4     y(I	YRZZ!7	?B>>#g-	!B => 		KKEMM*HHRL # !>>#x/$4YKxPS} UB B  

4 !	   HHRL	KKEMM*HHRLBHHRLsm   AF8E 56C- +E 1%E F8-AD<9E ;D<<E EF8F5%FF5F22F55F8c                0   U R                   nUR                  SSS9  [        R                  " U R                  S-   S[        U5      S9u  p4 [        R                  " USSS9 nUR                  U5        UR                  5         [        R                  " UR                  5       5        S	S	S	5        [        R                  " U[        U 5      5        g	! , (       d  f       N/= f! [         a)     [        R                  " U5        e ! [         a     e f = ff = f)
zEWrite ``data`` to ``path`` atomically (write tmp, fsync, os.replace).Tr|   .z.tmp)prefixsuffixdirwr=   r>   N)r   r   tempfilemkstempnamer   r   fdopenwriteflushfsyncfilenoreplaceBaseExceptionunlinkOSError)rI   datar   r   tmp_namefs         r5   _atomic_write_textr      s    [[F
LLL-##499s?6sSY{[LBYYr31QGGDMGGIHHQXXZ  2 	

8SY'	 21
  	IIh 	  		sI   C" "AC((C" 
CC" "
D-DD
DDDD)r   c          
        [        5       n[        U5         [        R                  " [        R                  SS95      n [        R                  SS9nU" U5      n[        U[        5      (       a  [        U5      S:X  a  Uu  pxOUSp[        5       US'   U" USS9n	U	(       a  SS	R                  U	5      -   n
[        U
5      eUnU(       a  [        5       nUS
U SU  SU S3-   n[        [        [        R                  " USSS9S-   5        X:w  a  [        [        U5        UsSSS5        $ ! [         a    Sn Nf = f! , (       d  f       g= f)u  Run a single read → mutate → stage-audit → validate → atomic-write cycle.

Inside the cross-process lock:

  1. Read a fresh brain.json so waiters see prior committed writes.
  2. Read the current inbox.md text (or '' if absent).
  3. Invoke ``mutate(brain)`` to apply the change in memory.
  4. Stage the audit append in memory — do NOT touch inbox.md yet.
  5. Validate the staged brain dict via
     ``scripts.validate_brain.validate_brain``. On any failure, abort
     BEFORE writing either file, so brain.json and inbox.md remain
     byte-identical to their pre-transaction state.
  6. Atomically replace brain.json with the new text.
  7. Atomically replace inbox.md with the staged text.

``mutate`` may return:
    - None              : no audit entry, no caller payload
    - str detail        : audit detail string, no payload
    - (detail, payload) : audit detail + payload returned to caller

Two-file atomicity caveat: POSIX gives us atomic single-file
``rename()`` via ``os.replace``, not multi-file atomic commit. If the
process is killed between steps 6 and 7, brain.json may be updated
while inbox.md is not. The reverse (inbox without brain) is impossible
because inbox is written last. This is acceptable for an
append-only audit log: missing one trailing line is recoverable from
the brain.json diff, while a phantom inbox entry for a write that
never happened would be worse. Validation failures — the common
failure mode — never write either file.
r=   r>   ra   r   NrX   )rootuB   notice: transaction aborted — brain.json validation failed:
  - z
  - 
##     — notice CLI · 

- r   Fr   )r   r   r   r   r   rC   INBOXFileNotFoundError
isinstancetuplelenr0   joinr   r   r   )actionmutater   	validatorbrainexisting_inboxresultdetailpayloaderrsmsg	new_inboxstamps                r5   transactionr      sC   >  !I	G	

5??G?<=	 "__g_>N fe$$V)9$OFG$dG!el T*WZbZgZghlZmmCS/!"	EE&w1&xrJI 	JJuQU;dB	
 &ui0? 
	 ! 	 N	 	 
	s/   (D;D)CD;)D85D;7D88D;;
E	c                    [         R                  " SSU R                  5       R                  5       5      R                  S5      S S =(       d    S$ )Nz[^a-zA-Z0-9]+-<   rZ   )resubrS   rE   rU   s    r5   rt   rt   >  s:    66"C):;AA#FsKUvUr4   c                P   ^ [        U4S jU R                  S/ 5       5       S 5      $ )Nc              3  T   >#    U  H  oR                  S 5      T:X  d  M  Uv   M     g7f)
display_idNrg   ).0xis     r5   	<genexpr>find.<locals>.<genexpr>C  s#     K.q%%2E2J.   (	(r{   nextrg   )r   r   s    `r5   rF   rF   B  s!    KAEE'2.KTRRr4   c                P   ^ [        U4S jU R                  S/ 5       5       S 5      $ )Nc              3  T   >#    U  H  oR                  S 5      T:X  d  M  Uv   M     g7frt   Nr   )r   r   rU   s     r5   r   find_by_slug.<locals>.<genexpr>G  s"     E.q%%-12D.r   r{   r   )r   rU   s    `r5   find_by_slugr   F  s!    EAEE'2.EtLLr4   c           
         [         R                  SSS9 nUR                  S[        5        SU  SU S35        S S S 5        g ! , (       d  f       g = f)Nar=   r>   r   r   r   r   )r   r   r   r0   )r   r   r   s      r5   auditr   J  sD    	C'	*a	%w1&xrJK 
+	*	*s   $A
Ac                    U [         ;   a  U $ U [        ;   a*  [        U    n[        SU  SU S3[        R                  S9  U$ [        SU  SSR                  [         5       35      e)zJTolerate legacy 'Research / Read' input by silently migrating to Research.note: 'z&' is a legacy category; recording as ''filezunknown category: . allowed: , )
CATEGORIESLEGACY_CATEGORY_MAPprintr   stderrr   r   )rd   news     r5   _resolve_categoryr  O  sk    
j

!!!#&uB3%qIPSPZPZ[

)#k$))J:O9PQ
RRr4   c                    U c  gU S:X  a  [        S[        R                  S9  gU [        ;   a  [        SU  S3[        R                  S9  gU [        ;  a$  [        SU  S	S
R                  [        5       35      eU $ )z<Map legacy 'promoted' onto Option A statuses with a warning.r   r'   znote: 'needs_review' is no longer a Link Triage lane; recording as 'unexplored'. Use 'explainer_ready' / 'read' / 'archived' / 'dropped' explicitly when relevant.r   r   zx' is a legacy link status (Option A); recording as 'archived'. Use 'explainer_ready' or 'read' explicitly when relevant.r   zunknown link status: r   r   )r  r   r  LEGACY_LINK_STATUSESLINK_STATUSESr   r   )rd   s    r5   _resolve_link_statusr  Z  s    
{
n? 		
 
""cU H H	

 
-0[=AY@Z[\\Jr4   z
^https?://c                l    [        U =(       a#    [        R                  U R                  5       5      5      $ N)bool_URL_REmatchrS   r   s    r5   _looks_like_urlr  u  s!    .gmmAGGI.//r4   >   rU   refgclidfbclidmc_cidmc_eidutm_idcontextref_srcutm_term
utm_medium
utm_sourceutm_contentutm_campaignc                l   U (       d  U $  [        U R                  5       5      nUR                  =(       d    SR	                  5       nUR
                  R	                  5       nUR                  =(       d    Sn[        U5      S:  a  UR                  S5      (       a  USS n[        UR                  SS9 VVs/ s H"  u  pVUR	                  5       [        ;  d  M  XV4PM$     nnn[        [        U5      5      n[        X#XAR                  US45      $ ! [         a    U R                  5       s $ f = fs  snnf )	zReturn a canonical form of url for dedup. Strips tracking params,
lowercases scheme + host, drops fragment, normalizes trailing slash.httpsrv   rB   Nr@   F)keep_blank_valuesra   )r	   rS   	ExceptionschemerE   netlocrI   r   endswithr   query_DROP_PARAMSr   sortedr
   params)	urlpr   r!  rI   kvquery_pairsr#  s	            r5   canonicalize_urlr,    s     
SYY[! hh!'((*FXX^^F66=SD
4y1}s++CRy(1!''U(S 5(Sfqggi|3 A6(SK 5f[)*EvtXXubABB  yy{5s   D 7D0D0D-,D-g      ?      z[A-Za-z0-9]{3,}>8   allandanyarebutcancomforhashowmaynetnotoneorgthetootwowaswhowhywwwfromhavehttpintorZ   justlikemustr%   oversomethanthattheythisverywerewhatwhenwillwithaboutcouldr  mightnotestheirtherewherewhichwouldshouldbecausecapturec                    U (       d  / $ [         R                  U 5       Vs/ s H-  nUR                  5       [        ;  d  M  UR                  5       PM/     sn$ s  snf r
  )	_TOKEN_REfindallrE   
_STOPWORDS)textts     r5   _tokensrj    sP    	(006 ,6!wwy
* AGGI6 , , ,s   AAc                   / nU R                  S5      =(       d    SnUR                  U5        UR                  U R                  S5      =(       d    / 5        U R                  S5      =(       d    0 n[        U[        5      (       Ga  UR                  S5      =(       d    0 R                  S5      =(       d    / nU H+  n[        U[
        5      (       d  M  UR                  U5        M-     UR                  S5      =(       d    0 R                  S5      n[        U[
        5      (       a  UR                  U5        UR                  S5      =(       d    0 nS	 H<  nUR                  U5      n	[        U	[
        5      (       d  M+  UR                  U	5        M>     UR                  S
5      =(       d    /  H+  n
[        U
[
        5      (       d  M  UR                  U
5        M-     UR                  S5      =(       d    0 nS H<  nUR                  U5      n	[        U	[
        5      (       d  M+  UR                  U	5        M>     U R                  S5      n[        U[        5      (       aB  S H<  nUR                  U5      n	[        U	[
        5      (       d  M+  UR                  U	5        M>     U R                  S5      =(       d    0 n[        U[        5      (       a  UR                  5        Hs  n	[        U	[
        5      (       a  UR                  U	5        M+  [        U	[        5      (       d  MB  U	 H+  n[        U[
        5      (       d  M  UR                  U5        M-     Mu     SR                  S U 5       5      n[        U5      S:  a  USS nU$ )zBuild the matching corpus for an item.

Plan 7 spec: title + first 500 chars of notes/explainer + tags.
Also includes ``template_fields`` string values so each per-type
template's primary text (premise, goal, action, etc.) participates.
r`   ra   rs   phasesrP   r[  raw_capturerQ   )intentnext_actionopen_questionsrR   )summaryr    )explainer_mddomaintemplate_fields c              3  6   #    U  H  o(       d  M  Uv   M     g 7fr
  r3   )r   r(  s     r5   r   _corpus_for.<locals>.<genexpr>  s     *u!AAus   
	i  N)
rg   rh   extendr   dictr   valueslistr   r   )rZ   partsr`   rl  inbox_notesnrd   rQ   r)  r*  qrR   r    tfr   blobs                   r5   _corpus_forr    s    EHHW#E	LL	LL&!'R(XXh%2F&$zz'*0b55g>D"A!S!!Q  zz'"(b--m<c3LL**Y'-2*AAA!S!!Q + -.4"4A!S!!Q 5 zz&!'RAA!S!!Q  88FD$+AA!S!!Q ,
 
#	$	*B"dA!S!!QAt$$A!!S))Q 	  88*u**D 4y4ET{Kr4   c           	        / nU  HW  nUR                  S5      n[        U[        5      (       a  U(       d  M2  UR                  U[	        [        U5      5      45        MY     [        5       nU H#  u  pV[        U5       H  nXG==   S-  ss'   M     M%     [        U5      =(       d    Sn/ n	0 n
U H  u  p6U(       d  0 nOh[        U5      n0 n[        [        U5      S5      nUR                  5        H2  u  p~[        R                  " SU-   SXG   -   -  5      S-   nX-  U-  X'   M4     U	R                  X;45        XU'   M     X4$ )zvReturn (ordered_index, by_slug) of TF-IDF vectors.

Each vector is a sparse dict[token -> weight] so cosine is cheap.
rt   rB   )rg   r   r   rh   rj  r  r   setr   maxr{   mathlog)r{   docsitrt   df_toksri  Nindexby_slugr*  r  lengthr~  idfs                   r5   _build_indexr    s(   
 )+Dvvf~$$$DT7;r?345	 
 yBTAEQJE   	D	QA02E+-G
"$ABATA&F
hhA!be)459
c) # 	dY  >r4   c                   U (       a  U(       d  g[        U 5      [        U5      -  nU(       d  gSnU H  nX0U   X   -  -  nM     [        R                  " [        S U R	                  5        5       5      5      n[        R                  " [        S UR	                  5        5       5      5      nU(       a  U(       d  gX5U-  -  $ )Ng        c              3  *   #    U  H	  oU-  v   M     g 7fr
  r3   r   r   s     r5   r   _cosine.<locals>.<genexpr>       1j1uj   c              3  *   #    U  H	  oU-  v   M     g 7fr
  r3   r  s     r5   r   r    r  r  )r  r  sqrtsumrz  )r   r   commonnumri  nanbs          r5   _cosiner    s    AVc!f_F
Ctad{ 	31ahhj11	2B	31ahhj11	2BRr'?r4   	thresholdtop_kc          	     :  ^ U R                  S5      =(       d    / nT(       d  / $ [        U5      u  pVUR                  T5      nU(       d  / $ [        U4S jU 5       S5      nT1n	[        U[        5      (       ar  UR                  S5      =(       d    /  HT  n
[        U
[        5      (       d  M  [        U
R                  S5      [
        5      (       d  M@  U	R                  U
S   5        MV     U R                  S5      =(       d    /  H  n[        U[        5      (       d  M  UR                  S5      UR                  S5      pUT:X  a(  [        U[
        5      (       a  U	R                  U5        Mi  UT:X  d  Mq  [        U[
        5      (       d  M  U	R                  U5        M     / nU H4  u  nnX;   a  M  [        UU5      nUU:  d  M!  UR                  UU45        M6     UR                  S S	9  USU  VVs/ s H  u  nnU[        US
5      S.PM     snn$ s  snnf )a  Compute the top-K similar items to ``target_slug`` from brain.items.

Returns a list of ``{"to_slug": str, "score": float}`` dicts sorted by
descending score. Already-confirmed relationships are excluded so we
never re-suggest something the user already accepted.

The caller is responsible for stamping ``via`` / ``at`` and writing the
result onto ``item.suggested_relationships``. Kept stateless so it can
be re-used by the cron pass and the interactive capture skill.
r{   c              3  T   >#    U  H  oR                  S 5      T:X  d  M  Uv   M     g7fr   r   r   r  target_slugs     r5   r   (suggest_relationships.<locals>.<genexpr>7  s      LUrffVn.KUr   Nrelationshipsto_slugrE  toc                    U S   * $ )NrB   r3   r   s    r5   <lambda>'suggest_relationships.<locals>.<lambda>N  s
    qter4   keyr-  )r  score)rg   r  r   r   ry  r   addr  rh   sortround)r   r  r  r  r{   r  r  targettarget_itemexcludedrrelr   ri  scoredrt   r*  rU   scs    `                 r5   suggest_relationshipsr  "  s    IIg$"E	!%(NE[[%F	LULdSK%H+t$$17R7A!T""z!%%	2BC'H'HQy\* 8
 yy)/R/#t$$wwv1
1c 2 2LLO+*Q"4"4LLO 0 ')FaFA	>MM4)$  KKOK$?Efu~N~eaE"aL1~NNNs   7Hcron-auto-connectrc   r  r  c                 ^^ U R                  S5      =(       d    / n[        U4S jU 5       S5      nU(       d  / $ [        U TX4S9nU(       d  / $ [        5       n/ n	UR	                  S/ 5      n
UR                  S5      =(       d    /  Vs1 s H+  n[        U[        5      (       d  M  UR                  S5      iM-     nnU
 Vs1 s H+  n[        U[        5      (       d  M  UR                  S5      iM-     nnU H  nUS   mTU;   d  TU;   a  M  TUS   UUS	.nU
R                  U5        U	R                  U5        [        U4S
 jU 5       S5      nUc  M]  UR	                  S/ 5      nUR                  S5      =(       d    /  Vs1 s H+  n[        U[        5      (       d  M  UR                  S5      iM-     nnTU;   a  M  [        U4S jU 5       5      (       a  M  UR                  TUS   UUS	.5        M     U	$ s  snf s  snf s  snf )zCompute suggestions for ``target_slug`` and append to both sides.

Idempotent: a (to_slug) already present in suggested_relationships or
already in confirmed relationships is skipped. Returns the list of
suggestions actually appended on the target.
r{   c              3  T   >#    U  H  oR                  S 5      T:X  d  M  Uv   M     g7fr   r   r  s     r5   r   %_apply_suggestions.<locals>.<genexpr>]  s      G";)F22r   Nr  suggested_relationshipsr  r  r  )r  r  rc   re   c              3  T   >#    U  H  oR                  S 5      T:X  d  M  Uv   M     g7fr   r   )r   r  
other_slugs     r5   r   r  x  s      I5RFF6Nj,Hbb5r   c              3  z   >#    U  H0  n[        U[        5      =(       a    UR                  S 5      T:H  v   M2     g7fr  Nr   ry  rg   )r   r   r  s     r5   r   r    s5      $"1 !T"FquuY'7;'FF"   8;)	rg   r   r  r0   rf   r   ry  rh   r1  )r   r  rc   r  r  r{   r  sugsr[   addedtarget_sugsr  target_confirmedrU   target_pendingri   other
other_sugsother_confirmedr  s    `                 @r5   _apply_suggestionsr  R  s    IIg$"EGGNF	 +4CD		BE##$=rBK39::o3N3TRT3T 03TQ%a. )i(3T 00; /1$Q- 'aeeI&N /y\
))Z>-I!wZ	
 	5!UI5I4P=%%&?D
6;ii6P6VTV6V 36V(D1 ,155+6V 3/) $"$ $ $"wZ	
 	1 < LE0/&3s$   
G:'G:G?G?H H)daysr  c                  [         R                  " [        R                  5      [	        US9-
  nSnU R                  S5      =(       d    /  GH  nUR                  S5      =(       d    / n[        U[        5      (       d  M5  / nU H  n[        U[        5      (       d  M  UR                  S5      n	[        U	[        [        45      (       a  [        U	5      U:  a  US-  nM\  UR                  S5      n
[        U
[        5      (       a  [        U
5      OSnUb  X:  a  UR                  U5        M  US-  nM     [        U5      [        U5      :w  d  GM  XuS'   [        U5        GM     U$ )	aS  Drop stale or now-too-weak ``suggested_relationships`` entries.

Returns the count of pruned rows. Used by the nightly auto-connect pass.
A suggestion missing an ``at`` is treated as fresh (don't blow it away
on a malformed timestamp), but suggestions below the active threshold are
removed so threshold increases take effect immediately.
)r  r   r{   r  r  rB   re   N)r   r0   r   r1   r   rg   r   r{  ry  intfloatr   
_parse_isorh   r   r\   )r   r  r  cutoffprunedr  r  keeprU   r  re   r[   s               r5   _prune_stale_suggestionsr    s#    \\(,,')*>>FFii &B&vv/06B$%%Aa&&EE'NE%#u..5<)3K!tB#-b##6#6BDBzR\A!  t9D	!,0()2) '* Mr4   c                   [        U [        5      (       a  U R                  5       (       d  g U R                  5       R                  SS5      n [        R
                  " U5      nUR                  c  UR                  [        R                  S9nU$ ! [         a:     [        R                  " U R                  5       S5      n N^! [         a      g f = ff = f)NZz+00:00r7   )tzinfo)r   r   rS   r   r   fromisoformat
ValueErrorstrptimer  r   r1   )rU   txtds      r5   r  r    s    aQWWYY
'')

C
*C""3' 	xxIIX\\I*H  	!!!'')Z8A 		s*   B 
C%C
CCCCc                p   [        5       oR                  S/ 5      nU R                  (       a2  U Vs/ s H%  o3R                  S5      U R                  :X  d  M#  UPM'     nnU R                  (       a=  [	        U R                  5      nU Vs/ s H  o3R                  S5      U:X  d  M  UPM     nnU R
                  (       a;  U Vs/ s H.  o0R
                  UR                  S5      =(       d    / ;   d  M,  UPM0     nnU R                  (       ag  U R                  R                  5       nU Vs/ s H@  o5UR                  SS5      S-   UR                  SS5      -   R                  5       ;   d  M>  UPMB     nnUR                  S	 S
9  U R                  (       a  [        [        R                  " U Vs/ s Hf  nUR                  S5      UR                  S5      UR                  S5      UR                  S5      UR                  S5      UR                  S5      S.PMh     snSSS95        gU H\  n[        SUR                  S5      S SUR                  SS5      S SUR                  SS5      S SUR                  SS5       35        M^     [        S[        U5       S35        gs  snf s  snf s  snf s  snf s  snf )Nr{   rT   ro   rs   r`   ra   ru  rt   c                p    SSSS.R                  U R                  S5      S5      U R                  SS5      4$ )Nr   rB   r   )r   r   r   rT   	   r   r   r  s    r5   r  cmd_list.<locals>.<lambda>  s6    !aCGGhYZ[]^]b]bcoqr]str4   r  r   outcome)idrt   r`   ro   rT   r  r   Fr   r   #z>3  z<7<16r   z	 item(s).)r   rg   rT   ro   r  tagr#  rE   r  r   r  r   r   )r   r   r{   r   catr  s         r5   cmd_listr    s3   Agr*xxUJUeeHo.I!UJzz

+!>EqUU:%6#%=E>uu%J%Q55QUU6]5Hb+Iq%eJwwGGMMO!aEq155"+=+CaeeFTVFW+W*^*^*`%`Ea	JJtJuvvdjj 	 a %%%quuV}UU7^z1BeeHo!%%	2B
 	 #$%	9 	:
 !%%%b)AEE(B,?+C2aeeJXZF[\_E``bcdchchiprtcubvwx 	Bs5zl)
$%' K ?J bs6   "JJ	J$%J$+J)1J)(=J.)J.+A-J3c                	   [        5       n[        XR                  5      nU(       d&  [        SU R                   3[        R
                  S9  gU R                  (       a   [        [        R                  " USSS95        g[        SUS	    S
US    35        [        SUR                  S5       35        [        SUR                  S5       3UR                  S5      (       a  SUR                  S5       3OS-   5        [        SUR                  S5       35        UR                  S5      (       a   [        SSR                  US   5      -   5        UR                  S5      (       a  [        SUR                  S5       35        UR                  S5      (       a  [        SUR                  S5       35        UR                  S5      =(       d    0 nUR                  S5      =(       d    0 R                  S5      =(       d    / nU(       a)  [        S5        U Vs/ s H  n[        S U 35      PM       nUR                  S!5      =(       d    0 nUR                  S"5      (       a  [        S#US"    35        UR                  S$5      (       a  [        S%US$    35        UR                  S&5      (       a,  [        S'5        US&    Vs/ s H  n[        S(U 35      PM       nUR                  S)5      =(       d    / nU(       ap  [        S*[        U5       S+35        U V	s/ s HK  n	[        S,U	R                  S-S.5       S/U	R                  S5      =(       d    U	R                  S05       35      PMM       n	UR                  S15      =(       d    /  V
s/ s HO  n
U
R                  S25      UR                  S5      :X  d&  U
R                  S35      UR                  S5      :X  d  MM  U
PMQ     nn
U(       a  [        S45        U Hv  n
U
R                  S25      UR                  S5      :X  a  S5OS6nUS5:X  a  U
R                  S35      OU
R                  S25      n[        S7U S8U
R                  S95       S:U 35        Mx     gs  snf s  snf s  sn	f s  sn
f );Nno item with display_id=r   rB   r   Fr   r   r  r   r  r`   z  slug:     rt   z  status:   rT   r  z
  outcome=ra   z  category: ro   rs   z  tags:     r   rr   z  source:   project_filez  project:  rl  rP   r[  z  notes:z    - rQ   rn  z  intent:   ro  z  next:     rp  z  questions:z    ? 	artifactsz  artifacts ():z    - [kind?] url_or_pathr  rE  r  z
  lineage:u   →u   ←z    ru  rn   z :: )r   rF   r  r  r   r  r   r   rg   r   r   )r   r   r  rl  r[  r~  exr  artsr   r  relsarrowr  s                 r5   cmd_showr    s   AT!TT](/cjjA!vvdjjAE:;A	Abr"W+
/0	L(
)*	L)*
+SUSYSYZcSdSdBFF9<M;N/Ojl
mn	L
+,
-.	vvf~~u^dii6
.CCD	vvhbffX.>-?@A	vvnu|BFF>4J3K%LMVVH#FZZ &B++G4:Ej?AEF1#,/?	I		$"B	vvhbl^<=	vvmel2m3D2E$FG	vvnR@P=QR=Qqcl 3=QR66+$"Dc$i[+,bfgbf]^vs+,BquuW~/U}AU.VW	Xbfgo.4"4 Q4!uuV}v.!%%+2O 4D QlAUU6]bffVn<E%E#(E>AEE$KquuV}EDqvtE7;<  ' @
  S 	hQs    Q/Q4AQ9AQ>Q>c           
     4  ^ ^^ [        T R                  5      mTS:X  aW  [        T R                  5      (       d  [        T R                  5      (       a  [        S5      e[        S[        R                  S9  [        T SS 5      nU(       a.  U[        ;  a$  [        SU SSR                  [        5       35      eU=(       d    [        R                  TS	5      mU UU4S
 jn[        SU5      u  p4[        U5        [        SU ST ST ST R                   35        g)Nr   a   refusing to capture a raw URL as a 'Read' item under Option A.
Use:  notice link capture <url> [--title T] [--note N] [--source S] [--tag T]
which records the URL in link_triage as 'unexplored' and lets the explainer pipeline promote it onto the Read page.zwarning: 'Read' is legacy under Option A; the Read page is now driven by Link Triage explainer-ready records. Continuing for backwards compatibility.r   rn   unknown type: r   r   r%   c           	       > U R                  S/ 5      n[        U Vs/ s H  o"R                  SS5      PM     sn=(       d    S/5      S-   n[        TR                  5      oDnSnU Vs1 s H  o"R                  S5      iM     nnXG;   a  U SU 3oFS-  nXG;   a  M  TR
                  n[        5       n	0 SU_SU_STR                  _S	T_S
T_SS_SS _SS_STR                  _S[        5       _SU	_SU	_STR                  =(       d    SU=(       d    TR                  U	S./_STR                  =(       d    / _SS _SX(       a  U/O/ S.S / S S S.S S S S.S._S/ _/ S SU S 3S!.En
UR                  U
5         [        XS"S#9  S&U S'T S T S(TR                   3X444$ s  snf s  snf ! [         a%  n[        S$U 3[        R                  S%9   S nANOS nAff = f))Nr{   r   r   rB   r   rt   r   r`   rn   ro   rT   r   r  rp   rP   rr   rq   rX   rY   r^   
notice CLIrb   rs   r  rl  rm  r[  rn  rp  ro  
started_atr  rq  completed_atrO   r  ru   rv   )r  
notes_pathrw   auto-on-capturerc   z)auto-connect: capture suggestion failed: r   r   (z) )rf   r  rg   rt   r`   r%   r0   rr   r8   r  rh   r  r  r  r   r  )r   r{   r   nidrU   baser~  takenr%   r[   rZ   excr   r  itypes               r5   _mutatecmd_capture.<locals>._mutate  sY   Wb)595a55q)59@aSAAEM4Q(-.1v.j&!AAvq jvvU
#
%q
*1177
E
 
 &w
 1:4
 BI'
 ahh	
 !-eg	
 8DR	

 
  8LXZ[\
 AEEKR
 "0
 +/D4&bQ&*bQUeij'+dS
 
  #A3a=!
$ 	T	Vq):; 3%r#awb	2SH<<= :.4  	V=cUC#**U	Vs#   F(F5
F$ $
G.GGrc  z
captured #r  rv   r   )r  ro   r  r`   r%   r   r  r   r  getattrTYPESr   rx   rg   r   r   )r   requested_typer  r  new_slugr  r  s   `    @@r5   cmd_capturer    s    
AJJ
'C
f}177##qvv'>'>D  	[	
 Q-N.5>.)9TYYuEUDVWXX?.223?E =D  	73MCx 	Jse2cU!E7"QWWI
67r4   c                  ^  [        [        5       T R                  5      nU(       aU  UR                  S5      S:X  a@  T R                  =(       d    SR                  5       (       d  [        S[        R                  S9  U 4S jn[        SU5        [        ST R                   S	3T R                  (       a  S
T R                   3OS-   5        g)Nro   r   ra   zhint: moving an Idea to Explore should answer the smallest validation/build step.
      Pass --next to record it. Continuing without one.r   c                z  > [        U TR                  5      nU(       d  [        STR                   35      eSUS'   S US'   SUS'   [        U5        [	        U5        UR                  S0 5      R                  SS / S S S.5      nTR                  (       a  TR                  US	'   TR                  (       a  TR                  US
'   TR                  (       a  TR                  US'   UR                  S5      =(       d
    [        5       US'   STR                   S3TR                  (       a  STR                   3-   $ S-   $ )Nr  r   rT   r  rQ   rp   rl  r  rn  ro  r   r  r  z moved to Explore    — ra   )rF   r  r   r\   ry   rf   rn  r   r   rg   r0   r   r  r  r   s      r5   r  cmd_explore.<locals>._mutateF  s   !QTT]7v>?? 8$"Y-i7r]]8R(33IbUY@
  88AHHR\66qvv2m$::

r*~66,/835<144&)*AHHahhZ.@UURTUUr4   rQ   r  z -> Explorez  next: r   )
rF   r   r  rg   r   rS   r  r   r  r   )r   prer  s   `  r5   cmd_explorer  <  s    
tvqtt
C
swwz"g-qvv|6J6J6L6L^	
V  	7#	AaddV;
!&&XaffX#6b
IJr4   c                t   ^  U 4S jn[        SU5        [        ST R                   ST R                   35        g)Nc                  > [        U TR                  5      nU(       d  [        STR                   35      eSUS'   TR                  US'   SUS'   SU;   a  SUS'   [	        U5        [        U5        UR                  S	0 5      R                  S0 5      nUR                  TR                  TR                  [        5       S
.5        UR                  S5      n[        U[        5      (       a#  [        TR                  5      US'   [        X5        STR                   STR                   3TR                  (       a  STR                   3-   $ S-   $ )Nr  r   rT   r  rR   rp   reading_queueFrl  r  r    r  z marked Done/r  ra   )rF   r  r   r  r\   ry   rf   updaterq  r0   rg   r   ry  _terminal_link_status_for_sync_link_triage_mirror)r   r  r  r    r   s       r5   r  cmd_done.<locals>._mutate\  s   !QTT]7v>??8qyyr)}"W+b "'BrMM(B'2262>	QYY199ceTUvvf~dD!!6qyyADN$Q+144&aii[1AIIuQYYK5H^^[]^^r4   rR   r  z	 -> Done/r   )r   r  r  r  r   r  s   ` r5   cmd_doner&  [  s5    _&  	AaddV9QYYK
()r4   c                     U S;   a  gU S:X  a  gg)z8Map item outcome onto the matching terminal link.status.)r   r   r   r   r   r3   )r  s    r5   r"  r"  t  s    '')r4   c           	       ^^ UR                  S5      n[        U[        5      (       d  gU R                  S/ 5      nUR                  S5      mUR                  S5      =(       d    [	        UR                  SS5      5      m[        UU4S jU 5       S5      nUct  TUR                  SS5      TUR                  S	S
5      UR                  S5      =(       d
    [        5       [        5       UR                  S5      SS.nUR                  U5        gUR                  S	UR                  S	S
5      5      US	'   TUS'   [        5       US'   UR                  S5      (       a	  US   US'   gg)aC  Push item.link state into the deprecated link_triage[] mirror.

Plan 03 keeps the mirror so existing renderers keep working for one
cycle. Every mutation that touches item.link also writes-through to
the matching mirror row (matched by item_slug + canonical_url).
Plan 4+ retires the mirror; this helper goes away with it.
r    Nlink_triagert   canonical_urlr'  ra   c              3     >#    U  HT  nUR                  S 5      T:X  d  M  UR                  S5      T:X  d!  [        UR                  SS5      5      T:X  d  MP  Uv   MV     g7f)	item_slugr*  r'  ra   Nrg   r,  )r   lt	canonicalrt   s     r5   r   +_sync_link_triage_mirror.<locals>.<genexpr>  sX      	DfFF;4' 
ff_%2!"&&"34	A 
fs   A2A	ArT   r   rq   rr  )r,  r'  r*  rT   captured_atrX   	explainerr[  rX   r2  )	rg   r   ry  rf   r,  r   r8   r0   rh   )r   rZ   r    triagerecr/  rt   s        @@r5   r#  r#  }  sB    88FDdD!!\\-,F88FD)R-=dhhub>Q-RI
	Df 	D 	C {88E2&&hhx688L1<UW%.1	
 	cHHXswwx'FGCM$CCxx/K  r4   c                t   ^  U 4S jn[        SU5        [        ST R                   ST R                   35        g)uK   Set or update next_action on an item (helper for the Ideas→Explore gate).c                >  > [        U TR                  5      nU(       d  [        STR                   35      eUR                  S0 5      R                  SS / S S S.5      nTR                  US'   [        U5        [        U5        STR                   STR                   3$ )Nr  rl  rQ   r  ro  r  z next_action set: )rF   r  r   rf   rh  r\   ry   r  s      r5   r  cmd_next.<locals>._mutate  s    !QTT]7v>??]]8R(33IbUY@
  FF=r144&*166(33r4   r   r  z next: r   )r   r  r  rh  r%  s   ` r5   cmd_nextr8    s4    
4  	AaddV7166(
#$r4   c                  ^ ^	^
^^^ T R                   S:X  a  [        5       nUR                  S5      =(       d    / nT R                  (       a2  U Vs/ s H%  o3R                  S5      T R                  :X  d  M#  UPM'     nnT R                  (       a   [        [        R                  " USSS95        gU HF  n[        SUR                  S5      S	 S
UR                  S5       SUR                  S5       S35        MH     [        S[        U5       S35        gT R                   S:X  aE  [        T R                  5      mU U4S jn[        SU5      n[        ST R                   SU S35        gT R                   S:X  Ga"  T R                  m[        T5      (       d  [        ST< 35      e[        T5      m	U U	U4S jn[        SU5      n[        U[        5      (       a<  UR                  S5      S:X  a'  [        SUR                  S5      =(       d    T 35        g[        U[        5      (       a  U=(       d    0 R                  S5      OUn[        U[        5      (       a  U=(       d    0 R                  S5      OS nU(       a  [!        U5        [        S!U S"T S#35        gT R                   S:X  aY  [        T R                  5      m[        T R                  5      m
U U
U4S$ jn[        S%U5        [        T R                   S&T 35        gg's  snf )(z0Manage link_triage records (Option A lifecycle).r{  r)  rT   r   Fr   r   [r  r  r'  z	   (item=r,  )r   z	 link(s).r  c           	     4  >^^ [        U TR                  5      mT(       d  [        STR                   35      eTR                  n[	        U5      mTR                  S5      =(       d    SnTR                  S5      n[        U[        5      (       d&  UTT	U(       a  SU S3OS S S [        T5      S.nUTS'   OGT	US'   TUS	'   UR                  S
U5        UR                  S[        T5      5        U(       a	  SU S3US'   [        TSU5        [        T5        [        T5        [        U T5        TR                  (       a;  U R                  S/ 5      n[        UU4S jU 5       S 5      nUb  TR                  US'   STR                   STR                   ST	 S3T	4$ )Nr  rt   ra   r    ru   /explainer.htmlr'  r*  rT   explainer_pathrr  explainer_written_atrs  rT   r*  r'  rs  r?  link.addr)  c              3     >#    U  Hc  nUR                  S 5      TR                  S5      :X  d  M)  UR                  S5      T:X  d!  [        UR                  SS5      5      T:X  d  M_  Uv   Me     g7f)r,  rt   r*  r'  ra   Nr-  )r   r.  r/  owners     r5   r   ,cmd_link.<locals>._mutate.<locals>.<genexpr>  sa      P&B{+uyy/@@ &&1Y>-bffUB.?@IM R&s   (A.2A.%	A.r[  r   <-  [])rF   r  r   r'  r,  rg   r   ry  
_domain_ofrf   rj   r\   ry   r#  r%   r   )
r   r'  
owner_slug
owner_linkr3  r4  r/  rC  r   resolved_statuss
         @@r5   r  cmd_link.<locals>._mutate  s   ADDME #;ADD6!BCC%%C(-I6*0bJ6*Jj$//%.-NXzl/&J^b$(,0(3
 !+f'6
8$.7
?+%%eS1%%h
90EF5;J<3WJ/0%S15!U#$Q.vvmR8P& P  ?#$66CLqttfDr/):!<oMMr4   rA  zlink added: r
  rc  znot a URL: c                &  > U R                  S/ 5      n[        U4S jU 5       S 5      nUcN  U R                  S/ 5      n[        U4S jU 5       S 5      nUb#  [        XR                  S5      =(       d    S5      nUGbD  TR                  =(       d    Sn[        X%T5        UR                  S5      =(       d    SnUR                  S	5      n[        U[        5      (       d%  TTS
U(       a  SU S3OS S S [        T5      S.nXrS	'   OBTUS'   UR                  ST5        UR                  S[        T5      5        U(       a	  SU S3US'   [        U5        [        U5        [        X5        ST ST SUR                  S5       S3nUSUR                  S5      UR                  S5      UR                  S5      =(       d    TTS.4$ TR                  =(       d    Tn	[        U	5      onSnU Vs1 s H  oR                  S5      iM     nnX;   a  U SU 3oS-  nX;   a  M  [        U Vs/ s H  oR                  SS5      PM     sn=(       d    S/5      S-   nTR                  =(       d    Tn[!        5       n0 SU_SU
_SU	_SS	_SS _S!S"_S#S _S$S%_S&S'_S(TR                  =(       d    S_S)[#        5       _S*U_S+U_S,TR                  =(       d    STUS-./_S.TR$                  =(       d    / _S/S _S0TU/S1.S / S S S2.S S S S3.S4._/ TTS
SU
 S3S S [        T5      S./ S SU
 S53S6.EnUR'                  U5        [        U U5         [)        X
S7S89  S;U S<T S=3S>XS?.4$ s  snf s  snf ! [*         a%  n[-        S9U 3[.        R0                  S:9   S nANBS nAff = f)@Nr{   c              3     >#    U  Hi  n[        UR                  S 5      [        5      (       d  M)  US    R                  S5      T:X  d$  [        US    R                  SS5      5      T:X  d  Me  Uv   Mk     g7fr    r*  r'  ra   Nr   rg   ry  r,  r   r  r/  s     r5   r   rD    sa      Tervvf~t4 jnn_5B)"V*..*CD	Q e   (A48A4+	A4r)  c              3     >#    U  H=  n[        UR                  S S5      5      T:X  d  UR                  S5      T:X  d  M9  Uv   M?     g7f)r'  ra   r*  N)r,  rg   )r   r.  r/  s     r5   r   rD    sA      >&B(r):;yH/9< R&s
   8A	Ar,  ra   zlink capturert   r    r   ru   r=  r>  r*  r'  rs  r?  zdedup: z merged into item (canonical=z, slug=r;  dedupedr   r`   )r   rt   r   r`   r/  r   r   rB   r   rn   ro   r   rT   r   r  rp   rP   r   Trr   rq   rX   rY   r^   rb   rs   r  rl  r  r  r  rO   rv   r  r    r  r  rw   r  r	  z.auto-connect: link.capture suggestion failed: r   r  rE  z [unexplored]captured)r   rt   r   )rf   r   r   rg   rr   rj   r   ry  rH  r\   ry   r#  r`   rt   r  r%   r0   r8   r  rh   r  r  r  r   r  )r   r{   existing_itemr3  existing_ltsrcexisting_slugr    r   r`   rU   r  r~  r   r  r  capture_noter[   rZ   r  r   r/  r'  s                       r5   r  rL    sl   LL"-E !Te T M $mR8">& > 	 *$0OOK4P4VTV$WM(hh0.m#6 !. 1 1& 9 ?R$((0!$--")2".UbF=/*Qhl(,04",Y"7D -1&),5D)OOE3/OOHj.CD$39-1X-.}- /(:cU #""+GM4E4Ef4M3NaQ  )(5(9(9&(A.;.?.?.M)6):):7)C)Js-6	 8 8 8 GGNsEUAT1q,12EqUU6]EE2*fAaSM61 *u=u!uu\1-u=D!EIC66=SLB c #)1 .5u   O  .6w  AJ4  	 
    !((4n  eg  (4R  #B  AHH$>sRTUV    &4T  /2l^L*."UYimn+/DRVW    %. +(.qc&A$(,0(3 "$"!'s!}? DB LL %Q-'"1->? se4uM2z<=5R R Rc 3 >V  'FseL::''s$   *M&M?
M! !
N+NNzlink.capturer   rT  zAlready captured (no-op): r`   r   rt   Nzcaptured link #ru  z (unexplored)c           	     R  > [        UU4S jU R                  S5      =(       d    /  5       S 5      nUGcK  U R                  S5      =(       d    / n[        UU4S jU 5       S 5      nU(       d  [        STR                   35      e[	        XR                  S5      =(       d    S5      nUc+  [        STR                   S	UR                  S5       S
35      e[        UR                  S5      [        5      (       d  UR                  S5      =(       d    SnUR                  S5      =(       d    TR                  UR                  S5      =(       d    TT	U(       a  SU S3OS UR                  S5      S [        T5      S.US'   US   nT	US'   TR                  (       a  TR                  US'   T	S:X  a\  SUS'   SUS'   SUS'   SU;   a  SUS'   UR                  S0 5      R                  S0 5      nSUS'   UR                  S[        5       5        OT	S:X  a\  SUS'   SUS'   SUS'   SU;   a  SUS'   UR                  S0 5      R                  S0 5      nSUS'   UR                  S[        5       5        OaT	S:X  a[  SUS'   SUS'   SUS'   SU;   a  SUS'   UR                  S0 5      R                  S0 5      nSUS'   UR                  S[        5       5        [        U5        [        U5        TR                  (       a  [        US TR                  5        [        X5        TR                   S!T	 3$ )"Nc              3  0  >#    U  H  n[        UR                  S 5      [        5      (       d  M)  US    R                  S5      T:X  dF  [        US    R                  SS5      5      T:X  d$  US    R                  S5      TR                  :X  d  M  Uv   M     g7frO  )r   rg   ry  r,  r'  )r   r  r   canonical_targets     r5   r   rD    s}      :2rvvf~t4 jnn_59II)"V*..*CDHXXF.!%%7	 2s   (BAB	Br{   r)  c              3     >#    U  H<  oR                  S 5      TR                  :X  d  UR                  S5      T:X  d  M8  Uv   M>     g7f)r'  r*  N)rg   r'  )r   r.  r   r^  s     r5   r   rD    s<      N6RVVE]aee5K!#!8<L!L !b6s
   7A	Azno link record for r,  ra   zlink_triage row for z points at missing item 'r   r    rt   r'  r*  ru   r=  r2  r>  rT   rr  r   r   r  rR   rp   r   Frl  r  r   r   zlink.status.note -> )r   rg   r   r'  r   r   ry  rH  r2  rf   r0   r\   ry   r%   rj   r#  )
r   rC  r3  foundowner_slug2r    r  r   r^  
new_statuss
          r5   r  rL    s   
 :aeeGn22 :
 E }}-3 N6 NOSU$':155'%BCC$Q		+(>(D"E=$.quug 6!IIk2316 
 "%))F"3T::"'))F"3"9rK$yy/8155).?)C)WGW",S^F;-*Odh(-		+(>04",-=">%E&M =D'DN{{ (){{^$V# #)h#)i !'g"e+-2E/*$$Xr2==fbI%)^SU3y("(h#,i !'g"e+-2E/*$$Xr2==fbI()^SU3z)"(h#-i !'g"e+-2E/*$$Xr2==fbI))^SU35!U#vve%7@$Q.eeWD--r4   zlink.statusr`  rB   )r   r   rg   rT   r   r  r   r   r  r   r'  r  r   r,  r   ry  r   )r   r   rowsr  r  
status_outr   r  slug_outr/  r^  rc  rK  r'  s   `        @@@@@r5   cmd_linkrg    s~   xx6Fuu]#)r88#Ct!uuX!(('BAtDC66$**T!%@A!AAaeeHoc*"QUU5\N)AEE+DVCWWXYZ 3t9+Y'(xx5.qxx8+	NZ !W5
QUUG2j\34xx9ees##{3'233$S)	o	Rb ^W5fd##

8(<	(I.vzz'/B/Ic.JKL  7A6N6N6<R$$\2TZC5?5M5M"))&1SWH"8,OC5#m<=xx8)!((3
+AEE2N	.` 	M7+tJ<()m Ds   "L6Lc                |     [        U 5      R                  =(       d    SR                  5       $ ! [         a     gf = f)Nra   )r	   r!  rE   r  )r'  s    r5   rH  rH    s7    $$*1133 s   +. 
;;c                  ^^^ U R                   mU R                  mU R                  =(       d
    [        5       m[	        U SS5      (       dX  [
        T-  nUR                  5       (       d  [        SU S35      e[        U5      (       a  [        ST 3[        R                  S9  UUU4S jn[        SU5        [        T S	T S
35        g)u*  Record that the explainer for ``slug`` is ready (Plan 06).

Used by the autonomous link-triage cron pass. Atomic update path:

  - ``items[].link.explainer_path`` is set to the supplied repo-relative
    path (typically ``items/<slug>/explainer.html``).
  - ``items[].link.explainer_written_at`` is stamped.
  - ``items[].link.status`` is promoted from ``unexplored`` to
    ``explainer_ready`` (no-op if already further along).
  - ``items[].phase`` is auto-promoted ``inbox`` → ``explore`` so the
    item surfaces on the read lane.
  - The deprecated ``link_triage[]`` mirror is synced via the existing
    helper so v2.x renderers stay correct for one transition cycle.

Failure path: if the slug isn't a link item, or the path doesn't exist
on disk, fail loudly. Cron pipelines depend on this contract.
no_verify_pathFz&set-explainer: file does not exist at z8. Write the explainer HTML before calling set-explainer.z8set-explainer: inserted missing item-page backlink into r   c                  > [        U T5      =(       d    [        U [        T5      5      nU(       d  [        ST S35      eUR	                  S5      S:w  a!  [        ST SUR	                  S5       S35      eUR	                  S5      n[        U[        5      (       d  [        ST S35      eTUS	'   T	US
'   UR	                  S5      nUS:X  a  SUS'   UR	                  S5      nUS:X  aa  SUS'   UR	                  S5      S:X  a  SUS'   UR                  S0 5      R                  SS / S S S.5      nUR	                  S5      (       d  T	US'   [        U5        [        U5        [        X5         [        XR	                  S5      =(       d    SSS9  T ST SU SUS    SU SUR	                  S5       S 3$ ! [         a%  n[        SU 3[        R                  S9   S nANPS nAff = f)!Nno item with slug or id 'r   rn   r    zset-explainer: item '' is type 'z', not 'link'.zL' has no embedded link sub-object. Run `notice link add` or recapture first.r?  r@  rT   r   r   rp   rP   rQ   r   r   rl  r  r  rt   ra   zauto-on-explainerr	  z/auto-connect: set-explainer suggestion failed: r   z set-explainer z (link.status z->z, phase r;  )r   rF   _try_intr   rg   r   ry  rf   r\   ry   r#  r  r  r  r   r  )
r   r  r    old_link_status	old_phaser  r  rI   slug_arg
written_ats
          r5   r  "cmd_set_explainer.<locals>._mutate  s   !X&E$q(82D*E8
!DEE66&>V#'z 2FF6N#>3  vvf~$%%'z 2H H  "&'1#$((8,l*.DNFF7O	#BwK vvh7*(8x,77	"#4D B 66,''#-< r '	#q&&."6B<OP
 jv .+,BtH~.> ?Kr"&&/!2!5	
  	#CC5Izz#	#s   +"F3 3
G"=GG"set-explainerz: explainer ready (r;  r   )rt   rI   rr  r0   r  r   existsr   rM   r  r   r  r   )r   r  r  rI   rq  rr  s      @@@r5   cmd_set_explainerrv    s    $ vvH66D&J 1&..}}8 AI I  %V,,J4&QZZ
.
` )	XJ)$q
12r4   c                   ^  T R                   [        ;  a.  [        ST R                    SSR                  [        5       35      eU 4S jn[	        SU5        [        T R                   ST R                    35        g)z6Change an item's `type` without changing its category.r  r   r   c                n  > [        U TR                  5      =(       d    [        U [        TR                  5      5      nU(       d  [	        STR                   S35      eUR                  S5      nTR                  US'   [        U5        [        U5        UR                  S5       SU STR                   3$ )Nrl  r   rn   rt   z retype r`  )	r   rt   rF   rn  r   rg   rn   r\   ry   )r   r  oldr   s      r5   r  cmd_retype.<locals>._mutate>  s    !QVV$AQ0@(A8BCCffVnVV6
r&&.!#d166(;;r4   retypez retyped to r   )rn   r  r   r   r   r  rt   r%  s   ` r5   
cmd_retyper|  9  sd    vvU>!&&TYYu=M<NOPP< '"	QVVHL
)*r4   c                   ^ ^ [        T R                  5      mU U4S jn[        SU5        [        T R                   ST 35        g)z6Change an item's `category` without changing its type.c                F  > [        U TR                  5      =(       d    [        U [        TR                  5      5      nU(       d  [	        STR                   S35      eUR                  S5      nTUS'   [        U5        [        U5        UR                  S5       SU ST 3$ )Nrl  r   ro   rt   z recategorize r`  )r   rt   rF   rn  r   rg   r\   ry   )r   r  ry  r   r  s      r5   r  !cmd_recategorize.<locals>._mutateQ  s    !QVV$AQ0@(A8BCCffZ :r&&.!uD>>r4   recategorizez recategorized to r   )r  ro   r   r  rt   )r   r  r  s   ` @r5   cmd_recategorizer  M  s=    
AJJ
'C? (	QVVH&se
,-r4   z,https://hermes-agent.tail8e09c4.ts.net/items)pending	confirmedfailedc                    SU  S3$ )Nz~/AI/projects/rv   r3   )rt   s    r5   _default_project_folderr  p  s    D6##r4   c                   ^^ U R                   mSS0mUU4S jn[        SU5        TS   (       a  [        ST S35        g[        ST S35        g)	a"  Mark an idea/project as graduated and queue a pending folder spawn.

Idempotent: a re-run on an already-graduated item is a no-op and prints
a friendly message. The actual ``~/AI/projects/<slug>/`` folder is
created MacBook-side by ``mc_graduate_sync.py``; the Pi only records
intent here.
flagFc                p  >^ [        U T
5      =(       d    [        U [        T
5      5      nU(       d  [        ST
 S35      eUR	                  S5      nUS;  a!  [        SUR	                  S5       SU S35      eUR	                  S5      S	:X  a  S
T	S'   g UR	                  S5      =(       d    T
m[        5       nSUS'   S	US'   SUS'   UR                  S0 5      nUR	                  S5      =(       d    [        T5      US'   UR                  SU5        UR                  S0 5      R                  S0 5      nS	US'   UR                  SU5        [        U5        [        U5        U R                  S/ 5      n[        U4S jU 5       S 5      nUct  UR                  TUR	                  ST5      US[         ST S3UR	                  S5      UR	                  S5      UR	                  S5      UR	                  S5      S.S.5        O/UR                  SU5        UR	                  S5      =(       d    SUS'   T S US    3$ )!Nrl  r   rn   )r!   r#   z%graduate is for idea/project items; 'rt   rm  r  r   Tr  r   rT   rR   rp   rt  project_foldergraduated_atrl  r  pending_graduationsc              3     >#    U  H5  n[        U[        5      (       d  M  UR                  S 5      T:X  d  M1  Uv   M7     g7fr   r  r   gslug_actuals     r5   r   0cmd_graduate.<locals>._mutate.<locals>.<genexpr>  s3      <EqZ4-@eeFm{: E   A A 	A r`   r  rv   premise	kill_ruleposture)r  r  r  r  )rt   r`   	queued_atrT   nb_item_urlrt  r  z graduated -> )r   rF   rn  r   rg   r0   rf   r  r\   ry   r   rh   ITEM_NB_URL_BASE)r   r  r  r[   r  r  queueexistingr  alreadyrq  s           @r5   r  cmd_graduate.<locals>._mutate  s6   !X&E$q(82D*E8
!DEEv++FF6N#;ugQ8  66)+"GFOffVn0U8#97]],b1!vv&67_;RS^;_
nb)MM(B'2262>")	^R(r 2B7 <E <=ACLL#5#"2!31[MC&(ff-=&>!vvi0!#!4!vvi0	$  R0!)h!7!D9HXnR0@-A,BCCr4   graduate	[notice] z: already graduatedzP: graduation queued. Run `mc-graduate-sync` on your MacBook to spawn the folder.r   rt   r   r  )r   r  r  rq  s     @@r5   cmd_graduater  t  sj     vvHuoG0Dd 
G$v	(#678 	 	z "J J	
 r4   c                f   ^ U R                   mU4S jn[        SU5        [        ST ST S35        g)zFReverse the NB-side graduation link without touching the local folder.c                  > [        U T	5      =(       d    [        U [        T	5      5      nU(       d  [        ST	 S35      eUR	                  S5      =(       d    T	nSnUR	                  S5      S:X  a=  S US'   SUS'   S	US
'   UR                  S0 5      R                  S0 5      nS US'   S US'   SnUR	                  S5      =(       d    0 nSU;   a  UR                  SS 5        SnSU;   a  UR                  SS 5        SnU(       a  [        U5        [        U5        [        U R	                  S5      =(       d    / 5      nU R	                  S5      =(       d    /  Vs/ s H1  n[        U[        5      (       a  UR	                  S5      U:X  a  M/  UPM3     snU S'   U[        U S   5      -
  nU(       d  U(       d  g U SU SUS:w  a  SOS S3$ s  snf )Nrl  r   rt   Fr  r   r   rT   rQ   rp   rl  rR   r  Trt  r  r  r  z ungraduate (removed z pending rowrB   rU   ra   r;  )r   rF   rn  r   rg   rf   popr\   ry   r   r   ry  )
r   r  r  changedr  r  beforer  removedrq  s
            r5   r  cmd_ungraduate.<locals>._mutate  s   !X&E$q(82D*E8
!DEEffVn066)+ ByM$BxL#BwKh+66vrBAAiL $AnGVV%&,"r!FF#T*GRFF>4(G2R QUU017R834::$
:!q$''AEE&M[,H :$

  3q!6788wm yGqLSb,IL	
$
s   .G	
G	
ungraduater  z.: ungraduated. The local folder ~/AI/projects/z/ was NOT removed.r   r  )r   r  rq  s     @r5   cmd_ungraduater    sE    vvH#
J g&	
H: **23E	G r4   c                   ^^^ U R                   m[        U SS5      mSS0mUUU4S jn[        SU5        [        ST S35        g	)
zDCalled by ``mc_graduate_sync.py`` after the local folder is created.created_pathNr  Fc                2  >^ [        U T	5      =(       d    [        U [        T	5      5      nU(       d  [        ST	 S35      eUR	                  S5      =(       d    T	mU R	                  S5      =(       d    / n[        U4S jU 5       S 5      nUc  [        ST 35      eUR	                  S5      S:X  a  S	TS
'   g [        5       nSUS'   XCS'   T(       a2  TUS'   UR                  S0 5      nTUS'   [        U5        [        U5        T S3T(       a  ST 3-   $ S-   $ )Nrl  r   rt   r  c              3     >#    U  H5  n[        U[        5      (       d  M  UR                  S 5      T:X  d  M1  Uv   M7     g7fr   r  r  s     r5   r   :cmd_confirm_graduation.<locals>._mutate.<locals>.<genexpr>  s3      6u!
1d(;Af4 Aur  zno pending graduation for rT   r  Tr  confirmed_atr  rt  r  z graduation confirmedz at ra   )
r   rF   rn  r   rg   r   r0   rf   r\   ry   )
r   r  r  rowr[   r  r  r  r  rq  s
         @r5   r  'cmd_confirm_graduation.<locals>._mutate  s+   !X&E$q(82D*E8
!DEEffVn0+,2 6u 67;=;9+GHH778+"GFOU#H N".C0"5B#/B 2R 34%1d<.!
 	
79
 	
r4   confirm-graduationr  z: graduation confirmedr   )rt   r  r   r  )r   r  r  r  rq  s     @@@r5   cmd_confirm_graduationr    sJ    vvH1nd3LuoG
4 $g.	IhZ5
67r4   )reason)r  r  r  r  )goalworking_definitionsuccess_criteriar^   )
north_starstack	decisionsrepo_url
deploy_urlr  )r   r   rT   r3   zdict[str, tuple[str, ...]]TEMPLATE_FIELDS_BY_TYPE>   r  r^   r  r  )r  doingrR   r   c                   ^ ^^ T R                   mT R                  mU UU4S jn[        SU5        [        T R                   ST 35        g)zGSet an item's ``template_fields.<key>`` value, with per-type whitelist.c           
     :  > [        U T	R                  5      =(       d    [        U [        T	R                  5      5      nU(       d  [	        ST	R                   S35      eUR                  S5      =(       d    Sn[        R                  US5      nU(       d  [	        SU S35      eT
U;  a$  [	        ST
 S	U S
SR                  U5       S35      eT
[        ;   a   [        R                  " T5      nOTnUS:X  a1  T
S:X  a+  U[        ;  a!  [	        SSR                  [        5       35      eT
S:X  a   [        U[        5      (       d  [	        S5      eT
S;   a#  [        U[        5      (       d  [	        T
 S35      eUR!                  S0 5      nUR                  T
5      nXFT
'   [#        U5        [%        U5        UR                  S5       ST
 ST 3nUb  US[        R&                  " USS9 S3-  nU$ ! [        R                   a  n[	        ST
 SU S35      eS nAff = f)Nrl  r   rn   r%   r3   ztype 'z!' has no settable template fieldszUnknown field 'z' for type 'z
'. Valid: r   r   zvalue for 'z' must be JSON (z/); e.g. '["a","b"]' or '{"frontend":"Next.js"}'r$   rT   zadmin.status must be one of r  zstack must be a JSON object)r  r^   r  z must be a JSON listrt  rt   z set z = z (was F)r   r;  )r   rt   rF   rn  r   rg   r  r   _TEMPLATE_FIELD_JSON_KEYSr   r   JSONDecodeError_ADMIN_STATUSESr   ry  r{  rf   r\   ry   r   )r   r  r  allowedparseder  ry  rq  r   r  rd   s            r5   r  cmd_set.<locals>._mutate+  s$   !QVV$AQ0@(A8BCCv(&)--eR8@A  g!#l5' :))G,-Q0 
 ++C FGxF/4Q.tyy/I.JK  '>*VT":"::;;>>"6400u$89::]],b1ffSk3rVVF^$E#c#7?

3U CDAFFG3 ''  !#&6qc :M N s   G/ /HHHr  z: set r   )valuer  r   r  rt   )r   r  r  rd   s   ` @@r5   cmd_setr  &  sA    
''C
%%C,\ w	QVVHF3%
 !r4   c                F     [        U 5      $ ! [        [        4 a     gf = f)Nr@   )r  	TypeErrorr  r   s    r5   rn  rn  ^  s'    1vz" s   
   c                  ^  T R                   [        ;  a&  [        ST R                    3[        R                  S9  gU 4S jn[        SU5        [        ST R                   ST R                    ST R                   35        g	)
z*Add a relationship edge between two items.zunknown relationship type: r   rB   c                  > [        U TR                  5      n[        U TR                  5      nU(       a  U(       d  [        S5      eU R	                  S/ 5      nUR                  UR                  S5      UR                  S5      TR                  [        5       S.5        STR                   STR                   STR                   3$ )Nzone or both items not foundr  rt   )rE  r  rn   rq   r  ru  z #)	rF   id_fromid_tor   rf   rh   rg   rn   r0   )r   rY  dstr  r   s       r5   r  cmd_relate.<locals>._mutatej  s    1aii 1agg#:;;||OR0GGFOGGFOFF%	
 	 199+Qqvvhb	22r4   relatezedge: #z -[z]-> #r   )rn   	REL_TYPESr  r   r  r   r  r  r%  s   ` r5   
cmd_relater  e  sf    vvY+AFF843::Fq3 '"	GAII;c!&&qwwi
89r4   c                    [        X5      =(       d    [        U [        U5      5      nU(       d  [        SU S35      eU$ )zJLook up an item by slug or numeric display_id; raise SystemExit otherwise.rl  r   )r   rF   rn  r   )r   rd   r  s      r5   _resolve_slug_argr  }  s9    	e	!	?T%#%?B4SE;<<Ir4   c                4  ^^^^ U R                   mU R                  =(       d    SmU R                  b  [        U R                  5      O[        mU R
                  b  [        U R
                  5      O[        mUUUU4S jn[        SU5        [        T S35        g)zCompute (and persist) suggested_relationships for one item.

Used by the cron pass and by humans during manual debugging. Threshold
and top-K are tunable via flags for testing.
r  c                   > [        U T5      n[        XS   TTTS9nU(       a  [        U5        [        U5        U(       d  g SR	                  S U 5       5      nUS    S[        U5       SU 3$ )Nrt   r  r   c              3  <   #    U  H  oS     SUS    S3v   M     g7f)r  (r  r;  Nr3   )r   rU   s     r5   r   ;cmd_compute_suggestions.<locals>._mutate.<locals>.<genexpr>  s#     Le)~Qqzl!<es   z compute-suggestions u	    new — )r  r  r\   ry   r   r   )r   r  r  snippetsrq  r  r  rc   s       r5   r  (cmd_compute_suggestions.<locals>._mutate  su    "1h/"1Vn'*iuN6"V$99LeLL.!!6s5zl)H:VVr4   compute-suggestionsz: compute-suggestions doner   )
rt   rc   r  r  AUTO_CONNECT_THRESHOLDr  r  AUTO_CONNECT_TOP_Kr   r  )r   r  rq  r  r  rc   s     @@@@r5   cmd_compute_suggestionsr    s|     vvH
%%
&&C&'kk&=akk"CYIGG/CL5GE
W 
W %w/	XJ0
12r4   c                  ^^^^^ U R                   b  [        U R                   5      O[        mU R                  b  [	        U R                  5      O[
        mU R                  =(       d    Sm[        [        U SS5      5      mU R                  b  [        U R                  5      OSmUUUUU4S jn[        SU5        T(       a  [        S5        g[        STS	 S
35        g)a	  Run the auto-connect pass over recent (or all) items.

Defaults to items modified in the last 24h. Pass ``--recent-hours N`` to
widen the window or ``--all`` to walk every item. Stale suggestions
older than ``AUTO_CONNECT_PRUNE_DAYS`` are always pruned at the end.
r  r/  Fg      8@c           	       >^ U R                  S5      =(       d    / n[        R                  " [        R                  5      nU[        TS9-
  n/ nU H  nUR                  S5      m[        T[        5      (       a  T(       d  M2  T(       a  UR                  T5        ML  [        UR                  S5      =(       d)    UR                  S5      =(       d    UR                  S5      5      nUc  M  Xc:  a  M  UR                  T5        M     SnU HY  m [        U TTTTS9nU(       d  M  U[        U5      -  n[!        U4S jU 5       S 5      n
U
c  MC  [#        U
5        [%        U
5        M[     ['        U TS9nU(       d  U(       d  g S[        U5       SU SU S3$ ! [         a)  n	[        S	T S
U	 3[        R                  S9   S n	A	M  S n	A	ff = f)Nr{   )hoursrt   rY   rX   rq   r   r  zauto-connect: failed for : r   c              3  T   >#    U  H  oR                  S 5      T:X  d  M  Uv   M     g7fr   r   )r   r  slug_vs     r5   r   4cmd_auto_connect.<locals>._mutate.<locals>.<genexpr>  s      #S2&&.F:RBBr   )r  zauto-connect pass over z item(s): added z suggestion(s), pruned z stale)rg   r   r0   r   r1   r   r   r   rh   r  r  r  r  r   r  r   r   r\   ry   r  )r   r{   now_dtr  targetsr  r[   added_totalr  r  r  r  r  r  r  r  rc   walk_alls               @r5   r  !cmd_auto_connect.<locals>._mutate  s   g$"hll+)%00BVVF^Ffc**&v&BFF#56 4!vvl34!vvl35B z{NN6"  F*1f/2i168 us5z)"#S#SUYZ*";/$[1  *!yAv%c'l^ 4 M!8H	
  1&C5A

Ss   F
G
!GG
auto-connectzauto-connect: walked all itemsz0auto-connect: walked items modified in the last r  hr   )r  r  r  r  r  r  rc   r  r  recent_hoursr   r  )r   r  r  r  r  rc   r  s     @@@@@r5   cmd_auto_connectr    s     '(kk&=akk"CYIGG/CL5GE
%%
&&CGAue,-H%&^^%?E!..!TE)
 )
V (./  	@q	KLr4   c                   ^^^ U R                   mU R                  mU R                  =(       d    SmTS;  a  [        ST S35      eUUU4S jn[	        SU5        [        ST ST 35        g	)
u   Promote a (pending) suggestion to a confirmed relationship.

Mirrors on the other side so both items reference each other. Accepts a
suggestion that never existed too — useful when the user types a
connect command from outside the suggested list.
manualautor  zunknown --via 'z'. allowed: auto, manualc                  >^^ [        U T
5      n[        U T	5      nUS   mUS   mTT:X  a  [        S5      e[        5       nUR                  S5      =(       d    / n[	        U4S jU 5       S 5      nUR                  S/ 5        [        U4S jUS    5       5      (       d6  US   R                  TSUUc  TOUR                  S5      =(       d    TS	.5        U Vs/ s H1  n[        U[        5      (       a  UR                  S
5      T:X  a  M/  UPM3     snUS'   [        U5        [        U5        UR                  S/ 5        [        U4S jUS    5       5      (       d  US   R                  TSUSS	.5        UR                  S5      =(       d    /  Vs/ s H1  n[        U[        5      (       a  UR                  S
5      T:X  a  M/  UPM3     snUS'   [        U5        [        U5        T ST ST S3$ s  snf s  snf )Nrt   z cannot connect an item to itselfr  c              3     >#    U  H5  n[        U[        5      (       d  M  UR                  S 5      T:X  d  M1  Uv   M7     g7fr  r  )r   rU   dst_slugs     r5   r   /cmd_connect.<locals>._mutate.<locals>.<genexpr>  s9      OA#At, 12y1AX1M ar  r  c              3  z   >#    U  H0  n[        U[        5      =(       a    UR                  S 5      T:H  v   M2     g7fr  r  )r   r  r  s     r5   r   r    5      20q a&G155+;x+GG0r  relatedrc   )r  r  r  rc   r  c              3  z   >#    U  H0  n[        U[        5      =(       a    UR                  S 5      T:H  v   M2     g7fr  r  )r   r  src_slugs     r5   r   r    r  r  mirror <-> z connected (via=r;  )r  r   r0   rg   r   rf   r1  rh   r   ry  r\   ry   )r   rY  r  r[   src_sugsr  rU   r  r  	other_argrq  rc   s          @@r5   r  cmd_connect.<locals>._mutate  s   8,9-v;v;x?@@U7745; O O 	+ 202 2 2 ''#! "#ms%))E2B2Ic	)   *
!q$''AEE),<,H x*
%& 	s+ 202 2 2 ''#! "	)  ww89?R?*
?!q$''AEE),<,H ?*
%& 	s5
*:3%qAA-*
 *
s   .G1G1.G6G6connectz
connected r   r   )rt   r  rc   r   r   r  )r   r  r  rq  rc   s     @@@r5   cmd_connectr    sn     vvHI
%%
8C
$$?3%/GHII,B\ 	7#	Jxji[
12r4   c                   ^^ U R                   mU R                  mUU4S jn[        SU5        [        ST ST 35        g)u   Remove a pending suggestion on both sides without promoting it.

No-op if the suggestion doesn't exist on either side (but never errors
— the goal is a stable "stop suggesting this" outcome).
c                  > [        U T5      n[        U T5      nUS   nUS   nSnUR                  S5      =(       d    / nU Vs/ s H1  n[        U[        5      (       a  UR                  S5      U:X  a  M/  UPM3     nn[	        U5      [	        U5      :w  a  XS'   [        U5        [        U5        SnUR                  S5      =(       d    / n	U	 Vs/ s H1  n[        U[        5      (       a  UR                  S5      U:X  a  M/  UPM3     n
n[	        U
5      [	        U	5      :w  a  XS'   [        U5        [        U5        SnU(       d  g SU SU 3$ s  snf s  snf )Nrt   Fr  r  Tzdismissed suggestion r   )r  rg   r   ry  r   r\   ry   )r   rY  r  r  r  touchedr  rU   new_src_sugsdst_sugsnew_dst_sugsr  rq  s              r5   r  'cmd_dismiss_suggestion.<locals>._mutate*  sQ   8,9-v;v;7745;#+ W8a *1d 3 3i8HH8T 8 W|H--9)*3S!G7745;#+ W8a *1d 3 3i8HH8T 8 W|H--9)*3S!G&xjhZ@@#WWs   .E6E.EEdismiss-suggestionz
dismissed r   r   )rt   r  r   r  )r   r  r  rq  s     @@r5   cmd_dismiss_suggestionr  !  sB     vvHIA4 $g.	Jxji[
12r4   c                  ^ U R                   n[        U SS5      (       a#   [        U R                  5      R	                  SS9nU(       d  [        S5      e [        R                  " U5      m[        T[        5      (       d  [        S	5      e[        TR                  S
5      [        5      (       a  TS
   R                  5       (       d  [        S5      eU4S jn[        SU5        [!        STS
    S35        g! [
         a   n[        SU R                   SU 35      eSnAff = f! [        R                   a  n[        SU 35      eSnAff = f)u  Plan 09 — write ``brain.json.morning_brief`` transactionally.

Accepts the JSON payload as the first positional argument or via
``--from-file PATH``. The payload must be an object with at least
``generated_at`` (ISO string); ``summary`` / ``generated_for_date`` /
``highlights`` are optional. Validation defers to ``validate_brain``
so the same shape contract is enforced everywhere.
	from_fileNr=   r>   zset-morning-brief: cannot read r  zTset-morning-brief: pass a JSON payload as the first argument or use --from-file PATHz#set-morning-brief: not valid JSON: z0set-morning-brief: payload must be a JSON objectgenerated_atz@set-morning-brief: payload.generated_at (ISO string) is requiredc                X   > TU S'   TR                  S5      =(       d    TS   S S nSU 3$ )Nmorning_briefgenerated_for_dater  
   zmorning_brief updated for r   )r   gfdr   s     r5   r  &cmd_set_morning_brief.<locals>._mutatef  s:    $/kk./O7>3J3B3O+C511r4   set-morning-briefz$morning_brief updated (generated_at=r;  r   )json_payloadr  r   r  rC   r   r   r   r   r  r   ry  rg   r   rS   r   r  )r   payload_rawr  r  r   s       @r5   cmd_set_morning_briefr  I  sD    ..Kq+t$$	Sq{{+55w5GK &
 	
D**[) gt$$KLLgkk.1377w~?V?\?\?^?^[\\2
 #W-	01H0I
KL/  	S>q{{m2aSQRR	S  D>qcBCCDs/   "C8 D% 8
D"DD"%E9EEc                   [         R                  R                  S[        [        5      5        SSKn[        5       nUR                  S/ 5      nUR                  5       n[        S[        R                  " 5       R                  S5       S35        UR                  S5      =(       d    /  Vs/ s H  nUR                  S5      S	:X  d  M  UPM     nn[        S
[        U5       S35        USS  H3  n[        SUR                  S5      S SUR                  S5       35        M5     [        5         / nU H?  nUR                  X5      n	U	(       d  M  UR                  XR!                  X5      U45        MA     UR#                  S S9  [        S[        U5       S35        USS  H$  u  pn[        SU	S SU
 SUS    SUS    35        M&     [        5         U Vs/ s H3  oR                  S5      S:X  d  M  UR%                  U5      (       a  M1  UPM5     nn[        S[        U5       S35        USS  H  n[        SUS    SUS    35        M     [        5         gs  snf s  snf )z>Print the same lanes the Today dashboard surfaces (text form).r   Nr{   u   === Today · z%A %Y-%m-%dz ===
r)  rT   r   zLink Triage queue (r     z  [r  r  r'  c                     U S   =(       d    S* $ )NrB   r   r3   )ri  s    r5   r  cmd_today.<locals>.<lambda>  s    QqTYQ<r4   r  zInbox rot (z<8zd  #r   r  r`   r   u   Explore — need next-action (z  #)r   rI   r   r   r   mc_pagesr   rg   now_utcr  r   r0   r2   r   inbox_decay_levelrh   inbox_decay_ager  item_next_action)r   r   r   r{   r  r.  triage_pendingdecay_itemsr  lvlageneeds               r5   	cmd_todayr*  p  s6   HHOOAs4y!AEE'2EF	M(,,.11-@A
HI$%EE-$8$>B$> ;$>R)\9 $>N ;	N 34B
78Ra BFF8$S)BFF5M?;< !	GK((43%=%=b%I2NO  /0	KK()
,-#BQ"C82cU$r,'7&82g;-HI (	Gf2&&"2i"?BHaHabdHeBDf	*3t9+R
892AhB|$%R7}56 	G/;$ gs   +III4IIc                R  ^^^^ SS K nU R                  S:X  GaO  [        5       nUR                  S5      =(       d    / nU Vs/ s H1  oDR                  S5      SL d  M  UR                  S5      S:w  d  M/  UPM3     nnUR	                  S S	9  U R                   (       a  [        UR                  " US
SS95        gU H  nUR                  S5      =(       d    0 nUR                  S5      (       a  SUR                  S5      S S  3OSn[        SUR                  S5      S SUR                  S5      S SUR                  S5       U 35        M     [        S[        U5       S35        gU R                  S:X  al  U R                  m[        5       m[        T5      mUUU4S jn[        SU5      u  pT(       a  [        U	5        [        SU
 S T S!35        g[        S"U
 S#35        gU R                  S$:X  a.  U R                  mU4S% jn[        S&U5      n
[        S'U
 S(35        gg s  snf ))Nr   r{  r{   r   Trp   rR   c                &    U R                  SS5      $ )Nr   r   r   r  s    r5   r  cmd_read.<locals>.<lambda>  s    !%%a"8r4   r  r   Fr   r    r'  r`  r   ra   r  r   z<4rF  ro   r  r  r`   r   z items in reading queue.r  c                V  >^ U R                  S/ 5      nS nT(       GaE  [        T5      m[        U4S jU 5       S 5      nU(       Gd  Tn[        U5      oDnSnU Vs1 s H  owR	                  S5      iM     nnXH;   a  U SU 3oFS-  nXH;   a  M  [        U Vs/ s H  owR	                  SS5      PM     sn=(       d    S/5      S-   n	0 SU	_SU_S	U_S
S_SS_SS_SS _SS_SS_SS_S[        5       _ST_ST_SSTTS./_S/ _SS _STT/S.S / S S S .S S S S!.S"._/ TTS#S$U S%3S S [        T5      S&./ S S$U S'3S(.EnUR                  U5        [        X5         [        XS)S*9  O)O([        U T5      =(       d    [        U [        T5      5      nU(       d  [        S+T S,35      eSUS'   [!        U5        [#        U5        [        X5        S-UR	                  S5       S.UR	                  S5       3n
XR	                  S5      UR	                  S5      44$ s  snf s  snf ! [         a     Nf = f)/Nr{   c              3     >#    U  Hi  n[        UR                  S 5      [        5      (       d  M)  US    R                  S5      T:X  d$  [        US    R                  SS5      5      T:X  d  Me  Uv   Mk     g7frO  rP  rQ  s     r5   r   ,cmd_read.<locals>._mutate.<locals>.<genexpr>  sa      X%B"266&>48 V*..9YF-bjnnUB.GHIU R%rR  r   rt   r   rB   r   r   r`   rn   r    ro   r   rT   r   r  rp   rP   r   Trr   zread queue addrq   rX   rY   r^   rb   rs   r  rl  r  r  r  rO   r   ru   r=  r>  rv   rU  r  r	  zno item or URL found matching 'r   zadd to reading_queue #ru  )rf   r,  r   rt   rg   r  r8   rH  rh   r#  r  r  r   rF   rn  r   r\   ry   )r   r{   rZ   r`   rU   r  r~  r   r  r  r   r/  
is_url_str
target_argr[   s              @r5   r  cmd_read.<locals>._mutate  s   LL"-ED,Z8	X% X  &EUAT1q49:EqUU6]EE:*#fAaSM61 *uEu!uu\15uEL!MPQQC$c+116=u #O 6>w IRSW  	
 ( !"2 %eg 0<R +B ",<ZWY$Z#[  %3D !7AZL'Y26"]aqu'v37DZ^'_#  &(#--6&206qc.I,048&0&;! *,&*)/s!}9D< LL&,Q5*15FGQ X $Az2Sd1hz>R6S #B:,a!PQQ$(D!4 T"$Q--dhh|.D-EQtxxPVGWFXYFHHV,dhh|.DEEEg ; FF % s   HH
H 
H('H(zread.addzCaptured new link #ru  z and added to reading queue.zItem #z added to reading queue.removec                  > [        U T5      =(       d    [        U [        T5      5      nU(       d  [        ST S35      eSU;   a  SUS'   [	        U5        [        U5        SUR                  S5       3nX!R                  S5      4$ )Nz!no item with slug or display_id 'r   r   Fzremove from reading_queue #r   )r   rF   rn  r   r\   ry   rg   )r   rZ   r   rq  s      r5   r  r3    s    8,KQ8J0KD #DXJa!PQQ$&(-_%4 T"2488L3I2JKF88L111r4   zread.removezRemoved item #z from reading queue.)r   r   r   rg   r  r  r   r   r  r0   r  r   r   r  )r   r   r   r{   r  queuedr    
url_suffixr  r  r  r1  rq  r2  r[   s              @@@@r5   cmd_readr8    s   xx6Fg$"$fu(?4(G"BFFSZO_eLe"uf8966$**VAEBCB66&>'RD:>((5//4 456rJAbff\*2.b
1CC0H266RY?J[\f[ghi  	3v;-789xx5XX
U$Z0
B	FH $J8x('uAj\9UVW  F3%789xx844		2 -1se#789! { gs   H$H$4H$c                Z    [         R                  " U [        [        5      S9R                  $ )N)cwd)
subprocessrunr   r   
returncode)cmds    r5   r<  r<  	  s    >>#3t9-888r4   c            	        [         R                  " SSS9n U R                  SSS9nUR                  S5      nUR	                  S[
        S	9  UR	                  S
5        UR	                  S5        UR	                  S5        UR	                  SSS9  UR                  [        S9  UR                  S5      nUR	                  S[        S9  UR	                  SSS9  UR                  [        S9  UR                  S5      nUR	                  S5        UR	                  S
SS9  UR	                  S[        S	9  UR	                  SSS9  UR	                  S5        UR	                  SSS9  UR                  [        S9  UR                  S5      nUR	                  S[        S9  UR	                  S5        UR	                  S5        UR	                  S 5        UR                  [        S9  UR                  S!5      nUR	                  S[        S9  UR	                  S"5        UR                  [        S9  UR                  S#5      nUR	                  S[        S9  UR	                  S$S[        S%9  UR	                  S&5        UR                  [        S9  UR                  S'5      nUR                  S(SS9nUR                  S)S*S+9nUR	                  S[        S9  UR	                  S,5        UR	                  S5        UR	                  S5        UR                  [         S9  UR                  SS-S+9nUR	                  S,5        UR	                  S.5        UR	                  S5        UR	                  S5        UR	                  SSS9  UR                  [         S9  UR                  S/5      nUR	                  S,5        UR	                  S/5        UR	                  S05        UR	                  S5        UR                  [         S9  UR                  S5      nUR	                  S5        UR	                  SSS9  UR                  [         S9  UR                  S15      nUR	                  S2[        S9  UR	                  S3["        S	9  UR	                  S4[        S9  UR                  [$        S9  UR                  S5S6S+9nUR	                  S7S8S+9  UR	                  S9S:S+9  UR	                  S;S<S=S>9  UR	                  S?S@SSASB9  UR                  [&        S9  UR                  SCSDS+9nUR	                  S7SES+9  UR	                  S3[        S	9  UR                  [(        S9  UR                  SFSGS+9nUR	                  S7SES+9  UR	                  SH5        UR                  [*        S9  UR                  SISJS+9nUR	                  S7SKS+9  UR                  [,        S9  UR                  SLSMS+9nUR	                  S7SES+9  UR                  [.        S9  UR                  SNSOS+9nUR	                  S7SES+9  UR	                  SPSQS SRSS9  UR                  [0        S9  UR                  STSUS+9nUR	                  S7SES+9  UR	                  SVSWS+9  UR	                  SXSYS+9  UR                  [2        S9  UR                  SZS[S+9nUR	                  S\S][4        S S^S_9  UR	                  S`SSaSb9  UR	                  Sc[4        S Sd[6         Se3Sf9  UR	                  SgSh[        S Si[8         Se3S_9  UR	                  SjS SkSl9  UR                  [:        S9  UR                  SmSnS+9nUR	                  S7SES+9  UR	                  Sc[4        S So9  UR	                  SgSh[        S Sp9  UR	                  SjS S9  UR                  [<        S9  UR                  SqSrS+9nUR	                  S7SsS+9  UR	                  StSuS+9  UR	                  SjSvS SwSx9  UR                  [>        S9  UR                  SySzS+9nUR	                  S7SES+9  UR	                  StSuS+9  UR                  [@        S9  UR                  S{S|S+9nUR	                  S}S~S SS9  UR	                  SSS SSS9  UR                  [B        S9  UR                  S5      nUR                  [D        S9  UR                  S5      R                  S S9  UR                  S5      R                  S S9  UR                  S/5      R                  S S9  UR                  SSS+9nUR                  S(SS9nUR                  S)SS+9n	U	R	                  SSS+9  U	R                  [F        S9  UR                  SSS+9n
U
R	                  SSS+9  U
R                  [F        S9  UR                  SSS+9nUR	                  SSS9  UR                  [F        S9  UR                  S5      nUR	                  SSSS9  UR                  S S9  U $ )NnoticezNotice Board CLI)progdescriptionr>  T)destrequiredr{  z--status)choicesz
--categoryz--tagz--queryz--json
store_true)r   )funcshowr  )rn   rc  r`   )rD  z--typez--sourcer  )defaultz--noterh   rQ   z--intentz--nextz
--deadliner   rh  rR   z	--outcome)rD  rE  z	--summaryr    r   r  z/attach a URL to an existing item as link_triage)helpr'  z=capture a raw URL into link_triage (creates placeholder item)z--titlerT   z--explainerr  r  rn   r  rt  zHrecord that items/<slug>/explainer.html is ready (used by the cron pass)rt   z+item slug or display_id (must be type=link)rI   z4repo-relative path, e.g. items/<slug>/explainer.htmlz--written-atrr  z)ISO timestamp to record (defaults to now))rC  rJ  z--no-verify-pathrj  z6skip the on-disk file-existence check (tests/dry runs))rC  r   rJ  r{  z1change an item type without touching its categoryzitem slug or display_idr  z1change an item category without touching its typero   r  zfmark an idea/project as graduated and queue a pending ~/AI/projects/<slug>/ spawn for the MacBook syncz3item slug or display_id (must be type=idea|project)r  zgreverse a graduation on the Notice Board side; does NOT delete the spawned ~/AI/projects/<slug>/ folderr  zfcalled by mc-graduate-sync after the local folder lands; marks the pending_graduations[] row confirmedz--created-pathr  zHabsolute path the MacBook sync wrote (defaults to ~/AI/projects/<slug>/))rC  rI  rJ  r  z8set an item's template_fields.<key> (per-type whitelist)r  z%template field name (depends on type)r  zYstring value, or JSON for structured fields (stack, decisions, success_criteria, sources)r  z0compute suggested_relationships for recent itemsz--recent-hoursr  z;look at items modified within the last N hours (default 24))rC  rn   rI  rJ  z--allz*walk every item (overrides --recent-hours))r   rJ  z--thresholdz!cosine similarity floor (default r;  )rn   rI  rJ  z--top-kr  z"max suggestions per item (default z--viaz>tag stamped on each new suggestion (default cron-auto-connect))rI  rJ  r  z1compute suggested_relationships for a single item)rn   rI  )rC  rn   rI  r  z:promote a (pending) suggestion to a confirmed relationshipz.item slug or display_id (one side of the edge)r  zother item slug or display_idr  z\record how this connection was confirmed (default: 'manual', or the suggestion's stored via))rE  rI  rJ  r  z:drop a pending suggestion on both sides without confirmingr  z>write brain.json.morning_brief (used by morning_brief_pass.py)r  r  z|JSON object with at least generated_at; e.g. '{"generated_at": "...", "summary": "...", "generated_for_date": "YYYY-MM-DD"}')nargsrI  rJ  z--from-filer  z1read the JSON payload from a file instead of argvr8   validatec                R    [        [        R                  [        [        5      /5      $ r
  )r<  r   
executabler   	VALIDATORr   s    r5   r  parser.<locals>.<lambda>  s    3PST]P^?_;`r4   renderc                R    [        [        R                  [        [        5      /5      $ r
  )r<  r   rN  r   RENDERrP  s    r5   r  rQ    s    cnncRXk=Z9[r4   c                    [        / SQ5      $ )N)gitrT   z--shortz--branch)r<  rP  s    r5   r  rQ    s
    =e9fr4   r   z$manage the first-class reading queuez'add an item or URL to the reading queuer  zdisplay_id, slug, or raw URLr4  z%remove an item from the reading queuezdisplay_id or slugz&list active items in the reading queuepublishz-mz	--messagezNotice Board updatec           
         [        S5      [        S[         S35      [        S[         S[        R                  " U R                  5       35      [        S[         S35      S4S   $ )Nz# dry run; review then run:zgit -C z add -Az commit -m z pushr   r@   )r  r   r   r   messagerP  s    r5   r  rQ    sb    +,vW%&v[AII)>(?@AvU#$	# 	#
r4   )$argparseArgumentParseradd_subparsers
add_parseradd_argumentSTATUSESset_defaultsr  r  r  r  r  r  r8  OUTCOMESr&  rg  r  r  rv  r|  r  r  r  r  r  r  r  r  r  r  r  r  r  r*  r8  )r(  r   r  lsublalclsllrsubrarrrls               r5   parserrk    s	   X;MNA




5CvANN:xN0NN< NN7Q^^I6NN8LN1NNN!vANN4cN"ANN8LN$QNNN!y!ANN7NN<$N/NN8UN+NN:|N4NN8annWXnFNNN$y!ANN4cN"ANN:$>NN8ann\:NNN$vANN4cN"ANN6$:NNN!vANN4cN"NN;xN@NN;NNN!vAD9D	%V	WBOODsO#R__U%;OOJOOHOOO"	] 
 
_BOOEOOIOOHOOJOOGHO-OOO"		"BOOEBOOH5OOM"BOOH$=OOO"		 BOOJOOH\O2OOO"x ANN93N'NN69N-NN7N%NN
N#W 	 	A NN6 MNNNN6 VNWNN>C  ENN%,<\P  RNN)N*x&YZANN6 9N:NN65N)NN
N#~,_`ANN6 9N:NN:NN(N)@ 	 	A
 NN6 UNVNNN%8 	 	A
 NN6 9N:NNN'7 	 	A
 NN6 9N:NN#.$1  2 NN.N/G 	 	A NN6 9N:NN5FNGNN=  
 NNN ? 	 	A NN#.udU  WNN7<D  FNN=ud;<R;SSTU  WNN97d<=O<PPQR  TNN7DX  ZNN(N)@ 	 	A NN6 9N:NN=udN;NN97dNCNN7DN)NN/N0I 	 	A NN6 PNQNN7!@NANN7$6N  O NNN$I 	 	A NN6 9N:NN7!@NANN.N/M 	 	A NN>d@  A NN={DK  MNN-N.wANN	N"NN:++1`+aNN8))/[)\NN8))/f)gv$JKAD9D	%N	OBOOH#AOBOOO"	(O	PBOOD3O4OOO"	&N	OBOOH\O2OOO" 	y!ANN4.CNDNN 
N  Hr4   c                T    [        5       R                  U 5      oR                  U5      $ r
  )rk  
parse_argsrG  )argvr   s     r5   mainro    s    D!A&&)#3r4   __main__)r:   r   returnr  )rI   r   rq  r  )rT   
str | Nonerq  r   )rZ   ry  rq  Noner
  )rZ   ry  rc   r   rd   rr  rq  rs  )rZ   ry  rl   r   rq  ry  )rt   r   rq  r   )r   r  )rI   r   r   r   rq  rs  )rd   r   rq  r   )rd   rr  rq  r   )rU   rr  rq  r  )r'  r   rq  r   )rh  r   rq  z	list[str])rZ   ry  rq  r   )r{   
list[dict]rq  zFtuple[list[tuple[str, dict[str, float]]], dict[str, dict[str, float]]])r   dict[str, float]r   ru  rq  r  )
r   ry  r  r   r  r  r  r  rq  rt  )r   ry  r  r   rc   r   r  r  r  r  rq  rt  )r   ry  r  r  r  r  rq  r  )rU   r   rq  zdatetime | None)r  rr  rq  r   )r   ry  rZ   ry  rq  rs  )rt   r   rq  r   )rU   r   )r   ry  rd   r   rq  ry  )__doc__
__future__r   rZ  
contextlibr   r   r  r   r   r;  r   r   r   collectionsr   r   r   r   pathlibr   urllib.parser	   r
   r   r   __file__resolver   r   r   r   rT  rO  r   LOCK_TIMEOUT_SECONDSr   r   r_  ra  r  r  rx   TYPE_TO_CATEGORYr  r  r0   r8   rG   r;   rM   rV   r\   rj   ry   r   r   r   r   r   contextmanagerr   r   r   rt   rF   r   r   r  r  compile
IGNORECASEr  r  	frozensetr$  r,  r  r  AUTO_CONNECT_PRUNE_DAYSre  rg  rj  r  r  r  r  r  r  r  r  r  r  r  r&  r"  r#  r8  rg  rH  rv  r|  r  r  GRADUATION_STATUSESr  r  r  r  r  __annotations__r  r  r  rn  r  r  r  r  r  r  r  r*  r8  r<  rk  ro  __name__exitr3   r4   r5   <module>r     s   C " W W W W W W  2 2  C CH~&&--|z		922	 	 

 z  ( NP@ ""     #N3 `	E; A :?
"
 8@ 09[  	  4  B& 5I ?DVSML
S0 **]BMM
20   C:    JJ)*	  
,9xB" 1G'9-O(--O!$-O>H-Ob #6*@$677"'7 "7 <F7t :Q0F (- KN F 0#L<~>2&0R&]@	Vr(> B 8 $EP.b"R EM#07 3   :5p08=@=@%P$N Hrj9Xv4 zHHTV r4   