From 3b49a948e09b35729be71842d627ae5a301f9370 Mon Sep 17 00:00:00 2001 From: ZiyaZhang <199893125+ZiyaZhang@users.noreply.github.com> Date: Sun, 19 Jul 2026 23:31:19 -0700 Subject: [PATCH] =?UTF-8?q?fix(desktop):=20session=20Graph=20node=20order?= =?UTF-8?q?=20=E2=80=94=20sort=20by=20seq=20+=20repair=20turn/user=20rever?= =?UTF-8?q?sal,=20node=20label=20previews?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also picks up two files the previous sync batch missed (test/artifact-compact-row.test.js, docs/qa-artifact-compact/). --- examples/desktop/config/deepseek-jsonrpc.yml | 4 +- examples/desktop/config/deepseek-vibe.yml | 4 +- .../qa-artifact-compact/after-expanded.png | Bin 0 -> 37301 bytes .../docs/qa-artifact-compact/after.png | Bin 0 -> 31102 bytes .../docs/qa-artifact-compact/before.png | Bin 0 -> 47026 bytes .../qa-artifact-compact/fixture-before.html | 105 +++++++++ .../qa-artifact-compact/fixture-expanded.html | 51 +++++ .../docs/qa-artifact-compact/fixture.html | 63 ++++++ .../desktop/scripts/assemble-oss-release.sh | 32 +++ .../src/renderer/chat-session-graph.js | 111 +++++++++- .../desktop/test/artifact-compact-row.test.js | 208 ++++++++++++++++++ .../desktop/test/chat-triple-view.test.js | 153 +++++++++++++ .../desktop/test/model-profile-guard.test.js | 20 +- 13 files changed, 737 insertions(+), 14 deletions(-) create mode 100644 examples/desktop/docs/qa-artifact-compact/after-expanded.png create mode 100644 examples/desktop/docs/qa-artifact-compact/after.png create mode 100644 examples/desktop/docs/qa-artifact-compact/before.png create mode 100644 examples/desktop/docs/qa-artifact-compact/fixture-before.html create mode 100644 examples/desktop/docs/qa-artifact-compact/fixture-expanded.html create mode 100644 examples/desktop/docs/qa-artifact-compact/fixture.html create mode 100644 examples/desktop/test/artifact-compact-row.test.js diff --git a/examples/desktop/config/deepseek-jsonrpc.yml b/examples/desktop/config/deepseek-jsonrpc.yml index 5220e2f5a7..6e4a9d4874 100644 --- a/examples/desktop/config/deepseek-jsonrpc.yml +++ b/examples/desktop/config/deepseek-jsonrpc.yml @@ -31,8 +31,8 @@ apiKey: !!js process.env.DEEPSEEK_API_KEY baseURL: !!js process.env.DEEPSEEK_BASE_URL models: - - id: deepseek-v4-flash - - id: deepseek-v4-pro + - deepseek-v4-flash + - deepseek-v4-pro # Showcase default: pin thinking on so the reasoning fold β€” our headline # visualization β€” is visible out of the box for a first-run user. The # provider default is already "enabled", but a future flip would silently diff --git a/examples/desktop/config/deepseek-vibe.yml b/examples/desktop/config/deepseek-vibe.yml index 5eec7f67a5..3fb6c0cd6f 100644 --- a/examples/desktop/config/deepseek-vibe.yml +++ b/examples/desktop/config/deepseek-vibe.yml @@ -29,8 +29,8 @@ apiKey: !!js process.env.DEEPSEEK_API_KEY baseURL: !!js process.env.DEEPSEEK_BASE_URL models: - - id: deepseek-v4-pro - - id: deepseek-v4-flash + - deepseek-v4-pro + - deepseek-v4-flash - id: bash name: '@deepseek-ai/dsh-bash-local' diff --git a/examples/desktop/docs/qa-artifact-compact/after-expanded.png b/examples/desktop/docs/qa-artifact-compact/after-expanded.png new file mode 100644 index 0000000000000000000000000000000000000000..e6ac027d34f47b8704f6e6bb5317aa33b258662f GIT binary patch literal 37301 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 literal 0 HcmV?d00001 diff --git a/examples/desktop/docs/qa-artifact-compact/after.png b/examples/desktop/docs/qa-artifact-compact/after.png new file mode 100644 index 0000000000000000000000000000000000000000..543a0a72d776b9b96b23cbfafdb24de83c3122ec GIT binary patch literal 31102 zcmdqJWn5J4x(7Ta7NUqEAfO^3QX(K7ih_VJNcSk+-KnA=NJ&bgl;qGopdww;%}93* zL)Uxp#6ElP^X&cZbI$Mm^727OhGEv4b>G+ZuWNbBNQ)7kAwPpaAPB{uJd#Bqj%Onf z$KISe3O^AfLS-Tl7ZBo)9>_aH&kvneSJ2(BU8y>Mw1Mtaa}PcPz#Y4i3ttNXH4ee|vt{K!Jvt zdC!ZwJG7!{ap+kJGOjJnPrKG_C`UV7v!$_dpxklJ=W=@fY$CogI%o&QW?y*%j_lV1 z-Kst;h|GBG)+ntPAQL`7QKAK&P@Qr#2Iq7h^!lDfM-FhR;nmz0#`wzJ^V z*(qOtnT4gLrA4|UrS;sY<)ZA{J_#cuYSPi%edUgFGcNDFy@$%U?COKeCvItKX)5>P zbQ={H2kgK0_e(e(=!|4!WDJ!hP&<8jl{({2P}UHnR%GUkZyPQwEL3g#+LF(%-*WQ! zX6T?m-{vr8XMIU%bFD`*fO4n6aaAwZJhzotPFHDRVFB;n6OMAj*e#q?5x!$;YAil6 zVBVUWo2y#B$v87^(*8qk7=w%(mZheo%yU{(6|bJ0oXq_^zv@Xi;^^qez|b?>WgRRK zqN}C7Q7<$C3uteDC)JaXqPMTFo^Ntu!pWhyF+I)&XZYe%GAob$qRW;}dk6a!=I!s# z@75u=ZZ|bG;g!rcmnE^{j_9E>8dmiuy%@fRo}#e|*PRJ%^N!@Toh5gh#laG++s@vv zZz3^#1e%ygT$d7-3AG=?#5CmH9Ld=wB<|gTi}3O?50k1l=b@n)qdwTY&c~|W099yd3)LA6FhoEtB4UCvGu>%Pb3F#U7Hyh6ok^v4ohY}`+u ze)6mXkwkMv!8hiW8dpe2l&!*V(j=@gKcUL%tyROJzl_I{l91F@+HBzz+S;7QS>*&p zwh#v^0ezWjMQI9+qWiz}o1M@K>0vnMaHWH#gW+&@|MJw6u*&2qE=S8JXCKGVF*9pu zd62|9jOM_LP~{&T=6hGL?m}C2G#Ud1t5kw6UxI@>qRcDkrY3SFtZ-f5zaMq2Zqkws z?4s4JKWbjNr?=2waIl7EoygeEEYft|o=LgI9?kD45ys=ZKDdEJccWEzmny$?+Wh)T zU%9`n##C*NslrV#t_5^{VdC5RacS?^v13$j#U3vGc6NoiCr(@C zHiaf3VoE7x}IWvi! zCq%AQUb}SZ(&^{Kd}|9&Y&P&Fw<=Y}_DRtNDW(JWaXefGGN|_AeS2>A{fFY7?gu+q zPa=1x)yYh#L}gn_X!B3ssm<%9LI+n*oV}u^s=B{mj`pS8iQW~j!^ZA1s;jGyw`ww$ z+N~7t>IF~<(CKbD(DF1=$Nsif=r5mpbC#6l{OPT{{Xn^qAlCZRE0>V?;Mm|$My0)# z0Gjbz%F=U56wN;#1?No#2{>~F|LRPY6LWTU=J5#6GxFvtFzO0=etpQ5*g@yWOy2f# zm3!(YBrmiWVpKYmz9V#wi)$vSGfpBdYCKvzm~K|7Lfk9rjb@*9pTd^c zT#R4!d&=qV%=ev*e1h8s$i5`#P?7RwqBAd@#y^+^ou<_iS2bu)%!$2{NH9a> zKFyq-Z;s6}8!Cm>DwLNQqD$Z0tV@Aoi)u(=-uhAe(gYRT!1~ZV;fBabfh`?(M;iML zCt|tQxXSp;1VgYQi%Yw0ZEUXc;QC?(${O#4gBJ~xml z-)&FS+fGq23@7(GM~A;&sb(+Sq%V6!ge!xVdcWARRkHS%J$A3maW&a>XaAF>c5BdC z^^zc_z0XrEG2t?IG~UL>5}KDd7Y(Y3YY|5!T|&Mt#ZPjY_HTNgFzPAnIg4YcM`=h) zXStk`HQfDitJWwfZMoKYHMY^bqpj`P(ZYRsL9Wyfua8$k&8#=yYKq895EUrXu0=u* z!!f!|MtmZNA}iA@fA>P<$B$v{J@gnyD{0m}`u3&H_y>DYJzYNNlOb7DncP}rV-X+l zM;fjSJL>m08?Nf$t)ZZFEy5$~O744><=?r1Qn^^YNC`2mI2J*??E25CLz z=2k`vwccKE$K=~=mvIuiR4gwaUug6XS=m6%K#hB01*6YmZ0D}nI*LE%cH4dQ@Zsxm z>Uu9?p~O~;AMZv~lNNPme8k*XsMtoyW|%^e%2#pFXW(oy`^Ne_>4ee4N&1{V4fVKaYhm3pGd^O^hrp(Yt!!Hm+md>TY#O zVv}9!h==j6VgB3;EFp7ap|Q)xJz3(*9UF3XB1ZY6l>}R;vE>mOTsSLrD#w{?JXRdE z6aCExH1Me*HZ&%%9|Z*jT&sN)+PQG!!2yzYgZ%|lLso5PCOTLrf_AT`v^Z}ph6fv_ z?oNc$aG56>Op?)yiIWgZCVJ>@;g&vsUZ18QBEm8aiP4gp_2+xzZ&L~GzF&_!tv5eE zZ}xF@(%;H@^_JI@%D6YQ-n>O>Pqor8n7y;CzTVz_6clABqyBuu0&D#@c;9-prD00i z7?bUw;bBv}Q>Me_7ZP$VmRVd-i}04cI5ze8X$ERuytS&TXPL7==DCLAVB1;lX^o5` zHDN&oj~l4%ZXN1dThl$|-gXP+Ndq!g@h0wFXoX<8uZx=nP2BoS&F}D} z?5ystgDsBqP6Ucqa-yoKpPM+Fo%Kr6wSJNkHrIsq9Z&mVUGn6wWT_P` zCW^C$ZtRvB_gtUJaGnUQR8dp2T^g2(J3DSSPFd?qhI#(2@>bo}wjJQ5ks*7b!0q#t z{RJj{4^hb?Uh4pMx0b7Th*5K$sRB?g#aU&SCMH5d@Lk#gdk+{J*zJNc8r&Lx+*?d^ z`M$Hh0Pnz+j64`%CJp?0|M}P8poEOfOxnmo154Msx%daj2K~*A%GSVuXS~>be2S1! zwa9B&Nn6cajV`X#VW{Yol&PkOrS2>ZwS5iZ9rLT`2R?@b})_j7PXbd`&4ds#@` zTLNGB?B;{qD?-!?@qmWY1zoAS;8MJtt^L6*H7#)6m4U80e>AJSsp;vS=*6VE`g%4_ zPG-lUc+0@y=6m<Mfs%4H@UR{7+K#)|wMIL_t|A>kH-+1i*?$!5@ zPoH)ko)co0je&{jkB2YnZXf>WADH&3<;B&z?SIJln+nR{>PLl!>fXJI zTV4HKP*$Qo*x1;Z7>#sA=J#5-i7#-;`KQO3 zZljg;^pXn-3Swd?^&cRNvV4v|oVof`N-DH4r@z2BD)Qbm2?FuuOMJX=%R6|R3Ey{F z4-uU5k4M#^nI}FVARuVJ4t+B$`c(Z)U^Gd6>IZm7xsprJi~aEzx&c!jgQsI{h$gRe zAmUHnL0t#R5OV)61qDS-13YC=z4r!4037erY=U^YJN85`+}tYOzI_X*=cQIySeVu8 z#3RCl@)ZNpPddWCJ--fmh>UAp8xK7_mk?HlK|sEGL;h}FZmtf8OkaKKvj-1ex$L_b zAdI4Y2E77Hbl~6xbMvejbDYyQW6x|6GXqrR2=R+1XVWARCbU)JO0J|aWFDwIc&??@ zzQ)DQHP@El%o!2Bg`eplBa48-+O`;KJJ-FjIMmqRuX+p-kCU%{UOj3W#(F_-$|zOk zM3$`q=gF6miAKjp(iGCQwY27XHh#v%@?5gtT>kLu*Xi!=jmkDlDAhpofl0>Nq04u1 zAwy;M7f&5Q5XDwUS7Nbyr@t3aP_{) zyUfgmXdY{8E33ugZGbZLNVmS8qTm2zEmANdL1}Vos`6mpM5ywm7xlX95@y2>g~hrS z&DPTs6q*ecnq%DnuXQkPEMbM5@Nwms0o%nv+s3d=r=%b9Dw8?2+pQ6+$T8>oJ5^pn z18E9TLc5=qVg;P>4Mak_(|N6v0z32Xq+@pXw_1^seO*L?o6B2MC==|k{`x$T)B610 z?kcN}o}R>BTY}gGCULm<0Elsch}ZebwQiewo|;G=>xbdYYB%2I=i}kU4$wj=xXjKF zVM)lyLgUPTHi+EB&Y_a$CKyxSis&bikQmv{eT=*uC&xdoWye->ePi82IXE}#s)**s*&_sY#tb`#+YrKJ3IiezA|2Wor?rP7Q*#s9*}B>_ zzAUb5<`t*&^7DP(_0pO~1ZChGwpN9(CDuJXD0f{Wqx**K;~*>9&$O%XQUMY#(W<`3 z%8CZgsdX)EYuGuDfQV|dn)IO5VPzEbGrWYo{RUixy*boDsl|=qO-=XxRTMw}p#6OB z{@w;wGXK55f5m}AA3-j|QiKuY+4v~pCi$)Epr%%&R*s(r+A#%R;EsZj_ zpv>wYKlpKVG`UK-UaS?HyBR76wamsx5F4I?JrgpG!R^q9`5*GEd5R5Fvz4h6sSIR==L_lPHuQ?jbj(!w^Cl~>tCd@9bG;?I8#VoE z!~46VnY|~{$LuuSH#!DzXK+GG+i8j?m9o@NpH+(u3L5?O>#o+1yZZW5t~=+Wc`gBm zSpgaWg(^XmFkH`|zDP1Wi`#WO|5{lRnW}$_5<26sYG5*7;eCm|xTFLc^s4GgrztQHg;p#U8A-Qh_4KJp ziZrEpiE^Hv6frSzJ}xl3r_4ja!KCd;TtOSpv14dzBt&>re8rrM2Y<)=?``Ruh z3`ofwgSK#3lppUe5N4>_+H8LAKo z!{xPQi*OMaXst%3u%Gic+*zKq>y)B%*b zMJzSrkcazLBdTDO?Jlfg0q6B-o>j^{Hp@}T?e9<^^PD%b&=uQSL#4J9T%L2G?IlG{ zYa4J>ni#3m)ZD^fCCM`WP!sD2uPSXmrC}&$#`&?E{jl6Gq$29=gt-8YL z0}E>-(1uu4to8|c$Q2~dajaFu=H_uwK~#BcP4qr|)cRO?!&vy&8p9}Y5E_McSPZ&B zkIt@GHAUWaa@-8V;qA_y6sez+Ni?}jf=M{8iV-c{#}Am-Nw$-yrSw|2`sL@|i5#xs zPI$%SzG@J&w?kysm#fhpZ^Pa&+cmzgjIb~te19XM#$0pDyvma>k*jr6cE+Dth-FTK z9#m@$W@dCz?;7;D=EwpkeGOIBNT!6#D5by<8}T!gyf$A@m9KIfk=xVb`W`0<6NkT# z7GjrjC5eYYwCKXw@!2niG;eS6t2g*-6kE7WHmR&mx@Lt+te3;On(IM_H#Z0x1BDU+ zr7Fpr{pQVvR&)pO4Z-{>FZa~1SFac{xqinwDLh(#`EqXTF%}ikwpuVwpMr|H5V_rR zqtNbt5hVdf)kgIvKj-cOyFf3J@l?f=c6N3_6QiRoMvoT?^^;8W_0um)4ck4f95=^S zc=%B`OWe5r6?wi)P;dc*82~NArB8t2|3~(O0a^L9v6=oLo zcho#qA!TLGdU~R(lg-r5^VowOQWLF70KwN&O~stIY85Ob#XEKXmJ_w@{TS5q^y{Y znU2R*nzC|evbnLbF`;b(=?AaA_4OHUF24vW$j3Q5K4*5k8t2B}A}El4mnP+B^T_#H zdnJn6pDl6)I0|+>d=Tx6b`N^}i#3ij`~m_~&1xTmgXPl0nu{%2pESomI0nYR!&1?_ zyu32oIj#AJ<&;E30Ss~vC8XKqyzR$;e6=R3cSP4IEhkBa`=Aj_!JNk3Vxpo${OtM< zzgK5z@Cog2YTFNfhciu_Kn4vHu7Um!RcdHxj*RaZ;Yp8cBeUI^&Q&qkOP7L~n3~qk zZb{G|@b%3M-oqd^O79jCOPc-Y)RfI*6LG4jQyk5GGac9XQC4%I^_2cm0 zyu!l#?7QfUfz(Dj9phXnLPR{GAArx7zFXgcn;Rk!A=Co@k$d>Xr|BMGg+uU&k$l*&y{xMxB z{Gdzh#fulx(q?WIpJY9m;17R{iUO`H@!%M6i$n6$Y0}phbaJ&o;$|wK9MtC|KflnE z2#23+IwXjfk(tCD&qK{FA!G;@Oo?7Rc<^n1j{vSoR%JwqN&wXmGc$96GcB}SPR{1R zL6_xU4^%I6TaIotHTgnch5ZZ%(jjkpUA@4_7P{r$Hq~|qyZ-2TYC+;_{NMUZGZJL( zoAiE+j^?)-Kb83qJg1g8AtCeO-8*OAL~)r5IE9Z`u$dE?J8;MQ5qPh1tOu0 zfnv*!`95l7)VlvkM8lVUs_zsvpF7i)SUK>}Hk5mF*|bH&bIsy5ujQ9hP_b@K~~G~1x}7QTroh%jh~*}v0oY@VLE%Zb4ma>bNHQPRp z5T3BIvAN669-b&KBXd174rn=MLQ79iFDO1Am(9`RWS@N|fSKGk?>?{o^LX15#RE3M z!jP0C<^1VJO^nmisC7?pQ#$KDxhxI$j#S0P3hqg%4XCzP(uV>?w6L-YsrEciS?;hx zoM~VXFj9ndTN=j9a9D)n23-@U5?9&dF_6d{p1E3AwUCBIxS6^wj1 z!DhcWW!z)-M7m;jax%G6{)o7^c&2JWXCwS0@$_kVYHpIIBi~pJ=C)WZKu7qf0zZDl zBrzEp8h*NrvSU556%icFj3m0o-TV1%c!h9d81vzVqMf+k=8V|rr3wk-Yl*x04P zp*h0|j=Ob6QZ~}+K{sd5V6%|EAMWgO#CNc>nWuTti)QE>%0r5zw&1qiC z(Zo)Zp;Bg6R(pWw^vKwt)gyh{)d1I4t=1jC{G{4x}`6LcK55O7i8OMBFJM z-MGPV{#t0lv!!7HP!!d(Y-!6wGc0XveBYP3O=zmCNAvF?bq5JUc^MfQU2{3LrS`Vh zgmz38_xEzz1vbMgce|7;UsTnAdl}2`n2Xm4;&50T=I7mA z-XW_a-cP-V4zx@rn9BT{nTX}it#auQo?_Z)o^$?Z^hw#5ZV`Cj19bBgU8A`LTwJ11^ zNG@J{k-_7(TRb0x!MFl$T@w@@D#EhF@K|?$pb?YACdJ2p)hN3uD=Ry`AhGoPIO3OJ zkXs(u2&N_`*C;6^x*J#K5&|Y&mcNMsXuT%r@-ZeRMp2rMuK&`0clym-g5&DctdS83 zwh@v1+J;W!IqD21z0L!DLwcJP-1(NRrQPRofv@?y8ieg+bEAiE9$xL%57zi;w!NTA zAkp{D=2I`n5O3B$8?H^k6xl~HDg`ZZr*EaorN9;?APDH;y4_|u+gVSJxSe3+xAj@Ayw503gTXp`SlAt!7(vI znQElkq@xQC4h~Nvh-IxJBP7ZNXb|%5bQ6)6s-NFguW%{ZJoELQN}ispj*dLWiAys& zFwi2$xThsRX#b<)GjDHkTo)@eDMmRtxx74Ub&a`U%e6yS%;R%eS()N9aq;kL*Jkgg z9Y>|%^hg1Eq(8YnUW12+ zv~B9=<2|+hLP`G%^*rrnp6^ir@Cn)3cYjlFub)`Riulh_qk`#mP-=SrL8>hSiQBH{ z+{r3n0|4udp=T+?hJ=t@y41mIYh-Lc0+Fws4eIHx^b`Q}UFknKHY*31P}iFpd$M$| zly)YgE+UMA07_Ya!`B$@ZVyqOZSAp|=7ZfS{k}dPT`V+1ohr}RI5%>mj1RXr7t5Ax zLA=O*QRV8oXSd)J*Z};aPAfK63NMD%#Egx$+&7+F`+jx|Ul0_;Qr{6wkMt+!R7Ht~ znRkB4A?YtPr9b;fOpJ(?#F^zuEH{miUMtCWx$-iygZWh%9RGWt*x~l z)XZ59(|(t(v`}E(KjT_HvaKeCYtCXT<3d0Tf}@kszD&Z-b+GhIGg9vC+4r8aBmt+I zJA6DBJ_24=Ewf1XoE*T~rfD1J8ln~n((gItqu;a%Z}_?>PPL-$YQFkA)fqMbz}NA% zC+8^c|LE?vYl$x5;nAI?A1qy~g?e=jytjr>Mkl}H-%nqvkt7eX0#VDw#YIBJwKqFO z=wR1+L7D|svD?ThR%$boA+)x!!Kyp*f#!hQZhjTgCL$tJ+uL^AzdVT6w!6~ZPeBs% z%{H+$m2*8+f4x7n!bzgQ}JQS%u z)Z#P`?rE^1#F?-0GA6~#oHf$DVJKr%nqRq<~xQdd8y?gI^*~QHU_n%Jw7NZUH|^Cby2)YZZF7SLpAxW}SK=u(e0<|BGY?ORcyP^jy1&0awDYb;2x7U9*L?uO73w-x(agMGew~xH zs{32F&ff5Rw_lT#r275My~XIo;js1lMVO%FzY=`gIpAFRX=xvmzZLVnIypaIZjOBg zRE?OJMYGhVzN5n$0;5*kVJ>AhGiP|-W`p9UH@&?kkfkXFLh+qOY9Y7J&k{Ua`Ps)k zk73@wUj>KS@G)uHBYizRh>ijo@=dEC8ad#&0N~20AtbOqm>wc2e4LzX{*01JQTSr< zwE6ymxd5Rnwq-MWZ?+RzEre}5D18&0aKq5^$R)SVyyQ^YALCT4-X^_*J1!3rgI zZVny3xxHPn-aiLXubs)L>~>N!*c##CO3)!8I~yGx?W6ypkJT%hKF;McY%UO|GD5s7 zk08F>{SE40#XtmXAXTnbjioz7<)Umn-Zn^uFxIfLfMUdpENUby0#B>H$^Ydl!Su_Sl_ z-;TO76d+_4{Bfg-NC>Ll{9p+qcoPt-l67BDefAN*vXWC!@ZP&Got0_fEubz{_d|SN z7}5dyf5UcNrB9w-`rwjK>S$3XU0PzoJzpO-lNpoq0& z3YoT`nu2R(ft@n06E78|C>s_h8x}cgWvs7H?`@$`dOU{PQkKz3YB(%B96~-VP=#sf z=p;7c@$@)*Vwzg(5SbnA@;@Adg-Y_oB*^Xb;!On)@;im9oBoSt_Pf>M3>I-aS11?gnB1 z_ejAl9UwLvivUJEkA?F9BSJVGJ9~5?rN^$xZ0ICxou(GoLJ7Ly8 zj_aw$$1A)!cG^XJgXh!2z04Vq=C#l7hLoRw+;CE)?Q&2}8M5B-wc8G%Be*xI(m7MH z6o$GA92h@K(PJw6lh62}6AtF;mL)!z(F^HfoB&-`M(*!*SqK&SvWAYP#M#|9>b%W? zkF%b1xfviv?J7^rc}Ku)b*i<}sbHEM50RXM3b_|A+)mSSaheUjhbp1e;4ChZz;vB} zf&sSc?ho%vuxHv({TMzg&||@b>y)6K!9V(dP5NfFI>~&fNJoV$SsOm{d-pELqw`NS z*0AlnBqRN$OADvn=M*mX9Xpy8K)PomCHgRTq=X1<8ym?KaA~Dz!8UqiXY^-{z?>>y zoyZxSJmWE7w)Iv{!RbKrVq!8wAQ732!WEfgzt#8&Bwe`hi$3M}zg1%Iks=Up4B2`gXC&;s-t0P;#I-; z3m5Wj*@b&q(?(-g`yJC1-N1{~%)Ry0X?dLKJ2z_#q-4q+&w{IK)RjxdwRj?xjKkpT z=f|igJzw(4?dQ6yd*3?nK`vh|`AtZ)UNZ4g%@IUboOnsH&b;}PnJfoqG(^T;${qfOHpe7&Y z7!Kk~t_c%X2R=J|LgrunmCF(G@XPYl5PpC*ytQ?b!N4Atz>mQ}NQlXvA%|HDF?i#6 zy~dU>j*n;sV)0N%$3GGch_4?0hw5#oN$AIu={8Y7vA-BOpE`Ty4CMX+V%zLBH#Ixq zr_BrvQ-|K>>9;->6O;Vpx?>0G%+JNkjvXhf;Y4AU;rhQ(R7VqJAOa#QC)bmq;ylq1 z>P&VDTndVN;|wsg62HxOThDACn^6AON@{3XZi;J+*|S|wjBAFV(>G0!Qjh<8DfQda z-o##XB~=ds@ih}Ln?tovXr3NM+t-FfLH8Yn(i05fMYBr{(-zzI|goas0S?i4`q3 zx6b}DgSOJV7aAMQccc&N1C5GpNK5rMf^Vbqld zsfUGhbl8)^LFl9~6r$JkkrU*2sBV#8u~=3pwo*~uvi{!Q3!SNB%uC{J87jeV-n=;+ zxzU{*7#wXqD=T-;gwjN+d#TxMsBWIwS*PT-renD=G75AN+$S+Va+c$>~F4I@+0STnT)srb)2?nesq-Iuw7F2ov&{Tc*;#pu$agc1RAFguDR6s zQwr!x)Q#6)+QHAnL`JHHt$CeuOoQtUc2Uq|lWgQ&iu*2KAi8M9AGq0*C@v>%QxmS~ zvT}lptLK5o$(ou)#tA<#ZAT|36+v0zvAb_RtnSY%OLj45r%yi)1m*qJRw35fP>GVt zOVB-ECM0-7M3h`3gFT+;m=9u1k@{0rRoVv-2mc$!y!r>mWTN!Nw)^4|Mf zLrA96Uf7>dQ&SUw1gF=Z*0xCRLKd-a0Mf!9of6vd^?EvI8;_;9XNd2^xa7wPe!fvl z?KPuDqMynW71CgO%GcKyw0Ln9HwhIZpTkghb!{Q8}D4A_YY?$AVkPpu`Y@jT;skYmhgwwKR*}C zXD`hfE#RC55#=x7B z8^EEAb=?s*78aJjIo5SHo6D>LxR)r9Ah8Z_H|iz_iUEe#){G za00>Eh`D;tXm>L701oeKb#?V-J0&0)9zMPu2)$T6SzTK&$RIkk45;8<%Tw)n;*i}! z$JH=CvvsJAEzvx4bsrLvSYZyT_=QOEwdZzA{^c%Pxgf+e-^l#D(C;|V+sn<%8z&FCuC^eWk_J2T3Jsf5O0wwaB7MskuE-oVK~fo{2^cHkQHEgWUyn zt-n}b!Oas$ z=|kDr*g{!#w~WHX>bGCNKI)jtq!y@H3Q-|CdQ=R*V-N%;b1K9F#N5C)g1Fj1f$>H& zmt?G~-R65%|G8ld7}8GIihvj)cj@$VxqTODL{FSN3Gw_Q!wyl15YoZi*!C?p$aREz zk&s^w1|YM}uJ^QOvA^+{aedV$K5opI9$ZKSUr$99?TvrZJ`)L$EjN8v+Z~;pTB3?% z$ln?NBc^)B8O0 zoz~3Cxo_MU(SorOYWLlocBPFCBK3TOLyl9gl!w`iF}IBR_;Gwzwa5y6BD{5KwE8^` z`HR>ZA-on67WPTuh_ka=<~Gn&&hsqygBiOZZ=4y9{v8R3BrOzprLx zg~mZ>*CCJu>%xjkm#}KLpo)ZP=-t3{M-{11gMA5Rg`HO+^rOI^!O&Dtjr;w;_%2IR*}P`cCa(Q zeKJh#QVeEKW>LFBu~Os9?qWk4U0m0p;jQ$%|Gx}xs(Y%+ID=aNBXV7LS^%_3$MWak zpKgK#2)<-=vdBqmY6L9J97w)u91FQXRAHHPex@wlJV{p5kD`{XjWKOV{J%8U}+obnqg;_HVQ_`j@VM zy%5MWf4ct4>waQXjcR4|Q`D_};#(3%Bb!JxvwPJDnBys>$_E!ht#1y?Y|nz)i0!L% z5c}@NAT+waobOdz+&?&lr=_O=%`xj`au0lqgby89Hsrc5@75QmZEtVyZ9655B**t$fLg-)>v^B%( zHcRkPA#MuECP0GHDA)GHdNVUKq5a}D@J<2#;_GOJAd6}?{0inXMZDaG3+7V1FL{8G zqTlM~4!NRR7Xb07Rk~GiVVS@!b(~51wZFFmSr&PE0LE7DOgfSaNZQ-m6WQ|rXM&^@ zN5{Q5nexkJ&us@==9b)?)HEOLPidf-8-1+a5uH_IAwUrjcwMCNgf6x;VE?NrqWI05 z_wAl@Gc!C!ofd|kEv;Nk8l|I<|F*IS{|g;*tK+-Yu)>++&9xrmsk!t|W_o$h-DbwFPgs@gS&g=6a`&{EPW7-%%#D}C+ zEIevcGLrA4+6v-SJaKYcl3=JlIHjQMx_8!iM`EHRji~Pz2+Kj%tu;;vja0RU0cHSi zFl800E_?LTqb9H=XOXbSb&ZXk%j|RV^MCrFa|2)RHr3aMrQ|Lq1g!73&N8osoI(f} zj80EqH$}J0-|>qQ3!qx)S;~PFV2L9e*uOKf9Sw%h1G~hq`X>u>f6s}sn!N}O4W+Hc z{|*(wa*+K4EGGU0teC6In3QchR|r>czV}(@Y4Edd4|&L1SX&>4LLY+4=zg%52*U{3 zd%eA`xe^x3#|=Fp^+&rgs@BSF(FrQW2eHzg^eLy=&WK#Jnv9Hv^&X)If7SQzw{BMK zt=lj_*kuCWQ<#B|PUvmjW?Ev&2N zFZ0?jMr(#~-JwMuMOaY(rZGMptMMx@FE_IfeeUA&`f&Cwn)iO*lc#J=*g($f*@H#N z(-R}c*Y2eWF*mB7^!PMxdt3JYRUc`hTR&$x+t*UCZX%jiR#rjQ2nL~z!6(q3y#4>l za=&roQexDz`?%{v)MpWk(|_l=U;oeq;1M!6kaZpb;k=!c{rpDw7l4abFic*@XBy6) zRf7>lh0JMLA8j1??sCLKxw?(k@HtK-ZONea{|-@BD#^(Fg2D`H+seuc8=LcQVF_0B zBqX>mUVP@}7GqZO6&6t9fXw;#syq=N4Un5^XrDT&3l^^B273P8x<)wPq?r%3R!P|} zmQ0BT{~L}Z{B{`a^dWbdNC{b(_aWCg)Dz>5%@5dT!<{J@jvwEw_x1`vI>U^wQr0c3 zrQ#|qPq_EM`SVn0bD7U$ovFc`2pQ3?^}}J(zd7tS+rK&N`DPOC8XJoO(6I|m2b`)x z--xyf^6gfW*4As0k{$r$Wi}-;w)gd=LJDPa@(Lxd1;ImvFej~73uF(FasG@{f=Z*Qm0v$k?SQL$o+|?X6(FLbrvO3qICS1jMp<- zz%VS#%p&PW*VnsNBHuyce(W399ixEgXXmK+<8_iOa~d#hqm&@?mlhWfvbi4>wv{Uo zcmpn$tv#B)Z^m=GCFYo`3$Hqvi9|S0beN|7m^*Kbbnr{#RXmIS(pH!NQP9wf}{vLGf?hXS4 z7i!CA+vjg0RhW7NK_a|E4Hzlv5x4Q26_yB)q2C`e;+6kj$cPF;(C!yTyj&sJQcz|G z2m@l@kUNw+#KSL*GXBjxlp{$4fH(*o!@VC~WJCz=EkG1GD@51v|j8nN?8Nl7BYy$l#+4LaA{^c2z_ z3sY0{51y6rp>|9BHM#9mpa0llpp5EC6SJ4_1kN6tk^Nx5{r?C?gzpuaKjdC zpkJJTv)S~|7qmroz>uKf?!g2#K+i+%4NPM32UL)`-98GMh!eg|2uxr;e*W?h3^@-j zK)Y6OX{>{d!zK%Do&xhwGH4Xw)t4_Jn!^J3L4jbZ#liHYH-}Jfp9Au?JUo3ZEsE~5 z)6*cnWB2!{=LU*e-<~}Um#M9R2B$py_p?(W9|vu!voc=2>P{|-i|1gNGg@9G8gS!! zNF^JX(!UoL?P9w&TOhQXZ8laTm8b8{Z`#lITkOdBl=;>7vJjT|`E$#OhsmVxBU^$b zYA?oK@A!!mpd0REHir3ZrUO@wZh-o&kX}K}X*8ZJMX{Ek@mFC@Rt(3;bXx)hRnuTl zr=lA~r9{en+c`zB-eS1S-CSJ0`;wVUVqhM!;%klH2oRQD=%*Pf2h#&ZOAG6FSQeYG z`|F~?bW+`DY_=OFu>oewZf!_hfcZ9i-JKgZpq7JE`M(KoYVS638DN)Mw${6N!P`}w zf8Axtw}xx@yJ@I)mfy|szfdVZUt1LTU8M9HgJ?O-eoMy*nu5jI{Bv#2SsfEee5Fu6 z4BWOy&>-R*_1>-gME^Asjn&1wpBMb+VJK!-shNrQenX_!_}iMB*S4l=jjAubpg_4! ze>!>N#<#V(9vBG<|IxC%ECm!GfFJh+5EzV?-9jd{*QQi`5)nGsoZZdv{>}%w3?}5gAS;Iw6t_}Ufq1SNJ}tM z5LDkh;Ax5tG4T*C(mKs(1v$hx9TxA>2b$EZHgIIYO zbN^Of|Lh6{ug&skwPgP16$wwcyATEjZa^?M)6;WW{?%GExDTU=G&gV7`cX6zUOjR< zDec&c-*9RyQt)(W?O;h9TBGa+&sN&_?Y$&f2&_EqUr!W3HUN9VjC8>!3ql451;NBv zYIxFhjd!vRnkR8~CV%+|<8+I{saDI7cOK5+@g=1zFL=Be@~8%=-z&CMU!s!jnNFFi|rV9>Ma4t!;TpW=LzQ<`;X z)5So5tsg$LB}*|fG$I-1lw6*Wet~4gqhc0~*V57444gm#8yg$p{$5jnmBw9!O9_%> z$-@Qs2a}#DOA{ylHp)bi|Jx|j-mmkB+2VhWgoYK2Pt#)Ff#cWVlR}RfQKPiJsZk`BYIsd6=+#`m_nPf6o@`8_bS6gAL+amWXmxZfB-A{<|&y9Ws;ovf_un z_@KMpuZ)w3uX~ddUF!bcf5N$!kCFkU|K<*-{9QzJ*L?SVrEK=n#%@K-;)PTHJuO=M zXIiwK_#aGhn>@xb-;=q!%?S=*kQM7T+KGF#H{VWIYTeO3B7C`oR7BJdTaLp*n<_3Y zzKXi@)}~`?bJJy|I=ub(p$5=SdLDF7|KUs2)Nx>-7j_*hD!chAXK&OTif3SLPIhl_~R8-6m1GD&n@sPFjhj| zpH+o8IbemYMUtLi57>mnS@TTQ`;fK1&Iy$X0^skbr#~LvLu9D*YS{(z+NvsTNC*Y# z9}RVI7Das7f7-yg=l$(@vCEd#%H@~3xq8jp5dHdRK{VIoQN>2m9P=$5++_1e5^F?c zWMoXt82$HL=0CN7SyiPnqz=LO7)X zlqu1O#x|t+xXg!d3cUG$+B@@bs`LJhAC($en$Sim*+NZ=5Myg0OJOkfrBSj*S<6}( zsU(HtMAjVC=uomRr&KD0h=go$sO(gBp8L?u)HCxm^PAsu{r>n}{_0$vuCC+j`}us| zpZmUF_tB6U7%AfjM2{fFYGL{6Hj2AwfOBfyOPylRxFsS$d#W|*EHlXvgg)V34`QPo z?N%_wRCAb9X4(Y8<)nlNEvNJ-^Lar8@tr&R;>G(827<`xf2xxXch96p#pjjLkXAhd zjTRH*UsW86Y%4`!!*#jE%=8??&=9NEv)+i=&qpLCaBint;20bg%EF>HS#L2*<_{Jb z8XOcbgwAIT71ab>%QvIjiLpFNgAGBbR#;r_m1+F!?(w-l? z4d8<-9Gk8W6?Zd9^rfh55)HZGd&DE%=O~vCLrbA<#;N}Me$2iLWmj=>#>z*@c^yV- z-|9{NrAth@x-5GT{#50hjaN1u*aAhEwMKXbM`CT*Dk)A$&!HKLZ*QJT(OzQ|vVYuB zUZ42Goq2J860LL%t2}-Be(+wtFZ}IL3uc@%>9J4^`N@5N@|!)KB6MZW(k6#3$6;ho z-0Jh_D{zEc8@S-W5`+%kId{qtuS zCGXuU@2YjkZZtB>b++zrN#gODDBioaX9#();=#{AA#RKlpFknHDoO|Tr4FpJ?Rd&$ zl4<8hm-^&KTH3%`A9zO zsW*%~D@nF!balTb+I^Zn{77L*G0wp-Y0O4tV`Jk} zVsUbHZOZDqhvqWmd=gk|XCs253?mI3t;^K`V`c_C=bu@_|$|Sp1uAES~X8kW$8=} zmR`mTaAK(JZt;qO)j~Wz`N+>d*O1>Nk2FRt64`B!gSfM#4n5Hh_KfVp{|Ym?*C4RtNGAdGI`@|k8(am(_wRa0Z8 zjEFEJ9}pR7cS2GMd8R>C^4%RD}U1 zQQZ0iKiE?;uk|T{?Q~ zd19beQ}$M4o=2oZt@rLJ2!FEY-D#NhATKv#21F<&EKNR`*TSWMzN;G<8Not(w^y1e zKK>w=q^g~WupxI>)$vMy=Sj#Nmq?I9At7X3b8WqhQXTd+fnr=w1|^sD)ZLwK zLF?x3U4qJz@pkPybLE#4URD0PC9lN@tAY;B3kxf+EI|Q=G}QPkH@a=ESR80KW8)jW2t=IvLxJ4+j?eVfK80C*HEsTv@~Y#NfU_t8 z>T)h+g!3lQc;?X$ViuO>*JN8A3Pjd>DBSxna4jaKSXo&@X1Fz@TswqzZ2eSb!Noty ztJE2JwZkzaMl17>=Ymhp5t+JD^3 zmwgR9_nWh!re_Zy>QF;z$p$|7{HABDr!4k71^+}WwP9cG*vTvW~Bo0yOR=j4z zF@lTvr2M(pbzG({pwWd6NCH)rlLWy zvFj`?lVEg(Uo~=mb8ih&e>I`$=QC|Py~do42l^{*AGupUn?Qnrk#V6^BwTX!*wkPJD(iaWDD;VX7-Dqg*IWMeVA&J`kphiYm(4FHu?yV0T~g z_4n_c&pF|sh&T<31@?qy$|8MsaKEEt8ijJCcp*fldx`emTw48n_M$SO;uEOoA&cM_GKp3uiiT%z$lCMDl zl~DA`e&qGv5z*WmEi-C?kSE|F`O!n=Jx!A+BTDN<%yINhyjPRgaKXuw>%7*s-`eWb zv9azHa>!6-brI)@m}O|jPHp~*hU$2;`VSH+r_hZaf5)iq{f4rKOvk#(U*U*guSR^{ zJ$1p-?n-qHoqTtunzAt)Nq~#2(vx@UczSobz`gZ8BWBWY@vgPN?ujK8a#sPZUxt(# z5D*Yz+}YF9c)RU^m?f1@iURLaEzFaK)2l|D;{^9s^J=mRuNj06z*o~Bvm`Ax-I(sU zxKmEojVB%a?I}(}_ZN>?>>jqPm{R!2E8*IVHg)RgVHH)? z61B~YXaduqU!sBGVf2|>!_^P{b-MDu4Jo+Ax;O-HE_=^Z;RtlavvuI@xLbv}yX z1^A6aC5$yRDnJjFGAVddf5P+%axU|qjjxugCS!an(5;)ak11T{gn5iyfq*&DVv=|F z!cx77*4=LGziI(3g}$_-0{fY zt0F#S+%+tnOF+ zG||jR-!-hmu#Zi*^|m6P*9)ai7MAp|`$EZLO7Cx_m~@qAefQ^~(ElTB-gz&2+(|L) z{m2aq1fJGXS5?KUgee<@mDoAZ$k6M@yy8$%PXo%&;DgHdtln}!!QH*`Thg&M)6jSv zD?XJ^HEA_9R;6vwS9OZ#Y`0Wb&y^Dwag5{aR$y zks9#C6oDMN+mtSE>9wysm||djI>f* zvV+RQh!vqINc)kCgFV@a4%~mwy>jIS8V*C~6bti=zkh$aJiO!e>-EVMktf=S1e~m= z5n00MhxVW8QnRUCr%tv4Z)5^GD`NU;k}ZL&Wi*6rzdpSEJe-gE`g$mBI`U{k%Cf6- zZ{0O<>!;n?+Nq8Cvnp%&mR7|tq5=J5oX2dFzJI63UT#FP{wYOv=>e}i7l^G~{ukas zZQQbwMId$e<+MUg=Z7R7^_g<0b3f9MY8UeG@c1WT0jaVPEricUu%jbSqbh=}gNcP@ z3$?wc(Y3E>`l8I`egrL`jedwPr+STlkflTUob8&G6eGQYC7Q#iGqNm?#1p5o%5B|- z4M%HJzImH+At-2bf$vfn$;xA8^%+B%w{PQkv%z5VxY#Y$|(H`JE)Un^uIn?4e2;a=3XBj0D zV+2w|xvo8j?d>b$=4X*HQhq<9yDCM<(Rlroipc98KSX)Ity>QrE;{Bk+4Z1z%h5+Q zkG7@{=};yBGZY+Bx#CPHG5GnIsWdiN2(O4QR{vrlX8(#=OU&xoNyPx_*^3v8%ge#7 z2Ha*Ia7B|(;yxx}sj{c6+OHYz`IeE5ov!_%$EhaSt*r=2q~GZx0TZ9Wn$#uV2uDrK zPY~9jzQHVcfk?-U7Am^Ap}QBHwbYL4pLCeefQ!deFn3|Y%+UH34*ww*61yNn^^V^; zqu{8|Fe9W3O0&t%OsaaVtVEad*8e`V#?0^N_-J5g#{lv)lOFoi$+&rQwU-=GmA}ze zLPD3TktJu8h?uU4S%{9kGyU+=1Jzs7A{u&I0p+Z869UY28QS8ur_bgkr z%@DgPtTC|ihe?=YOM*FgrPc(8tVb~`1lj9x`$V>kXj7-4Hi5E4UijzT*6ewYhrN+% z%{FV_vjL16h($3mZO2&hP8{>qeEpipO~)~re8@vr^Xo? zL*7XcC#m=)`CnefTEx$~oGv#Lc$@``_!mShYpcS$5yg!Qq`XY7t;0_m7(HLbmtZ5! z@p?7>8R7al-ah)D|Bq^k|7OuIJzp~SbAM6O{5RZ!xvNIMYv!&R5YM^0Wp2~_uYQ}_ z+}<*`w|xDp`QDUBf{*#8nf{%ExK{tAZ}FSE@8<5ixtr;)aw2o9WNwwrt&+J_!aKJN z{%jevPTw(GL_ZV}PESx4uE9|>)MiQnsO8$>tE=F=SKkQ|& VdgrY0nEwB(D5~#C*>&LbzW@cOM0WrH literal 0 HcmV?d00001 diff --git a/examples/desktop/docs/qa-artifact-compact/before.png b/examples/desktop/docs/qa-artifact-compact/before.png new file mode 100644 index 0000000000000000000000000000000000000000..949a7798c5a97f68e3ff42e424aa7a025ea04924 GIT binary patch literal 47026 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 + + + + + artifact-compact fixture (BEFORE) + + + + +
+
+
BEFORE: 8 markdown artifacts (hero-card shape)
+
+
+
+ + + diff --git a/examples/desktop/docs/qa-artifact-compact/fixture-expanded.html b/examples/desktop/docs/qa-artifact-compact/fixture-expanded.html new file mode 100644 index 0000000000..9e451169a6 --- /dev/null +++ b/examples/desktop/docs/qa-artifact-compact/fixture-expanded.html @@ -0,0 +1,51 @@ + + + + + + artifact-compact fixture (expanded) + + + + +
+
+
Fixture: L1 body expanded on row 3
+
+
+
+ + + + + diff --git a/examples/desktop/docs/qa-artifact-compact/fixture.html b/examples/desktop/docs/qa-artifact-compact/fixture.html new file mode 100644 index 0000000000..42cc48f081 --- /dev/null +++ b/examples/desktop/docs/qa-artifact-compact/fixture.html @@ -0,0 +1,63 @@ + + + + + + artifact-compact fixture + + + + +
+
+
Fixture: 8 markdown artifacts arriving in a row
+
+
+
+ + + + + + diff --git a/examples/desktop/scripts/assemble-oss-release.sh b/examples/desktop/scripts/assemble-oss-release.sh index e097ad42e8..15713ea371 100755 --- a/examples/desktop/scripts/assemble-oss-release.sh +++ b/examples/desktop/scripts/assemble-oss-release.sh @@ -197,6 +197,38 @@ if grep -q '"name": "dsh-desktop-demo"' "$PKGJSON"; then fi echo " rewrote: package.json name β†’ dsh-desktop" +# ─── Stage 2d: whitelist-restore hand-picked assets ───────────────────── +# Stage 2 rm's whole directories in EXCLUDES (e.g. docs/demo-shots) to +# keep ~90 MB of screenshot archives / internal QA runs out of the OSS +# release tree. A very small number of individual files under those +# excluded parents ARE meant to ship β€” for example the single final +# walkthrough recording referenced from README. Restore them here by +# re-extracting straight from the same git HEAD used in Stage 1, so we +# ship exactly the committed bytes (no lookaside copy that could drift). +# +# Fail loud if a whitelisted file is missing from HEAD β€” that means the +# source repo forgot to commit it and README's link would 404. +WHITELIST=( + "docs/demo-shots/showcase-2026-07-18/demo-walkthrough.mp4" +) +echo "[assemble-oss-release] restoring ${#WHITELIST[@]} whitelist entries…" +for path in "${WHITELIST[@]}"; do + # `git archive` errors non-zero if the path isn't in HEAD, so we probe + # first with `git ls-tree` and give a readable error before the extract. + if ! (cd "$REPO_ROOT" && git ls-tree --name-only HEAD -- "$path" \ + | grep -qxF "$path"); then + echo " MISSING: $path is not in HEAD β€” commit it in the source repo first" >&2 + exit 3 + fi + (cd "$REPO_ROOT" && git archive HEAD -- "$path") | tar -x -C "$OUT_DIR" + target="$OUT_DIR/$path" + if [ ! -f "$target" ]; then + echo " RESTORE-FAIL: $path did not land in $OUT_DIR" >&2 + exit 3 + fi + echo " restored: $path" +done + # ─── Stage 3: verification grep ───────────────────────────────────────── # Any residual hit here means the exclude list has drifted β€” abort. echo "[assemble-oss-release] verifying scrub…" diff --git a/examples/desktop/src/renderer/chat-session-graph.js b/examples/desktop/src/renderer/chat-session-graph.js index 08f25718c8..1eeba1a7e3 100644 --- a/examples/desktop/src/renderer/chat-session-graph.js +++ b/examples/desktop/src/renderer/chat-session-graph.js @@ -22,23 +22,110 @@ const COL_W = 60 const PAD_Y = 24 const PAD_X = 40 +// Extract user-visible text from a user/message data payload. Mirrors +// chat-side-drawer.extractText β€” kept local so the two views can diverge +// on truncation policy without cross-coupling. +function extractText(data) { + if (!data) return '' + if (typeof data === 'string') return data + if (typeof data.text === 'string') return data.text + if (typeof data.content === 'string') return data.content + if (Array.isArray(data.content)) { + return data.content.map((c) => (c && typeof c.text === 'string') ? c.text : '').join(' ') + } + if (typeof data.delta === 'string') return data.delta + return '' +} + +// First non-empty line of `text`, capped at ~28 chars with an ellipsis. +// 28 is a deliberate crop: the label reads on a single row alongside the +// `user Β·` tag; beyond ~35 chars SVG text bleeds into the next column at +// the default zoom. +function firstLine28(text) { + if (typeof text !== 'string') return '' + const trimmed = text.trim() + if (!trimmed) return '' + const nl = trimmed.indexOf('\n') + const line = nl === -1 ? trimmed : trimmed.slice(0, nl) + return line.length > 28 ? line.slice(0, 27) + '…' : line +} + +function typeOf(evt) { + return (evt && (evt.type || evt.event)) || '' +} + +// Repair the wire event stream so the DAG reads in causal order. Two +// passes: +// 1. Stable sort by evt.seq (missing seq is treated as 0, so unstamped +// events keep their relative order since Array.sort is stable). +// 2. Adjacent-pair fixup: the runtime protocol serialises turn/start +// before its triggering user/message echo β€” renderer.js ~L809 +// calls this out; the main chat stream hides it via optimistic +// bubble + echo adoption, but the Graph view sees the raw order +// and would draw the user node after its own turn. When a +// turn/start lands with no unclaimed user/message preceding it +// and the next event IS a user/message, swap them so user comes +// first. Fork / interrupt / turn/end are left in place β€” only +// the (turn/start, user/message) reversal is repaired. +function reorderEvents(events) { + if (!Array.isArray(events)) return [] + const sorted = events.slice().sort((a, b) => { + const sa = (a && typeof a.seq === 'number') ? a.seq : 0 + const sb = (b && typeof b.seq === 'number') ? b.seq : 0 + return sa - sb + }) + let pendingUser = false + for (let i = 0; i < sorted.length; i++) { + const t = typeOf(sorted[i]) + if (t === 'user/message') { + pendingUser = true + } else if (t === 'turn/start' || t === 'turn.start') { + if (pendingUser) { + pendingUser = false + } else if (i + 1 < sorted.length && typeOf(sorted[i + 1]) === 'user/message') { + // Only swap when the user's seq is at or right after the turn's + // (co-temporal echo). A large seq gap would mean the user + // arrived much later β€” a barge-in, not the echo bug β€” and must + // stay after the turn so the graph reads truthfully. + const turnSeq = (typeof sorted[i].seq === 'number') ? sorted[i].seq : 0 + const userSeq = (typeof sorted[i + 1].seq === 'number') ? sorted[i + 1].seq : 0 + if (userSeq - turnSeq <= 1) { + const tmp = sorted[i] + sorted[i] = sorted[i + 1] + sorted[i + 1] = tmp + pendingUser = false // swapped-in user is paired with this turn + i += 1 // skip past the just-paired turn so we don't re-process it + } + } + } else if (t === 'turn/end' || t === 'turn.end') { + pendingUser = false + } + } + return sorted +} + // Derive nodes + edges from a cachedEvents list. Same event model as // chat-side-drawer.deriveTurnRows so the two views agree. function deriveGraph(events) { const nodes = [] const edges = [] if (!Array.isArray(events)) return { nodes, edges } + const ordered = reorderEvents(events) let currentTurn = null let turnIdx = 0 let lastNodeId = null - for (const evt of events) { + for (const evt of ordered) { if (!evt || typeof evt !== 'object') continue const type = evt.type || evt.event || '' const data = evt.data || {} if (type === 'user/message') { const id = `u${nodes.length}` + const raw = extractText(data).trim() + const preview = firstLine28(raw) + const label = preview ? `user Β· "${preview}"` : 'user' nodes.push({ - id, kind: 'user', label: 'user', + id, kind: 'user', label, + title: raw && raw !== preview ? raw : null, turnId: null, seq: evt.seq || 0, }) if (lastNodeId != null) edges.push({ from: lastNodeId, to: id, kind: 'succession' }) @@ -47,6 +134,7 @@ function deriveGraph(events) { const id = `t${turnIdx}` currentTurn = { id, kind: 'turn', label: `#${turnIdx}`, + title: null, turnId: data.turnId || data.turn_id || id, seq: evt.seq || 0, interrupted: false, @@ -59,13 +147,19 @@ function deriveGraph(events) { } else if (currentTurn && (type === 'user/interrupt' || type === 'user/cancel')) { currentTurn.interrupted = true } else if (currentTurn && (type === 'turn/end' || type === 'turn.end')) { - const stop = (data.stopReason || data.stop_reason || '').toString().toLowerCase() + const stopRaw = (data.stopReason || data.stop_reason || '').toString().trim() + const stop = stopRaw.toLowerCase() if (stop.includes('cancel') || stop.includes('interrupt') || stop.includes('reject')) { currentTurn.interrupted = true } if (currentTurn.interrupted) { currentTurn.kind = 'interrupt' } + if (stopRaw) { + const short = stopRaw.length > 20 ? stopRaw.slice(0, 19) + '…' : stopRaw + currentTurn.label = `${currentTurn.label} Β· ${short}` + if (stopRaw.length > 20) currentTurn.title = stopRaw + } currentTurn = null } else if (type === 'session/fork' || type === 'session.fork') { const parentTurnId = data.fromTurnId || data.parentTurnId @@ -168,6 +262,15 @@ function renderSessionGraph(container, snapshot) { label.setAttribute('x', String(pos.x + NODE_R + 6)) label.setAttribute('y', String(pos.y + 4)) label.textContent = node.label + // Full text on hover: user messages truncated to 28 chars in the + // label carry the raw first line here; a turn whose stopReason got + // clipped surfaces the full reason. `` inside an SVG `<g>` + // yields the native browser tooltip without any extra scripting. + if (node.title) { + const titleEl = doc.createElementNS(SVG_NS, 'title') + titleEl.textContent = node.title + g.appendChild(titleEl) + } g.appendChild(label) // Bind click on any node that carries an actionable identifier: // turn nodes β†’ turnId, fork nodes β†’ childSessionId, user nodes β†’ @@ -194,6 +297,7 @@ if (typeof module !== 'undefined' && module.exports) { deriveGraph, layoutGraph, renderSessionGraph, + reorderEvents, _constants: { NODE_R, ROW_H, COL_W, PAD_X, PAD_Y }, } } @@ -202,6 +306,7 @@ if (typeof window !== 'undefined') { deriveGraph, layoutGraph, renderSessionGraph, + reorderEvents, } } diff --git a/examples/desktop/test/artifact-compact-row.test.js b/examples/desktop/test/artifact-compact-row.test.js new file mode 100644 index 0000000000..f3232ccbbf --- /dev/null +++ b/examples/desktop/test/artifact-compact-row.test.js @@ -0,0 +1,208 @@ +// Lock the artifact-card compact row shape (density-spec Β§2 L0, user +// directive 2026-07-18 P0). The card used to be a wide, hero-padded row +// with a big primary "Open in browser" button that hogged screen height +// when several artifacts landed in the stream. The fix reshapes it to: +// +// * `<details class="artifact-card">` with a native `<summary>` +// row (`.artifact-row`) β€” collapsed by default, expands inline. +// * L0 row = small 14px icon + filename + kind chip + version chip + +// live dot + tiny `open β†—` link (no primary button). +// * L1 body (`.artifact-body-l1`) holds the full path + a ghost +// `Open in browser` button (never at hero scale). +// * Consecutive `.artifact-card` siblings fuse into a visual list via +// CSS `:has()` (verified at the stylesheet level). +// +// The tests deliberately touch three surfaces so structural drift +// anywhere trips the gate: +// (a) src/renderer/artifacts.js β€” DOM builder (source strings). +// (b) src/renderer/style.css β€” row height, L1 body, group fusing. +// (c) IIFE contract β€” window.__dshArtifacts.onArtifactEvent. + +'use strict' + +const test = require('node:test') +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') + +const ROOT = path.join(__dirname, '..') +const artifactsSrc = fs.readFileSync(path.join(ROOT, 'src/renderer/artifacts.js'), 'utf8') +const styleCss = fs.readFileSync(path.join(ROOT, 'src/renderer/style.css'), 'utf8') + +// ---------- (a) DOM builder shape ---------------------------------------- + +test('artifacts.js: card root is a <details> element (not a bare div)', () => { + assert.match( + artifactsSrc, + /createElement\(['"]details['"]\)[\s\S]{0,120}el\.className\s*=\s*['"]artifact-card['"]/, + 'artifact-card root must be built as <details> so the row expands inline' + ) +}) + +test('artifacts.js: L0 row uses <summary class="artifact-row">', () => { + assert.match( + artifactsSrc, + /createElement\(['"]summary['"]\)[\s\S]{0,120}summary\.className\s*=\s*['"]artifact-row['"]/, + 'compact row must be a <summary> with class="artifact-row"' + ) +}) + +test('artifacts.js: L0 renders a tiny `open β†—` link (not a primary button)', () => { + // The link is on the summary row; the big button moved to the L1 body. + assert.match( + artifactsSrc, + /className\s*=\s*['"]artifact-open-link['"]/, + 'summary row must render an .artifact-open-link tiny action' + ) + assert.match( + artifactsSrc, + /open\s+β†—/, + 'action label must be `open β†—` (density-spec Β§2 L0 icon/link scale)' + ) +}) + +test('artifacts.js: L0 summary does NOT render an .artifact-open.primary button', () => { + // The old wide-card shape had `openBtn.className = 'artifact-open primary'` + // *directly on the summary row*. The primary button is banned from L0. + assert.doesNotMatch( + artifactsSrc, + /['"]artifact-open primary['"]/, + 'the hero primary button must not exist anywhere; L0 uses a link, L1 uses a ghost button' + ) +}) + +test('artifacts.js: L1 body wrapper exists with .artifact-body-l1', () => { + assert.match( + artifactsSrc, + /className\s*=\s*['"]artifact-body-l1['"]/, + 'L1 body wrapper must be rendered so users can expand for details' + ) +}) + +test('artifacts.js: L1 body contains a ghost "Open in browser" button', () => { + // Ghost/small classes are the density-spec convention for non-hero + // actions used elsewhere (see .ghost.small usage across renderer). + assert.match( + artifactsSrc, + /artifact-open ghost small[\s\S]{0,80}Open in browser/, + 'L1 body must expose an Open-in-browser action at ghost/small scale' + ) +}) + +test('artifacts.js: still exposes window.__dshArtifacts.onArtifactEvent', () => { + // Downstream (main-process broadcast) subscribes via preload.onArtifact, + // but this seam is what the debug menu + smoke tests hit. + assert.match( + artifactsSrc, + /window\.__dshArtifacts\s*=\s*\{[\s\S]*?onArtifactEvent[\s\S]*?\}/, + '__dshArtifacts.onArtifactEvent seam must remain (mock button + tests)' + ) +}) + +test('artifacts.js: openArtifact IPC still wired through window.dsh.openArtifact', () => { + assert.match( + artifactsSrc, + /window\.dsh\.openArtifact\(entry\.artifactId\)/, + 'preload β†’ main open bridge must still be the mechanism for open β†—' + ) +}) + +test('artifacts.js: appendGrouped wraps consecutive cards into an .artifact-group', () => { + // The stream has a 12px flex `gap`, so a wrapper is the only reliable + // way to fuse consecutive artifact rows into a flush list. Locks the + // renderer path that creates .artifact-group as-needed. + assert.match( + artifactsSrc, + /function\s+appendGrouped\(/, + 'appendGrouped helper must exist to fuse consecutive artifact cards' + ) + assert.match( + artifactsSrc, + /group\.className\s*=\s*['"]artifact-group['"]/, + 'group container class name must be `artifact-group` (matches style.css)' + ) +}) + +test('style.css: .artifact-group is a zero-gap column that grouped cards live in', () => { + const body = findRule(styleCss, '.artifact-group {') + assert.ok(body, '.artifact-group rule must exist') + assert.match(body, /gap:\s*0/, 'group gap must be 0 so grouped rows sit flush') + assert.match(body, /flex-direction:\s*column/, 'group is a vertical stack') +}) + +test('style.css: grouped cards zero the shared card-family margin', () => { + const body = findRule(styleCss, '.artifact-group > .artifact-card {') + assert.ok(body, 'grouped-card rule must exist') + assert.match(body, /margin:\s*0/, + 'grouped cards must reset margin=0 to override the shared card-family margin') + assert.match(body, /border-top:\s*none/, + 'grouped cards must drop top-border to collapse the shared seam') +}) + +// ---------- (b) CSS: row height, L1 body, group fusing -------------------- + +function findRule(css, selector) { + // Naive but adequate: match `selector { … }` for a single leaf rule. + // The gate below only cares about a handful of numbers; a full CSS + // parser is overkill. + const idx = css.indexOf(selector) + if (idx < 0) return null + const brace = css.indexOf('{', idx) + const close = css.indexOf('}', brace) + if (brace < 0 || close < 0) return null + return css.slice(brace + 1, close) +} + +test('style.css: .artifact-row has min-height ≀ 32px (L0 compact)', () => { + const body = findRule(styleCss, '.artifact-row {') + assert.ok(body, '.artifact-row rule must exist') + const m = body.match(/min-height:\s*(\d+)px/) + assert.ok(m, '.artifact-row must declare min-height') + const px = Number(m[1]) + assert.ok(px <= 32, `L0 row min-height must be ≀ 32px, found ${px}px`) +}) + +test('style.css: .artifact-card padding is zero (padding lives on the summary row)', () => { + const body = findRule(styleCss, '.artifact-card {') + assert.ok(body, '.artifact-card rule must exist') + // Old fat rule was `padding: 10px 12px;` β€” verify the flat card has + // shed it so the summary can drive its own compact padding. + assert.match(body, /padding:\s*0\s*;/, '.artifact-card must have padding:0 (row owns spacing)') +}) + +test('style.css: .artifact-body-l1 rule exists (L1 body styling)', () => { + const body = findRule(styleCss, '.artifact-body-l1 {') + assert.ok(body, '.artifact-body-l1 rule must be defined for the expanded body') + assert.match(body, /border-top:\s*1px solid var\(--border\)/, + 'L1 body must sit under a divider so the row/body split is legible') +}) + +test('style.css: adjacent artifact-cards fuse via `:has(+ .artifact-card)`', () => { + assert.match( + styleCss, + /\.artifact-card:has\(\+\s*\.artifact-card\)/, + 'auto-group fusing rule must exist so β‰₯2 cards render as one list' + ) + assert.match( + styleCss, + /\.artifact-card\s*\+\s*\.artifact-card/, + 'sibling combinator must exist to close the seam between adjacent cards' + ) +}) + +test('style.css: no hero `.artifact-open.primary` style survives', () => { + // Belt & suspenders β€” the DOM never emits it AND the stylesheet drops + // the class name entirely. + assert.doesNotMatch( + styleCss, + /\.artifact-open\.primary\b/, + 'primary variant of .artifact-open must not exist; L0 has no hero button' + ) +}) + +test('style.css: .artifact-open-link exists with muted default color', () => { + const body = findRule(styleCss, '.artifact-open-link {') + assert.ok(body, '.artifact-open-link rule must be defined') + assert.match(body, /color:\s*var\(--muted\)/, + 'the tiny action link defaults to muted so it reads as a link, not a button') +}) diff --git a/examples/desktop/test/chat-triple-view.test.js b/examples/desktop/test/chat-triple-view.test.js index b3e26e4ae0..afed791cce 100644 --- a/examples/desktop/test/chat-triple-view.test.js +++ b/examples/desktop/test/chat-triple-view.test.js @@ -220,3 +220,156 @@ test('renderSessionGraph: empty state when no events', () => { const empty = container._children[0] assert.equal(empty.className, 'chat-session-graph-empty') }) + +// -- Session graph: event ordering & labels -------------------------------- +// The runtime protocol serialises turn/start before its triggering +// user/message echo β€” the main stream masks this with an optimistic +// bubble + echo adoption dance (renderer.js ~L809), but the Graph view +// sees the raw wire order. These tests pin the reorder/repair pass in +// deriveGraph() so a wire-order fixture still draws the DAG in causal +// order, and pin the enriched node labels. + +test('deriveGraph: sorts out-of-order events by evt.seq', () => { + const events = [ + { type: 'turn/start', seq: 6, data: { turnId: 't1' } }, + { type: 'user/message', seq: 1, data: { text: 'hello' } }, + { type: 'turn/end', seq: 8, data: { turnId: 't1' } }, + { type: 'turn/start', seq: 2, data: { turnId: 't0' } }, + { type: 'user/message', seq: 5, data: { text: 'again' } }, + { type: 'turn/end', seq: 4, data: { turnId: 't0' } }, + ] + const g = graph.deriveGraph(events) + const kinds = g.nodes.map((n) => n.kind) + // seq-sorted stream is: u1 t2 e4 u5 t6 e8 β†’ nodes u, t, u, t + assert.deepEqual(kinds, ['user', 'turn', 'user', 'turn']) +}) + +test('deriveGraph: repairs (turn/start, user/message) reversal at close seq', () => { + // Wire-order bug: turn/start echoes before its triggering user/message. + // Both events share an adjacent seq window, so the repair pass must + // swap them so user precedes its turn in the DAG. + const events = [ + { type: 'turn/start', seq: 2, data: { turnId: 't0' } }, + { type: 'user/message', seq: 3, data: { text: 'do the thing' } }, + { type: 'assistant/message', seq: 4, data: { text: 'ok' } }, + { type: 'turn/end', seq: 5, data: { turnId: 't0' } }, + ] + const g = graph.deriveGraph(events) + assert.equal(g.nodes.length, 2) + assert.equal(g.nodes[0].kind, 'user', 'user node must come first') + assert.equal(g.nodes[1].kind, 'turn', 'turn node must follow user') + const succ = g.edges.find((e) => e.kind === 'succession') + assert.equal(succ.from, g.nodes[0].id) + assert.equal(succ.to, g.nodes[1].id) +}) + +test('deriveGraph: repairs pair even when seqs are identical', () => { + const events = [ + { type: 'turn/start', seq: 10, data: { turnId: 't0' } }, + { type: 'user/message', seq: 10, data: { text: 'same tick' } }, + ] + const g = graph.deriveGraph(events) + assert.deepEqual(g.nodes.map((n) => n.kind), ['user', 'turn']) +}) + +test('deriveGraph: preserves user AFTER turn on genuine barge-in (seq gap)', () => { + // Barge-in: user interrupts an in-flight turn much later than start. + // The gap (seq 20 vs 10) is too wide to be an echo β€” must stay + // ordered as the wire had it (turn then user), because chronology is + // real. + const events = [ + { type: 'turn/start', seq: 10, data: { turnId: 't0' } }, + { type: 'user/message', seq: 20, data: { text: 'wait cancel' } }, + ] + const g = graph.deriveGraph(events) + assert.deepEqual(g.nodes.map((n) => n.kind), ['turn', 'user']) +}) + +test('deriveGraph: user node label carries a truncated first-line preview', () => { + const events = [ + { type: 'user/message', seq: 1, data: { text: 'short one' } }, + { type: 'turn/start', seq: 2, data: { turnId: 't0' } }, + { type: 'user/message', seq: 3, data: { text: 'x'.repeat(80) } }, + { type: 'user/message', seq: 4, data: { text: 'line1\nline2\nline3' } }, + ] + const g = graph.deriveGraph(events) + const users = g.nodes.filter((n) => n.kind === 'user') + assert.equal(users.length, 3) + assert.equal(users[0].label, 'user Β· "short one"') + // Long-string label is truncated with an ellipsis (label = `user Β· "` + // (8) + up to 28 preview + `"` (1) = at most 37 chars). + assert.ok(users[1].label.endsWith('…"'), 'long text should be truncated with an ellipsis') + assert.ok(users[1].label.length <= 38, 'label must not exceed the 28-char preview cap + framing') + // Truncated preview exposes full text on hover via node.title. + assert.equal(users[1].title, 'x'.repeat(80)) + // Only the first line is used; subsequent lines are dropped. + assert.equal(users[2].label, 'user Β· "line1"') +}) + +test('deriveGraph: turn node label appends stopReason when set', () => { + const events = [ + { type: 'turn/start', seq: 1, data: { turnId: 't0' } }, + { type: 'turn/end', seq: 2, data: { turnId: 't0', stopReason: 'end_turn' } }, + { type: 'turn/start', seq: 3, data: { turnId: 't1' } }, + { type: 'turn/end', seq: 4, data: { turnId: 't1' } }, + { type: 'turn/start', seq: 5, data: { turnId: 't2' } }, + { type: 'turn/end', seq: 6, data: { turnId: 't2', stopReason: 'cancelled' } }, + ] + const g = graph.deriveGraph(events) + const turns = g.nodes.filter((n) => n.kind === 'turn' || n.kind === 'interrupt') + assert.equal(turns[0].label, '#0 Β· end_turn') + assert.equal(turns[1].label, '#1', 'no stopReason β†’ no suffix') + assert.equal(turns[2].kind, 'interrupt') + assert.match(turns[2].label, /^#2 Β· cancelled$/) +}) + +test('deriveGraph: reorder preserves fork parent + interrupt classification', () => { + // buildFixture()'s scenario with each (turn/start, user/message) pair + // swapped to simulate the wire-order bug. Fork/interrupt outcomes + // must land on the exact same edges after the repair pass. + const events = [ + { type: 'turn/start', seq: 2, data: { turnId: 't0' } }, + { type: 'user/message', seq: 1, data: { text: 'hello there' } }, + { type: 'assistant/message', seq: 3, data: { text: 'hi' } }, + { type: 'turn/end', seq: 4, data: { turnId: 't0' } }, + + { type: 'turn/start', seq: 6, data: { turnId: 't1' } }, + { type: 'user/message', seq: 5, data: { text: 'run a task' } }, + { type: 'assistant/message', seq: 7, data: { text: 'sure' } }, + { type: 'turn/end', seq: 8, data: { turnId: 't1' } }, + + { type: 'session/fork', seq: 9, data: { fromTurnId: 't1', childSessionId: 'child-abc' } }, + + { type: 'turn/start', seq: 11, data: { turnId: 't2' } }, + { type: 'user/message', seq: 10, data: { text: 'wait, cancel' } }, + { type: 'user/interrupt', seq: 12, data: {} }, + { type: 'turn/end', seq: 13, data: { turnId: 't2', stopReason: 'cancelled' } }, + ] + const g = graph.deriveGraph(events) + const forkEdge = g.edges.find((e) => e.kind === 'fork') + assert.ok(forkEdge, 'fork edge missing after reorder') + const forkParent = g.nodes.find((n) => n.id === forkEdge.from) + assert.equal(forkParent.turnId, 't1') + const interruptNode = g.nodes.find((n) => n.kind === 'interrupt') + assert.ok(interruptNode, 'interrupt node missing after reorder') + assert.equal(interruptNode.turnId, 't2') + // First succession edge on the main line goes user β†’ turn (repaired). + const first = g.edges.find((e) => e.kind === 'succession') + assert.equal(g.nodes.find((n) => n.id === first.from).kind, 'user') + assert.equal(g.nodes.find((n) => n.id === first.to).kind, 'turn') +}) + +test('renderSessionGraph: emits SVG <title> tooltip for truncated user labels', () => { + const doc = makeMiniDoc() + const container = doc.createElement('div') + container.ownerDocument = doc + graph.renderSessionGraph(container, { + events: [{ type: 'user/message', seq: 1, data: { text: 'y'.repeat(60) } }], + }) + const svg = container._children[0] + const userG = svg._children.find((c) => typeof c.className === 'string' && c.className.includes('node-user')) + assert.ok(userG, 'user node group missing') + const titleEl = userG._children.find((c) => c.tagName === 'TITLE') + assert.ok(titleEl, 'expected <title> tooltip child on the user node') + assert.equal(titleEl.textContent, 'y'.repeat(60)) +}) diff --git a/examples/desktop/test/model-profile-guard.test.js b/examples/desktop/test/model-profile-guard.test.js index 514d15668c..148ab4f48d 100644 --- a/examples/desktop/test/model-profile-guard.test.js +++ b/examples/desktop/test/model-profile-guard.test.js @@ -69,9 +69,11 @@ test('modelsFor: the profile default model IS in its supported list (no self-mis } }) -// PROFILE_MODELS mirrors the `id` fields in each DeepSeek model catalog so -// the renderer cannot drift from the runtime's validated configuration. -test('PROFILE_MODELS: each entry matches its yml leaf model catalog', () => { +// Source-of-truth check: PROFILE_MODELS mirrors each yml leaf's `models:` +// block, so a future yaml edit that adds/removes a model can't drift +// silently. We parse the yaml the shell-way β€” one leaf per profile β€” and +// compare the `models:` list line-by-line. +test('PROFILE_MODELS: each entry matches its yml leaf models: block', () => { const leafFor = { 'daemon-echo': null, // mock-llm β€” no models: block in yaml, always mock-echo 'stdio-echo': null, // mock-llm β€” ditto @@ -89,8 +91,11 @@ test('PROFILE_MODELS: each entry matches its yml leaf model catalog', () => { continue } const yaml = fs.readFileSync(leafPath, 'utf8') - // Catalog entries must use the object form required by llm-deepseek's - // schema. A scalar entry leaves yamlModels empty and fails this test. + // Find the `models:` block under `llm-deepseek` and collect its + // `- <name>` entries. The block is 6-space-indented, sits inside a + // `config:` map, and terminates when the indent drops back to a + // 2-space `- id:` list item. Bail early at the first line whose + // trim doesn't start with `- ` after the models: header. const lines = yaml.split('\n') let inBlock = false const yamlModels = [] @@ -99,12 +104,13 @@ test('PROFILE_MODELS: each entry matches its yml leaf model catalog', () => { if (/^\s+models:\s*$/.test(rawLine)) { inBlock = true; continue } continue } - const m = /^\s+-\s+id:\s+([\w-]+)\s*$/.exec(rawLine) + // Inside the block: entries look like ` - deepseek-v4-flash`. + const m = /^\s+-\s+([\w-]+)\s*$/.exec(rawLine) if (m) { yamlModels.push(m[1]); continue } // Any other non-empty line terminates the block. if (rawLine.trim() !== '') break } - assert.ok(yamlModels.length > 0, `${leafPath}: models must contain object entries with id fields`) + assert.ok(yamlModels.length > 0, `${leafPath}: parsed empty models: block`) assert.deepEqual(expected.slice().sort(), yamlModels.slice().sort(), `${profileName} PROFILE_MODELS drift vs ${path.basename(leafPath)}: expected ${JSON.stringify(yamlModels)}, got ${JSON.stringify(expected)}`) }