
    FWjo6                       d Z ddlmZ ddlZddlmc mZ ddl	Z	ddl
Z
ddlZddlZddlmZ g dZg dZej"                  j%                  de      d        Zej"                  j%                  d	e      d
        Zd Zd Zd Zd Zdddg dddddgg g g dddddddddd d!gd"dd#d$dd%gd&Zd'd(d)d*gd+d,Zd9d-Zd. Zd/ Zd0 Zd1 Zd2 Z d3 Z!ej"                  jE                  e
jF                  jI                  d4      d5k7  d67      d8        Z%y):uX  Entity Dossier card tests — the explicit-ask intercept, the mention
auto-show, the /profile fetch + emit contract (voice/entity_show.py), and the
HUD→engine contract (control/entity_action.json, applied by
ControlWatcher._entity_action). The JSON payloads here are EXACTLY what the
Mac HUD's EntityPanel writes, so these tests pin the contract between the
Swift card and the Python engine. No network, no HUD, no audio (fakes).

Run:            .venv/bin/python -m pytest test_entity_card.py -q
Live smoke:     LIVE=1 .venv/bin/python -m pytest test_entity_card.py -q -k live
  (LIVE hits the real memory service /profile AND writes a real
   control/entity_action.json so a RUNNING Jarvis pops the card on screen —
   the eyes-on proof that the whole chain renders. Run it after any deploy
   that touches the card, the endpoint, or the intercept.)
    )annotationsN)entity_show))z$pull up everything we know about AINAIN)zjarvis pull up AINr   )zshow me Saud AltamimiSaud Altamimi)who is MohamedMohamed)zwho's HishamHisham)ztell me about AlrugaibAlrugaib)z%tell me everything about Reval StudioReval Studio)zwhat do we know about DinasorDinasor)z"pull up the profile of Wafi EnergyzWafi Energy)u   من هو سعودu   سعود)u    كل شي نعرفه عن عينu   عين)let's push the code nowzpull upz6the show me nothing phrase isn't at the end so no tailzutterance,expectedc           	        g | j                  t        dfd       | j                  t        dd        t        j                  j                          t        j                  |      }g }|}|rA|d   }d}||k(  }|}|r1|j
                  }	 |	       }
|d   }|j
                  } |       }|
|v }|}|sdd	d
t        j                         v st        j                  |      rt        j                  |      nd
iz  }|j                  |       |r=t        j                  dfdf      t        j                  |      t        j                  |      dz  }dd|iz  }|j                  |       |rt        j                  dfd
f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  	      t        j                  |
      t        j                        t        j                        t        j                  |      dz  }dd|iz  }|j                  |       t        j                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}x}x}x}	x}
x}x}x}}g }}r1|j
                  } |       }d   }|j
                  } |       }||v }|}|stdd	dt        j                         v st        j                        rt        j                        ndiz  }|j                  |       rt        j                  dfd|f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                        t        j                  |      t        j                        t        j                        t        j                  |      dz  }dd|iz  }|j                  |       t        j                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}x}x}x}x}}y) zexplicit_ask runs the show INLINE (truth over speed) and returns
    (status, name) so the brain note can never claim a card that isn't there._showc                4    j                  |       xs d| g fS Nshownappendnfireds    >/Users/ahmed/devFolder/Ultron/claude-voice/test_entity_card.py<lambda>z.test_explicit_ask_fires_show.<locals>.<lambda>8       5<<?#Fw26F#F    mentioned_entitiesc                    g S N ts    r   r   z.test_explicit_ask_fires_show.<locals>.<lambda>9       Rr   r   r      %(py2)spy2res==z%(py5)s == %(py8)spy5py8%(py10)spy10in)z%(py16)s
{%(py16)s = %(py14)s
{%(py14)s = %(py12)s.lower
}()
} in %(py23)s
{%(py23)s = %(py21)s
{%(py21)s = %(py19)s.lower
}()
}expected)py12py14py16py19py21py23z%(py25)spy25zassert %(py28)spy28Nr   )z{%(py8)s
{%(py8)s = %(py6)s
{%(py6)s = %(py4)s.lower
}()
} in %(py15)s
{%(py15)s = %(py13)s
{%(py13)s = %(py11)s.lower
}()
})py4py6r,   py11py13py15z%(py17)spy17zassert %(py20)spy20)setattrr   	_COOLDOWNclearexplicit_asklower@py_builtinslocals
@pytest_ar_should_repr_global_name	_safereprr   _call_reprcompare_format_boolopAssertionError_format_explanation) monkeypatch	utterancer1   r&   @py_assert1@py_assert0@py_assert4@py_assert7@py_assert6@py_assert13@py_assert15@py_assert18@py_assert20@py_assert22@py_assert17@py_format3@py_format9@py_format11@py_format24@py_format26@py_format27@py_format29@py_assert5@py_assert10@py_assert12@py_assert14@py_assert9@py_format16@py_format18@py_format19@py_format21r   s                                   @r   test_explicit_ask_fires_showrl   2   s    EWGI%9<H!

"
"9
-CK3K33q6KWK6W$KK)9KSVKV\\K\^K)9^)KKKKKKK3KKK3KKKK3KKK6WKKK6KKKWKKKKKKK)9^KKKKKKKKKKKKKKK)9KKKVKKK\KKK^KKKKKKKKKKKKKKK9595X^^9^%9q99)99%)999999995999599995999%)9999999X999X999^999%999999999)999999999999999r   rP   c                   g | j                  t        dfd       | j                  t        dd        t        j                  j                          t        j                  |      }|t
        d   t
        d   fv rCg }d }||u }|}|r }|}|s$t        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
z  }dd|iz  }	|j                  |	       |rXdddt        j                         v st        j                        rt        j                        ndiz  }
|j                  |
       t        j                  |d      i z  }dd|iz  }t        t        j                  |            d x}x}x}x}}y y )Nr   c                4    j                  |       xs d| g fS r   r   r   s    r   r   z4test_explicit_ask_ignores_non_asks.<locals>.<lambda>D   r   r   r   c                    g S r   r   r    s    r   r   z4test_explicit_ask_ignores_non_asks.<locals>.<lambda>E   r"   r   r   r#   is)z%(py2)s is %(py5)sresult)r%   r+   z%(py7)spy7znot %(py9)spy9r   assert %(py13)sr=   )rA   r   rB   rC   rD   NEGATIVE_CASESrH   rK   rF   rG   rI   rJ   r   rL   rM   rN   )rO   rP   rr   rQ   rS   @py_assert3rR   rd   @py_format6@py_format8r^   @py_format12@py_format14r   s                @r   "test_explicit_ask_ignores_non_asksr|   @   s   EWGI%9<H!%%i0F ^A&q(9::++v~+e)+)+++v++++++v+++v+++++++++++++e+++e+++++++++++++ ;r   c                   | j                  t        dd        | j                  t        dd        t        j                  j                          t        j                  d      }g }|}|r|d   }d}||k(  }|}|sdd	d
t        j                         v st        j                  |      rt        j                  |      nd
iz  }|j                  |       |r_t        j                  dfdf      t        j                  |      t        j                  |      dz  }dd|iz  }	|j                  |	       t        j                  |d      i z  }
dd|
iz  }t        t        j                  |            dx}x}x}x}}y)u   'pull up reval clients' when no such entity exists → ('unknown', …) so
    the brain says "nothing found" instead of "on your HUD, sir" (the gaslight
    Ahmed hit).r   c                    d| g fS )Nunknownr   r   s    r   r   z7test_explicit_ask_truthful_on_unknown.<locals>.<lambda>S   s    Ar8Jr   r   c                    g S r   r   r    s    r   r   z7test_explicit_ask_truthful_on_unknown.<locals>.<lambda>T   r"   r   z&pull up everything about reval clientsr   r   r$   r%   r&   r'   r)   r*   r-   r.   ru   r=   N)rA   r   rB   rC   rD   rF   rG   rH   rI   rJ   r   rK   rL   rM   rN   )rO   r&   rQ   rR   rS   rT   rU   r\   r]   r^   rz   r{   s               r   %test_explicit_ask_truthful_on_unknownr   O   s     W.JK%9<H!

"
"#K
LC&3&33q6&Y&6Y&&&&&&&3&&&3&&&&3&&&6Y&&&6&&&Y&&&&&&&&&&&&&&r   c                   g | j                  t        dfd       | j                  t        dd        t        j                  j                          t        j                  }d} ||      }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  t              rt        j                  t              nd	t        j                  |      t        j                  |      t        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}x}x}}dg}|k(  }|st        j                  d|fd|f      dt        j                         v st        j                        rt        j                        ndt        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}}y)uD   'show me saud' + known 'Saud Altamimi' → fires the canonical name.r   c                4    j                  |       xs d| g fS r   r   r   s    r   r   z8test_explicit_ask_prefers_known_entity.<locals>.<lambda>^   r   r   r   c                
    ddgS )NSaudr   r   r    s    r   r   z8test_explicit_ask_prefers_known_entity.<locals>.<lambda>`   s	    6?";r   zshow me saud)r   r   r'   zR%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.explicit_ask
}(%(py4)s)
} == %(py9)sr   py0r%   r:   r;   rt   assert %(py11)sr<   Nr   )z%(py0)s == %(py3)sr   )r   py3assert %(py5)sr+   )rA   r   rB   rC   rD   rH   rK   rF   rG   rI   rJ   rM   rN   )rO   rQ   rw   rc   @py_assert8rT   @py_format10rz   @py_assert2@py_format4rx   r   s              @r   &test_explicit_ask_prefers_known_entityr   Z   sB   EWGI%9;=!##QNQ#N3Q7QQ37QQQQQ37QQQQQQQ;QQQ;QQQ#QQQNQQQ3QQQ7QQQQQQQQ$%%5%%%%%5%%%%%%%5%%%5%%%%%%%%%%%r   c                   g | j                  t        dfd       | j                  t        dd        t        j                  j                          t        j                  }d} ||      }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  t              rt        j                  t              nd	t        j                  |      t        j                  |      t        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            d x}x}x}x}}t        j                  }d} ||      }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  t              rt        j                  t              nd	t        j                  |      t        j                  |      t        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            d x}x}x}x}}t              }d}||k(  }	|	st        j                  d|	fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                        rt        j                        ndt        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            d x}x}	}y )Nr   c                4    j                  |       xs d| g fS r   r   r   s    r   r   z5test_explicit_ask_double_fire_guard.<locals>.<lambda>i   r   r   r   c                    g S r   r   r    s    r   r   z5test_explicit_ask_double_fire_guard.<locals>.<lambda>j   r"   r   r   r   r   r'   r   r   r   r   r<   r#   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenr   r   py1r   r;   assert %(py8)sr,   )rA   r   rB   rC   rD   rH   rK   rF   rG   rI   rJ   rM   rN   r   )rO   rQ   rw   rc   r   rT   r   rz   r   rS   @py_format7r]   r   s               @r   #test_explicit_ask_double_fire_guardr   f   s   EWGI%9<H!##M$4M#$45M9MM59MMMMM59MMMMMMM;MMM;MMM#MMM$4MMM5MMM9MMMMMMMM##M$4M#$45M9MM59MMMMM59MMMMMMM;MMM;MMM#MMM$4MMM5MMM9MMMMMMMMu::?:33uu:r   c                   | j                  t        dd        | j                  t        dd        t        j                  j                          t        j                  }d} ||      }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  t              rt        j                  t              nd	t        j                  |      t        j                  |      t        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}x}x}}t        j                  j                          t        j                  }d} ||      }d}||u }|st        j                  d|fd||f      d	t        j                         v st        j                  t              rt        j                  t              nd	t        j                  |      t        j                  |      t        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}x}x}}y)zmain.py's integration point: handle_utterance returns (status, name) on
    an explicit ask (driving a truthful brain note), None otherwise.r   c                    d| g fS r   r   r   s    r   r   zFtest_handle_utterance_returns_status_on_explicit_ask.<locals>.<lambda>u   s    !R8Hr   r   c                    g S r   r   r    s    r   r   zFtest_handle_utterance_returns_status_on_explicit_ask.<locals>.<lambda>v   r"   r   r   r   r'   )zV%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.handle_utterance
}(%(py4)s)
} == %(py9)sr   r   r   r<   Nr   rp   )zV%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.handle_utterance
}(%(py4)s)
} is %(py9)s)rA   r   rB   rC   handle_utterancerH   rK   rF   rG   rI   rJ   rM   rN   )rO   rQ   rw   rc   r   rT   r   rz   s           r   4test_handle_utterance_returns_status_on_explicit_askr   r   s    W.HI%9<H!''Q(8Q'(89Q=QQ9=QQQQQ9=QQQQQQQ;QQQ;QQQ'QQQ(8QQQ9QQQ=QQQQQQQQ!''J(AJ'(ABJdJBdJJJJBdJJJJJJ;JJJ;JJJ'JJJ(AJJJBJJJdJJJJJJJJr   ainr   thingz2026-07-01T00:00:00+00:00)keynameetypealiases
created_atzAIN is Reval Studio's client. 
0544067114)phonesemailsurlsamounts      z
2026-07-15)facts	relations
last_touchr   companyzmentioned-withoutzAhmed's studio.)otherotypertype	directionother_summaryx1factu   Ain lead — Mohammad.)iddatekindr   tone)entitysummaryreadquickcountsr   timelineu   AIN — Reval Studio's clientWhozA lead handed to Reval Studio.)titlebodyF)headlinesectionscachedc                    t        j                          |z   } |        sNt        j                          |k  r6t        j                  d        |        st        j                          |k  r4y y y y )Ng{Gz?)timesleep)predtimeoutdeadlines      r   	_wait_forr      sK    yy{W$Hfx/

4 fx/f/fr   c                  ) g )| j                  t        dd        | j                  t        dd        | j                  t        d)fd       t        j                  d      }g }d}|j                  } |       }||v }|}|sd	}|j                  }	 |	       }
||
v }|}|st	        j
                  d
|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd|iz  }|j                  |       |st	        j
                  d
fd
f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  	      t	        j                  |
      dz  }dd|iz  }|j                  |       t	        j                  |d      i z  }dd|iz  }t        t	        j                  |            dx}x}x}x}x}x}x}x}x}	}
g })})r)d   d   }d}||k(  }|}|sdddt        j                         v st	        j                  )      rt	        j                  )      ndiz  }|j                  |       )r_t	        j
                  d|fd|f      t	        j                  |      t	        j                  |      dz  }d d!|iz  }|j                  |       t	        j                  |d      i z  }d"d#|iz  }t        t	        j                  |            dx}x}x}x}})d   d   }g }|d$   }d%}||u }|}|r|d&   }d}||u }|}|st	        j
                  d'|fd(||f      t	        j                  |      t	        j                  |      d)z  }d*d+|iz  }|j                  |       |r_t	        j
                  d'fd,|f      t	        j                  |      t	        j                  |      d-z  }d.d/|iz  }|j                  |       t	        j                  |d      i z  }d0d1|iz  }t        t	        j                  |            dx}x}x}x}x}x}x}}|d	   } | d2   d3   }d}!||!k(  }|slt	        j
                  d|fd4||!f      t	        j                  |      t	        j                  |!      d5z  }"d6d7|"iz  }t        t	        j                  |            dx}x}}!| d2   d8   }d9}!||!k(  }|slt	        j
                  d|fd4||!f      t	        j                  |      t	        j                  |!      d5z  }"d6d7|"iz  }t        t	        j                  |            dx}x}}!| d:   d;   }t        |t              }|sd<d=t        j                         v st	        j                  t              rt	        j                  t              nd=t	        j                  |      d>t        j                         v st	        j                  t              rt	        j                  t              nd>t	        j                  |      d?z  }#t        t	        j                  |#            dx}}| d@   }t        |t              }|sd<d=t        j                         v st	        j                  t              rt	        j                  t              nd=t	        j                  |      d>t        j                         v st	        j                  t              rt	        j                  t              nd>t	        j                  |      d?z  }#t        t	        j                  |#            dx}}t!        )fdA       g }t#        )      }dB}||k(  }|}|r)d   d   }d}||k(  }$|$}|st	        j
                  d|fdC||f      dDt        j                         v st	        j                  t"              rt	        j                  t"              ndDdt        j                         v st	        j                  )      rt	        j                  )      ndt	        j                  |      t	        j                  |      dEz  }d d!|iz  }|j                  |       |r_t	        j
                  d$fdF||f      t	        j                  |      t	        j                  |      dGz  }dHdI|iz  }%|j                  |%       t	        j                  |d      i z  }dJdK|iz  }&t        t	        j                  |&            dx}x}x}x}x}x}x}$})d   d   }'|'d$   }dL}!||!u }|slt	        j
                  d'|fdM||!f      t	        j                  |      t	        j                  |!      d5z  }"d6d7|"iz  }t        t	        j                  |            dx}x}}!|'d&   }|t$        k(  }|st	        j
                  d|fdN|t$        f      t	        j                  |      dOt        j                         v st	        j                  t$              rt	        j                  t$              ndOdPz  }(dQdR|(iz  }#t        t	        j                  |#            dx}}|'d	   d2   d3   }d}!||!k(  }|slt	        j
                  d|fd4||!f      t	        j                  |      t	        j                  |!      d5z  }"d6d7|"iz  }t        t	        j                  |            dx}x}}!y)Su   STAGE 1: card pops instantly (composing=True, composed=None). STAGE 2 (on
    a daemon thread): the composed dossier swaps in (composing=False,
    composed=<dict>). The spoken confirmation returns after STAGE 1 — it never
    waits for composition.fetch_profilec                     t        t              S r   dictFAKE_PROFILEr   s    r   r   z1test_show_entity_two_stage_emit.<locals>.<lambda>   	    \@Rr   fetch_composedc                     t        t              S r   )r   FAKE_COMPOSEDr   s    r   r   z1test_show_entity_two_stage_emit.<locals>.<lambda>   s	    mATr   emitc                *    j                  | |f      S r   r   r   kweventss     r   r   z1test_show_entity_two_stage_emit.<locals>.<lambda>       FMM5"+,Fr   r   showingprofiler/   )zD%(py3)s in %(py9)s
{%(py9)s = %(py7)s
{%(py7)s = %(py5)s.lower
}()
}msg)r   r+   rs   rt   z%(py11)sr<   )zJ%(py14)s in %(py20)s
{%(py20)s = %(py18)s
{%(py18)s = %(py16)s.lower
}()
})r3   r4   py18r@   z%(py22)spy22r#   zassert %(py25)sr8   Nr   entity_profiler$   r%   r   r'   r)   r*   r-   r.   ru   r=   	composingTcomposedrp   z%(py3)s is %(py6)sr   r;   %(py8)sr,   z%(py11)s is %(py14)sr<   r3   %(py16)sr4   assert %(py19)sr5   r   r   )z%(py1)s == %(py4)sr   r:   assert %(py6)sr;   r   r   r   r   z5assert %(py5)s
{%(py5)s = %(py0)s(%(py2)s, %(py3)s)
}
isinstancelist)r   r%   r   r+   r   c                      t               dk\  S N   r   r   s   r   r   z1test_show_entity_two_stage_emit.<locals>.<lambda>       c&kQ&r   r   )z0%(py5)s
{%(py5)s = %(py2)s(%(py3)s)
} == %(py8)sr   )r%   r   r+   r,   )z%(py13)s == %(py16)s)r=   r4   z%(py18)sr   zassert %(py21)sr6   F)z%(py1)s is %(py4)s)z%(py1)s == %(py3)sr   r   r   r   r+   )rA   r   show_entityrE   rH   rK   rJ   rF   rG   rI   r   rL   rM   rN   r   r   r   r   r   )*rO   r   rQ   r   rU   r   rS   rR   rV   r[   @py_assert19rW   r   rz   rk   @py_format23r_   r`   rT   r\   r]   r^   r{   e1rc   rd   re   r   @py_format15@py_format17ri   @py_format20r   rw   @py_format5rx   rf   rj   @py_format22e2r   r   s*                                            @r   test_show_entity_two_stage_emitr     s   
 F_6RS%57TUVFH

!
!%
(C?9?		?	?9#?y?CII?IK?yK'?????9???9????????????	??????????yK???y??????C???C???I???K???????????????6666fQil6&66l&666666666666666666666l&6666l666&666666666666666	1B=2k?=d=?d"=r*~==~'====?d===?===d=======~===~=================mG8V$--$----$---$----------8W%00%0000%000%0000000000g&x07:0$77777777:777:7770777777$777$7777777777j)0:)400000000:000:000)000000400040000000000&'@3v;@!@;!@q	!@0@@0@ @@@@@;!@@@@@@3@@@3@@@@@@v@@@v@@@;@@@!@@@@@@@0@@@@@@@0@@@@@@@@@@@@@@@	1Bk?#e#?e####?e###?###e#######j>*>]****>]***>******]***]*******i="6*3e3*e3333*e333*333e3333333r   c                   g | j                  t        dd        | j                  t        dd        | j                  t        dd       | j                  t        dfd       t        j                  d	       g }d
   d   d   }d}||u }|}|rd
   d   d   }d}||u }|}|st        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }	dd|	iz  }
|j                  |
       |r_t        j                  dfdf      t        j
                  |      t        j
                  |      dz  }dd|iz  }|j                  |       t        j                  |d
      i z  }dd|iz  }t        t        j                  |            dx}x}x}x}x}x}x}}t        fd       t              }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j
                  t              ndd t        j                         v st        j                        rt        j
                        nd t        j
                  |      t        j
                  |      d!z  }	d"d|	iz  }
t        t        j                  |
            dx}x}}d   d   }g }|d   }d}||u }|}|r|d   }d#}||u }|}|st        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }	dd|	iz  }
|j                  |
       |r_t        j                  d|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }|j                  |       t        j                  |d
      i z  }dd|iz  }t        t        j                  |            dx}x}x}x}x}x}x}}y)$u   POST /dossier 404s / times out / returns null → STAGE 2 still emits, with
    composed=None composing=False, so the HUD drops the spinner and renders the
    raw profile. STAGE 1 is unaffected.r   c                     t        t              S r   r   r   s    r   r   z;test_show_entity_compose_failure_degrades.<locals>.<lambda>   r   r   r   c                     y r   r   r   s    r   r   z;test_show_entity_compose_failure_degrades.<locals>.<lambda>   s    r   _COMPOSE_RETRY_Sg        r   c                *    j                  | |f      S r   r   r   s     r   r   z;test_show_entity_compose_failure_degrades.<locals>.<lambda>   r   r   r   r   r#   r   Tr   Nrp   r   r   r   r,   r   r   r   r4   r   r5   c                      t               dk\  S r   r   r   s   r   r   z;test_show_entity_compose_failure_degrades.<locals>.<lambda>   r   r   r   r'   r   r   r   r   r   F)rA   r   r   rH   rK   rJ   r   rL   rM   rN   r   r   rF   rG   rI   )rO   rQ   r   rc   rS   rR   rd   rV   re   r   r]   r  r  ri   r  r  r   s                   @r   )test_show_entity_compose_failure_degradesr     sy    F_6RS%5~F%7=VFHE"Q6!9Q<$QQ$,Q1j1IQTQ1IT1QQQQ$QQQ$QQQQQQQQQQ1ITQQQ1IQQQTQQQQQQQQQQQQQQ&'v;!;!;!33vv;!	1B>2j>>T>>T!>bo>>o&>>>>>T>>>>>>>T>>>>>>>o>>>o>>>>>>>>>>>>>>>>>r   c                  
 g 
| j                  t        dd        | j                  t        dd        | j                  t        d
fd       t        j                  d      }t        j                  d       
 }|sed	d
dt        j                         v st        j                  
      rt        j                  
      ndiz  }t        t        j                  |            d }d}|j                  } |       }||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d x}x}x}}y )Nr   c                    d ddi i dS )Nr   )r   r   r   r   r   r   r   s    r   r   z4test_show_entity_unknown_is_silent.<locals>.<lambda>   s    Tb",."#>r   r   c                ,    t        j                  d      S )Nzcomposed unknown entity)pytestfailr   s    r   r   z4test_show_entity_unknown_is_silent.<locals>.<lambda>   s    &++.G"Hr   r   c                &    j                  |       S r   r   r   s     r   r   z4test_show_entity_unknown_is_silent.<locals>.<lambda>   s    FMM%,@r   zNobody Whatsoeverg?zassert not %(py0)sr   r   z
don't haver/   )zD%(py1)s in %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = %(py3)s.lower
}()
}r   )r   r   r+   rs   zassert %(py9)srt   )rA   r   r   r   r   rF   rG   rH   rI   rJ   rM   rN   rE   rK   )rO   r   rQ   @py_format2rR   rS   rU   r   ry   r   r   s             @r   "test_show_entity_unknown_is_silentr     s!   F_>? %5HJV@B

!
!"5
6CJJsO::vv&399&9;&<;&&&&<;&&&<&&&&&&3&&&3&&&9&&&;&&&&&&&r   c                    t        t        j                  j                  t        j                  j	                  t
              d      d      j                         } | j                  dd      d   j                  dd      d   }d}||v }|st        j                  d	|fd
||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                  d      dz   d|iz  }t        t        j                   |            dx}}| j"                  }d} ||      }d}	||	k\  }
|
st        j                  d|
fd||	f      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |      t        j                  |	      dz  }t        j                  d      dz   d|iz  }t        t        j                   |            dx}x}x}x}
}	y)u   Ahmed's law: "pull up X" means the HUD card on EVERY input path. Pin
    that BOTH user-input entry points — the spoken listen loop and the typed
    HUD chat box (handle_text) — run entity_show.handle_utterance. A new input
    path added without the intercept should fail this test.zmain.pyzutf-8)encodingzasync def handle_textr#   
    async defr   r   r/   z%(py1)s in %(py3)stypedr   z2typed HUD messages must fire the dossier intercept
>assert %(py5)sr+   Nzhandle_utterance(r   )>=)zK%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.count
}(%(py4)s)
} >= %(py9)ssrcr   z9expected the intercept on both the spoken and typed pathsz
>assert %(py11)sr<   )openospathjoindirname__file__r   splitrH   rK   rJ   rF   rG   rI   _format_assertmsgrM   rN   count)r  r  rR   r   r   rx   rQ   rw   rc   r   rT   r   rz   s                r   )test_every_input_path_fires_the_interceptr)     s&   
 rww||BGGOOH5yA!!% II-q1!4::;LaPQRSE =& =+<+<= = =3<9  = =6<f= =$<$< "' = =3<9 "' = =+<+<<= = =)<)<= =99 D( D9() DQ D)Q. D2C2CD)Q D D=CVD D+C+C  D D:C)  D D:C)  D D:C) ) D D:C) * D D:C) ./ D D2C2CCD D D0C0CD D Dr   c                    ddl } ddlm} | j                  |      }|j	                  dd      }t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t
              rt        j                  t
              ndd	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      d
z  }t        j                  d      dz   d|iz  }t        t        j                  |            dx}x}}|d   j	                  dd      d   }	dD ]  }
|
|	v }|st        j                  d|fd|
|	f      dt        j                         v st        j                  |
      rt        j                  |
      nddt        j                         v st        j                  |	      rt        j                  |	      nddz  }t        j                  d|
 d      dz   d|iz  }t        t        j                  |            d} y)u   The card's note field must dispatch a pooled Claude worker that FIXES
    the graph (memory_update/memory_remember + card refresh), with the plain
    saved note only as the pool-down fallback — never the primary path.r   Nzdef _entity_note_agentr#   r   r'   r   r   agentr   z.control.py lost the entity-note curation agentz
>assert %(py8)sr,   r  )
agent_poolmemory_updatememory_rememberr   	fire_saver/   )z%(py0)s in %(py2)sneedler   )r   r%   zcuration agent lost its z wiringz
>assert %(py4)sr:   )inspectvoice.controlcontrol	getsourcer&  r   rH   rK   rF   rG   rI   rJ   r'  rM   rN   )r1  r3  r  r+  r   rc   rS   r   r]   r   r0  rQ   r\   r  s                 r   $test_entity_note_is_a_curation_agentr5     sd    #


G
$CII.2Eu:LL:?LLL:LLLLLL3LLL3LLLLLLuLLLuLLL:LLLLLLLLLLLLLL8>>+Q/2D/~IIIvIIIIIIvIIIvIIIIIIIIIIIII!9&IIIIIII/r   c            	        dd l } dd lm} | j                  |      }d}||v }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      nddz  }t	        j                  d      dz   d	|iz  }t        t	        j                  |            d x}}d
}||v }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      nddz  }dd	|iz  }t        t	        j                  |            d x}}dD ]D  }|j                  }d}	 ||	      }
|
|v }|st	        j
                  d|fd|
|f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |	      t	        j                  |
      dt        j                         v st	        j                  |      rt	        j                  |      nddz  }t	        j                  d| d      dz   d|iz  }t        t	        j                  |            d x}x}	x}
}G y )Nr   entity_action.jsonr/   r  r  r   z=control.py no longer watches the HUD card's back-channel filer  r+   _entity_actionr   )z"show_entity"z"entity_note"z"entity_summary"")zK%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.strip
}(%(py4)s)
} in %(py8)scmd)r   r%   r:   r;   r,   zcmd z not dispatched in control.pyz
>assert %(py10)sr.   )r1  r2  r3  r4  rH   rK   rJ   rF   rG   rI   r'  rM   rN   strip)r1  r3  r  rR   r   r   rx   r:  rQ   rw   rc   rT   r]   r^   s                 r   *test_control_watcher_handles_entity_actionr<     s   #


G
$C H3& H6G6GH3 H H>Gi   H HAGH H/G/G $' H H>Gi $' H H6G6GGH H H4G4GH H"s""""s"""""""""s"""s"""""""EyyOOy~O~$OOO~OOOOOOsOOOsOOOyOOOOOO~OOOOOOOOOOOOOSE1N&OOOOOOOO Fr   LIVE1zLIVE=1 only)reasonc                    t        j                  d      } g }| }| r| j                  }d} ||      }|}|sBdddt        j                         v st        j                  |       rt        j                  |       ndiz  }|j                  |       | rddt        j                         v st        j                  |       rt        j                  |       ndt        j                        t        j                        t        j                        dz  }|j                  |       t        j                  |d      i z  }t        j                  d	      d
z   d|iz  }	t        t        j                  |	            d x}x}x}x}}| d   d   }d}
||
kD  }|slt        j                  d|fd||
f      t        j                  |      t        j                  |
      dz  }dd|iz  }t        t        j                  |            d x}x}}
t        j                  j!                  t        j                  j#                  t$              dd      }|dz   }t'        |d      5 }t)        j*                  ddd|       d d d        t        j,                  ||       t/        j0                  d       t        j                  }|j2                  }
 |
|      }| }|s t        j                  d      dz   dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |
      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }t        t        j                  |            d x}x}
x}}t5        d       y # 1 sw Y   sxY w) Nr   r   r$   r%   r   z@%(py10)s
{%(py10)s = %(py6)s
{%(py6)s = %(py4)s.get
}(%(py8)s)
})r:   r;   r,   r.   r   zlive /profile returned nothingz
>assert %(py13)sr=   r   r   )>)z%(py1)s > %(py4)sr   r   r;   r3  r7  z.tmpwr   )r:  r      u@   engine did not consume entity_action.json — is Jarvis running?zh
>assert not %(py7)s
{%(py7)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.path
}.exists
}(%(py5)s)
}r!  ctrl)r   r%   r:   r+   rs   uA   
LIVE: command consumed — the AIN card should be ON SCREEN now.)r   r   getrF   rG   rH   rI   rJ   r   rL   r'  rM   rN   rK   r!  r"  r#  r$  r%  r   jsondumpreplacer   r   existsprint)r   rQ   rR   rc   rT   rg   r\   r^   rz   r{   rw   r   r  r   rD  tmpfrU   r   r]   s                       r   test_live_profile_and_cardrM    s   ''.GN7N7w{{N8N{8,N,NNNNNN7NNN7NNNN7NNNNNwNNNwNNN{NNN8NNN,NNNNNNNNN.NNNNNNNN8W%))%))))%)))%))))))))))77<<19,.D
-C	c31		-7; 
JJsDJJqMww Kw~~ K~d# K## K# K9J9JJK KDJFK K2J2J  K KAJ  K KAJ  K KAJ  K KDJFK K2J2J # K KAJ # K KAJ $ K K K7J7JK K K	
NO 
s   O$$O.)g       @)&__doc__
__future__r   builtinsrF   _pytest.assertion.rewrite	assertionrewriterH   rF  r!  r   r  voicer   EXPLICIT_CASESrv   markparametrizerl   r|   r   r   r   r   r   r   r   r  r  r  r)  r5  r<  skipifenvironrE  rM  r   r   r   <module>rZ     sz   #    	    -~>
: ?
: n5, 6,'	&		K  UW,GI.%"bRP<H*Y,5$57 8 lF2B@ A 0 *JKL4@?$'(DJ 	P  BJJNN6*c1-HP IPr   