@sleep2agi/agent-network-dashboard 0.5.1-preview.14 → 0.5.1-preview.141

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +3 -3
  3. package/.next/diagnostics/route-bundle-stats.json +32 -32
  4. package/.next/fallback-build-manifest.json +3 -3
  5. package/.next/prerender-manifest.json +3 -3
  6. package/.next/server/app/_global-error.html +1 -1
  7. package/.next/server/app/_global-error.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/server/app/_not-found.html +2 -2
  15. package/.next/server/app/_not-found.rsc +12 -12
  16. package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
  17. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  18. package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
  19. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  20. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  21. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  22. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  23. package/.next/server/app/admin.html +2 -2
  24. package/.next/server/app/admin.rsc +14 -14
  25. package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
  26. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  27. package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
  28. package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
  29. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  30. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  31. package/.next/server/app/index.html +2 -2
  32. package/.next/server/app/index.rsc +14 -14
  33. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  34. package/.next/server/app/index.segments/_full.segment.rsc +14 -14
  35. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  36. package/.next/server/app/index.segments/_index.segment.rsc +7 -7
  37. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  38. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  39. package/.next/server/app/login.html +2 -2
  40. package/.next/server/app/login.rsc +14 -14
  41. package/.next/server/app/login.segments/_full.segment.rsc +14 -14
  42. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  43. package/.next/server/app/login.segments/_index.segment.rsc +7 -7
  44. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  45. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  46. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  47. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  48. package/.next/server/app/logs.html +2 -2
  49. package/.next/server/app/logs.rsc +14 -14
  50. package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
  51. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  52. package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
  53. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  54. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  55. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  56. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  57. package/.next/server/app/messages.html +2 -2
  58. package/.next/server/app/messages.rsc +14 -14
  59. package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
  60. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  61. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  62. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  63. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  64. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  65. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  66. package/.next/server/app/node.html +2 -2
  67. package/.next/server/app/node.rsc +14 -14
  68. package/.next/server/app/node.segments/_full.segment.rsc +14 -14
  69. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  70. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  71. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  72. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  73. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  74. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  75. package/.next/server/app/nodes.html +2 -2
  76. package/.next/server/app/nodes.rsc +14 -14
  77. package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
  78. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  79. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  80. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  81. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  82. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  83. package/.next/server/app/page.js.nft.json +1 -1
  84. package/.next/server/app/page_client-reference-manifest.js +1 -1
  85. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  86. package/.next/server/app/server-logs.html +2 -2
  87. package/.next/server/app/server-logs.rsc +14 -14
  88. package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
  89. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  90. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  91. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  92. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  93. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  94. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  95. package/.next/server/app/settings/networks.html +2 -2
  96. package/.next/server/app/settings/networks.rsc +14 -14
  97. package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
  98. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  99. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  100. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  101. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  102. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  103. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  104. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  105. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  106. package/.next/server/app/settings/tokens.html +2 -2
  107. package/.next/server/app/settings/tokens.rsc +14 -14
  108. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
  109. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  110. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  111. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  112. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  113. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  114. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  115. package/.next/server/app/settings.html +2 -2
  116. package/.next/server/app/settings.rsc +14 -14
  117. package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  118. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  119. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  120. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  121. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  122. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  123. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  124. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  125. package/.next/server/app/tasks.html +2 -2
  126. package/.next/server/app/tasks.rsc +14 -14
  127. package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
  128. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  129. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  130. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  131. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  132. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  133. package/.next/server/chunks/ssr/{[root-of-the-server]__03b.f76._.js → [root-of-the-server]__0sv~g.o._.js} +2 -2
  134. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -0
  135. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +3 -3
  136. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  137. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  138. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  139. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  140. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  141. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
  142. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
  143. package/.next/server/middleware-build-manifest.js +3 -3
  144. package/.next/server/pages/404.html +2 -2
  145. package/.next/server/pages/500.html +1 -1
  146. package/.next/server/server-reference-manifest.js +1 -1
  147. package/.next/server/server-reference-manifest.json +1 -1
  148. package/.next/static/chunks/{0l_~q07bhpkcx.js → 03a4--7ncekmk.js} +1 -1
  149. package/.next/static/chunks/05kol0eq4~_d5.js +4 -0
  150. package/.next/static/chunks/{03~~oirxz7~vc.js → 07gxjdi4w5ot1.js} +1 -1
  151. package/.next/static/chunks/0a4hmfvj-81x5.css +2 -0
  152. package/.next/static/chunks/0b_0vo6gv.2d0.js +1 -0
  153. package/.next/static/chunks/0o~yjk9gw3w3d.js +1 -0
  154. package/.next/trace +2 -2
  155. package/.next/trace-build +1 -1
  156. package/app/components/ServersDrawer.tsx +17 -0
  157. package/app/components/TopoGraph.tsx +2558 -188
  158. package/package.json +1 -1
  159. package/screenshots/v0.10.2-disk-verify/disk-render-full.png +0 -0
  160. package/screenshots/v0.10.2-disk-verify/disk-render.png +0 -0
  161. package/screenshots/v0.11.0-147/after.png +0 -0
  162. package/scripts/p0-147-screenshot.mjs +53 -0
  163. package/scripts/p0-servers-drawer-screenshot.mjs +2 -0
  164. package/scripts/topo-active-links-chip-hover-lift-test.mjs +93 -0
  165. package/scripts/topo-chip-digit-fontweight-test.mjs +105 -0
  166. package/scripts/topo-chip-digit-hover-bold-test.mjs +94 -0
  167. package/scripts/topo-chip-row-group-hover-brighten-test.mjs +107 -0
  168. package/scripts/topo-chip-row-hover-lift-test.mjs +95 -0
  169. package/scripts/topo-chrome-button-hover-lift-test.mjs +94 -0
  170. package/scripts/topo-chrome-fullscreen-icon-sw-test.mjs +92 -0
  171. package/scripts/topo-chrome-reset-icon-sw-test.mjs +80 -0
  172. package/scripts/topo-chrome-segmented-radius-test.mjs +100 -0
  173. package/scripts/topo-chrome-zoom-icon-sw-test.mjs +90 -0
  174. package/scripts/topo-click-ripple-opacity-test.mjs +99 -0
  175. package/scripts/topo-dashboard-version-attr-test.mjs +69 -0
  176. package/scripts/topo-dense-alias-opacity-test.mjs +68 -0
  177. package/scripts/topo-edge-badge-digit-fw-test.mjs +103 -0
  178. package/scripts/topo-edge-badge-fontsize-test.mjs +90 -0
  179. package/scripts/topo-edge-badge-hover-opacity-test.mjs +94 -0
  180. package/scripts/topo-edge-badge-hover-stroke-test.mjs +92 -0
  181. package/scripts/topo-edge-badge-letter-spacing-test.mjs +86 -0
  182. package/scripts/topo-edge-badge-opacity-test.mjs +80 -0
  183. package/scripts/topo-edge-badge-pin-opacity-test.mjs +86 -0
  184. package/scripts/topo-edge-badge-stroke-test.mjs +92 -0
  185. package/scripts/topo-edge-endpoint-hover-sw-test.mjs +127 -0
  186. package/scripts/topo-edge-freshness-floor-test.mjs +99 -0
  187. package/scripts/topo-edge-particle-hover-r-test.mjs +113 -0
  188. package/scripts/topo-edge-particle-radius-test.mjs +76 -0
  189. package/scripts/topo-edge-visible-linecap-test.mjs +89 -0
  190. package/scripts/topo-endpoint-ring-r-hover-test.mjs +89 -0
  191. package/scripts/topo-filter-pill-hover-lift-test.mjs +101 -0
  192. package/scripts/topo-filter-pill-hover-opacity-test.mjs +110 -0
  193. package/scripts/topo-filter-pill-value-fw-test.mjs +88 -0
  194. package/scripts/topo-filter-pill-x-hover-scale-test.mjs +99 -0
  195. package/scripts/topo-flow-rail-hover-sw-test.mjs +113 -0
  196. package/scripts/topo-flow-rail-linecap-test.mjs +79 -0
  197. package/scripts/topo-freshness-chip-hierarchy-test.mjs +93 -0
  198. package/scripts/topo-freshness-chip-tabular-test.mjs +41 -0
  199. package/scripts/topo-freshness-floor-lift-test.mjs +92 -0
  200. package/scripts/topo-freshness-suffix-tabular-test.mjs +88 -0
  201. package/scripts/topo-group-box-geom-transition-test.mjs +110 -0
  202. package/scripts/topo-group-box-rx-pin-test.mjs +103 -0
  203. package/scripts/topo-group-box-stroke-test.mjs +105 -0
  204. package/scripts/topo-group-label-count-fontweight-test.mjs +108 -0
  205. package/scripts/topo-group-label-count-fw-test.mjs +100 -0
  206. package/scripts/topo-group-label-fw-pin-test.mjs +99 -0
  207. package/scripts/topo-group-label-letter-spacing-test.mjs +113 -0
  208. package/scripts/topo-group-label-tint-geom-test.mjs +94 -0
  209. package/scripts/topo-group-label-tint-transition-test.mjs +97 -0
  210. package/scripts/topo-group-pip-fontsize-test.mjs +106 -0
  211. package/scripts/topo-group-tint-rx-pin-test.mjs +107 -0
  212. package/scripts/topo-hover-detail-body-fw-test.mjs +101 -0
  213. package/scripts/topo-hover-detail-model-fw-test.mjs +98 -0
  214. package/scripts/topo-hover-detail-opacity-test.mjs +98 -0
  215. package/scripts/topo-hover-detail-rx-test.mjs +81 -0
  216. package/scripts/topo-hub-core-fill-hover-test.mjs +85 -0
  217. package/scripts/topo-hub-digit-fontsize-test.mjs +86 -0
  218. package/scripts/topo-hub-digit-fw-hover-test.mjs +102 -0
  219. package/scripts/topo-hub-halo-light-trough-test.mjs +88 -0
  220. package/scripts/topo-hub-halo-r-hover-test.mjs +82 -0
  221. package/scripts/topo-hub-halo-radius-test.mjs +86 -0
  222. package/scripts/topo-hub-halo-trough-test.mjs +83 -0
  223. package/scripts/topo-hub-highlight-opacity-test.mjs +88 -0
  224. package/scripts/topo-hub-highlight-radius-test.mjs +90 -0
  225. package/scripts/topo-hub-hover-ring-opacity-test.mjs +96 -0
  226. package/scripts/topo-hub-hover-ring-stroke-test.mjs +86 -0
  227. package/scripts/topo-hub-spoke-hover-opacity-test.mjs +119 -0
  228. package/scripts/topo-hub-spoke-hover-sw-test.mjs +121 -0
  229. package/scripts/topo-hub-spoke-linecap-test.mjs +80 -0
  230. package/scripts/topo-label-card-opacity-hover-test.mjs +99 -0
  231. package/scripts/topo-layout-toggle-radius-test.mjs +87 -0
  232. package/scripts/topo-legend-count-active-opacity-test.mjs +102 -0
  233. package/scripts/topo-legend-count-pin-fw-test.mjs +90 -0
  234. package/scripts/topo-legend-label-fontweight-test.mjs +94 -0
  235. package/scripts/topo-legend-pin-ring-stroke-test.mjs +101 -0
  236. package/scripts/topo-legend-row-letter-spacing-test.mjs +101 -0
  237. package/scripts/topo-minimap-offline-opacity-test.mjs +90 -0
  238. package/scripts/topo-minimap-online-hover-opacity-test.mjs +92 -0
  239. package/scripts/topo-minimap-online-opacity-test.mjs +93 -0
  240. package/scripts/topo-minimap-online-radius-test.mjs +85 -0
  241. package/scripts/topo-minimap-viewport-linejoin-test.mjs +75 -0
  242. package/scripts/topo-minimap-viewport-opacity-test.mjs +96 -0
  243. package/scripts/topo-minimap-viewport-rx-test.mjs +85 -0
  244. package/scripts/topo-more-flows-fontweight-test.mjs +103 -0
  245. package/scripts/topo-node-alias-letter-spacing-test.mjs +112 -0
  246. package/scripts/topo-node-halo-hover-opacity-test.mjs +104 -0
  247. package/scripts/topo-node-halo-light-offline-test.mjs +80 -0
  248. package/scripts/topo-node-halo-offline-opacity-test.mjs +87 -0
  249. package/scripts/topo-node-label-card-rx-test.mjs +85 -0
  250. package/scripts/topo-node-pulse-peak-test.mjs +89 -0
  251. package/scripts/topo-node-pulse-trough-test.mjs +91 -0
  252. package/scripts/topo-node-sub-text-fw-test.mjs +75 -0
  253. package/scripts/topo-node-sub-text-letter-spacing-test.mjs +115 -0
  254. package/scripts/topo-overlap-stale-build-guard-test.mjs +66 -0
  255. package/scripts/topo-overlap-test.mjs +42 -1
  256. package/scripts/topo-panel-count-fw-hover-test.mjs +105 -0
  257. package/scripts/topo-panel-stroke-hover-test.mjs +110 -0
  258. package/scripts/topo-pressure-bar-height-test.mjs +92 -0
  259. package/scripts/topo-pressure-kicker-fontweight-test.mjs +76 -0
  260. package/scripts/topo-recent-pip-radius-2-test.mjs +72 -0
  261. package/scripts/topo-recent-pip-radius-test.mjs +76 -0
  262. package/scripts/topo-recent-row-content-opacity-test.mjs +81 -0
  263. package/scripts/topo-recent-row-count-pin-fw-test.mjs +106 -0
  264. package/scripts/topo-recent-row-letter-spacing-test.mjs +109 -0
  265. package/scripts/topo-recent-row-pip-hover-r-test.mjs +104 -0
  266. package/scripts/topo-recent-row-text-fontweight-test.mjs +90 -0
  267. package/scripts/topo-runtime-icon-hover-test.mjs +96 -0
  268. package/scripts/topo-spoke-active-opacity-test.mjs +104 -0
  269. package/scripts/topo-spoke-active-stroke-test.mjs +95 -0
  270. package/scripts/topo-spoke-idle-opacity-test.mjs +91 -0
  271. package/scripts/topo-status-ring-hover-sw-test.mjs +105 -0
  272. package/scripts/topo-vendor-chip-hover-lift-test.mjs +87 -0
  273. package/scripts/topo-vendor-glyph-fontweight-test.mjs +102 -0
  274. package/scripts/topo-vendor-letter-hover-scale-test.mjs +129 -0
  275. package/scripts/topo-vendor-suffix-hover-brighten-test.mjs +87 -0
  276. package/scripts/topo-zoom-level-hover-fw-test.mjs +95 -0
  277. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +0 -1
  278. package/.next/static/chunks/03mfrl9fd9smg.js +0 -1
  279. package/.next/static/chunks/0kt7qs66ra2.2.css +0 -2
  280. package/.next/static/chunks/116zy6jdhuaa2.js +0 -1
  281. package/.next/static/chunks/12m~64tlig.y4.js +0 -4
  282. /package/.next/static/{rDAS8dVgx4ldg7AtRGdUB → Bv_r6o_p1UTgHlTvQaZNt}/_buildManifest.js +0 -0
  283. /package/.next/static/{rDAS8dVgx4ldg7AtRGdUB → Bv_r6o_p1UTgHlTvQaZNt}/_clientMiddlewareManifest.js +0 -0
  284. /package/.next/static/{rDAS8dVgx4ldg7AtRGdUB → Bv_r6o_p1UTgHlTvQaZNt}/_ssgManifest.js +0 -0
@@ -0,0 +1,81 @@
1
+ /* Round 418 verification: recent-row content preview gets
2
+ * opacity=0.7 tspan wrapper. Subordinate-text tier at the SVG-
3
+ * text scope — sibling to chip-internal-hierarchy label-tier
4
+ * (opacity-70) at HTML scope.
5
+ *
6
+ * Contract:
7
+ * - Seed a flow with content so recent-signal panel mounts a row
8
+ * - The row text contains a <tspan opacity="0.7"
9
+ * data-recent-row-content-tspan> wrapping the preview text
10
+ * - tspan textContent matches the truncated content
11
+ * - Pre-R418 invariants on the row preserved:
12
+ * * R320 count tspan fw=600 cold / 700 hot
13
+ * * R363 row text fw=500
14
+ * * R220 letter-spacing pin signature
15
+ */
16
+ import { chromium } from 'playwright';
17
+ import { readFileSync } from 'node:fs';
18
+
19
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
20
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
21
+
22
+ const browser = await chromium.launch({ headless: true });
23
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
24
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
25
+ await ctx.addInitScript(() => {
26
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
27
+ });
28
+ await ctx.route('**/api/hub/status*', async (route) => {
29
+ const r = await route.fetch();
30
+ const b = await r.json();
31
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
32
+ const mk = (alias) => ({
33
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
34
+ network_id: nid, project_dir: null,
35
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
36
+ });
37
+ await route.fulfill({ response: r, json: { ...b, sessions: [ mk('alpha'), mk('beta') ] } });
38
+ });
39
+ // Seed a flow with content text so the row preview renders
40
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({
41
+ json: { messages: [
42
+ { id: 'm1', from_alias: 'alpha', to_alias: 'beta', content: 'hi there!', created_at: fresh, network_id: 'default' },
43
+ ] },
44
+ }));
45
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
46
+
47
+ const page = await ctx.newPage();
48
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
49
+ await page.waitForSelector('[data-recent-row-content-tspan]', { timeout: 15000 });
50
+ await page.waitForTimeout(400);
51
+
52
+ const probe = await page.evaluate(() => {
53
+ const tspans = Array.from(document.querySelectorAll('[data-recent-row-content-tspan]'));
54
+ if (tspans.length === 0) return null;
55
+ const sample = tspans[0];
56
+ return {
57
+ count: tspans.length,
58
+ opacityAttr: sample.getAttribute('opacity'),
59
+ textContent: sample.textContent,
60
+ parentTag: sample.parentElement?.tagName,
61
+ };
62
+ });
63
+
64
+ await browser.close();
65
+
66
+ const results = {
67
+ // At least one content tspan mounted
68
+ tspan_mounted: (probe?.count || 0) >= 1,
69
+ // R418 wire: opacity 0.7
70
+ opacity_0_7: probe?.opacityAttr === '0.7',
71
+ // textContent is the truncated preview (R0 truncate appends '...' suffix when
72
+ // input exceeds the 8-char limit, so total can exceed 8 — assert non-empty
73
+ // and matches the seeded content prefix).
74
+ text_truncated: !!probe?.textContent && probe.textContent.startsWith('hi '),
75
+ // Parent is a <text> element (SVG context)
76
+ parent_text: probe?.parentTag === 'text',
77
+ };
78
+ const ok = Object.values(results).every(Boolean);
79
+ console.log(`${ok ? '✅' : '❌'} recent-row content preview tspan opacity=0.7:`, JSON.stringify(results),
80
+ '\n probe:', probe);
81
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,106 @@
1
+ /* Round 445 verification: recent-row count tspan fontWeight lift on
2
+ * isRowPinned. Extends R320 cold/hot fw binary (600/700) to also fire
3
+ * on pin. cold+pinned → 700; cold+un-pinned → 600.
4
+ *
5
+ * Contract:
6
+ * - rest: every recent-row-count reports font-weight '600' +
7
+ * pinned='false'
8
+ * - click a recent-row: that row's count flips to '700' +
9
+ * pinned='true'; siblings stay rest
10
+ */
11
+ import { chromium } from 'playwright';
12
+ import { readFileSync } from 'node:fs';
13
+
14
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
15
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
16
+
17
+ const browser = await chromium.launch({ headless: true });
18
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
19
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
20
+ await ctx.addInitScript(() => {
21
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
22
+ });
23
+ await ctx.route('**/api/hub/status*', async (route) => {
24
+ const r = await route.fetch();
25
+ const b = await r.json();
26
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
27
+ const mk = (alias, status) => ({
28
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
29
+ network_id: nid, project_dir: null,
30
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
31
+ });
32
+ await route.fulfill({ response: r, json: { ...b, sessions: [
33
+ mk('alpha', 'working'),
34
+ mk('beta', 'idle'),
35
+ mk('gamma', 'idle'),
36
+ ] } });
37
+ });
38
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({
39
+ json: { messages: [
40
+ // Use only 1 message each so neither row crosses the hot threshold (≥10)
41
+ { id: 'm1', from_alias: 'alpha', to_alias: 'beta', content: 'ping', created_at: fresh, network_id: 'default' },
42
+ { id: 'm2', from_alias: 'alpha', to_alias: 'gamma', content: 'pong', created_at: fresh, network_id: 'default' },
43
+ ] },
44
+ }));
45
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
46
+
47
+ const page = await ctx.newPage();
48
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
49
+ await page.waitForSelector('[data-recent-row-count]', { timeout: 15000 });
50
+ await page.waitForTimeout(400);
51
+
52
+ const readAll = () => page.evaluate(() => {
53
+ const ts = [...document.querySelectorAll('[data-recent-row-count]')];
54
+ return ts.map(t => {
55
+ const row = t.closest('[data-recent-row]');
56
+ return {
57
+ rowKey: row?.getAttribute('data-recent-row') || null,
58
+ fw: t.getAttribute('font-weight'),
59
+ pinned: t.getAttribute('data-recent-row-count-pinned'),
60
+ hot: t.getAttribute('data-recent-row-count-hot'),
61
+ };
62
+ });
63
+ });
64
+
65
+ const rest = await readAll();
66
+ const firstRow = rest[0]?.rowKey;
67
+
68
+ // Pin the first row via click on its <g data-recent-row>
69
+ let pinned = null;
70
+ if (firstRow) {
71
+ const row = await page.$(`[data-recent-row="${firstRow}"]`);
72
+ if (row) {
73
+ await row.click();
74
+ await page.waitForTimeout(250);
75
+ pinned = await readAll();
76
+ }
77
+ }
78
+
79
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
80
+ const sourceWired = /fontWeight=\{\(isHot \|\| isRowPinned\) \? '700' : '600'\}/.test(fileText);
81
+
82
+ await browser.close();
83
+
84
+ const restAll600 = rest.every(r => r.fw === '600');
85
+ const restNoPin = rest.every(r => r.pinned === 'false');
86
+ const restNoHot = rest.every(r => !r.hot || r.hot !== 'true');
87
+ const pinnedEntry = pinned?.find(r => r.rowKey === firstRow);
88
+ const pinFw_700 = pinnedEntry?.fw === '700';
89
+ const pinFlag = pinnedEntry?.pinned === 'true';
90
+ const othersRest = pinned ? pinned.filter(r => r.rowKey !== firstRow).every(r => r.fw === '600' && r.pinned === 'false') : false;
91
+
92
+ const results = {
93
+ rest_count_ge_2: rest.length >= 2,
94
+ rest_all_fw_600: restAll600,
95
+ rest_no_pin: restNoPin,
96
+ rest_no_hot: restNoHot,
97
+ pinned_target_fw_700: pinFw_700,
98
+ pinned_target_flag: pinFlag,
99
+ pinned_others_stay: othersRest,
100
+ source_wired: sourceWired,
101
+ };
102
+ const ok = Object.values(results).every(Boolean);
103
+ console.log(`${ok ? '✅' : '❌'} recent-row count fw pin:`, JSON.stringify(results),
104
+ '\n rest:', JSON.stringify(rest),
105
+ '\n pinned target:', JSON.stringify(pinnedEntry));
106
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,109 @@
1
+ /* Round 434 verification: recent-signal row text 3-tier letter-spacing —
2
+ * rest 0 / hover 0.25 / pinned 0.5. Mirror of R433 legend-row pattern
3
+ * at recent-signal scope. Hover-letter-spacing family becomes 10 anchors.
4
+ *
5
+ * Contract:
6
+ * - need flow messages to populate recent rows — fixture seeds
7
+ * alpha↔beta + alpha↔gamma so ≥2 rows mount
8
+ * - rest: every recent-row-text reports letter-spacing '0px' +
9
+ * hovered='false' + pinned='false'
10
+ * - hover one data-recent-row wrapper: that row's text reports
11
+ * letter-spacing '0.25px' + hovered='true' (R434 mid tier)
12
+ * - siblings stay rest
13
+ * - source-file probe confirms 3-tier conditional
14
+ */
15
+ import { chromium } from 'playwright';
16
+ import { readFileSync } from 'node:fs';
17
+
18
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
19
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
20
+
21
+ const browser = await chromium.launch({ headless: true });
22
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
23
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
24
+ await ctx.addInitScript(() => {
25
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
26
+ });
27
+ await ctx.route('**/api/hub/status*', async (route) => {
28
+ const r = await route.fetch();
29
+ const b = await r.json();
30
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
31
+ const mk = (alias, status) => ({
32
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
33
+ network_id: nid, project_dir: null,
34
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
35
+ });
36
+ await route.fulfill({ response: r, json: { ...b, sessions: [
37
+ mk('alpha', 'working'),
38
+ mk('beta', 'idle'),
39
+ mk('gamma', 'idle'),
40
+ ] } });
41
+ });
42
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({
43
+ json: { messages: [
44
+ { id: 'm1', from_alias: 'alpha', to_alias: 'beta', content: 'ping1', created_at: fresh, network_id: 'default' },
45
+ { id: 'm2', from_alias: 'alpha', to_alias: 'gamma', content: 'ping2', created_at: fresh, network_id: 'default' },
46
+ ] },
47
+ }));
48
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
49
+
50
+ const page = await ctx.newPage();
51
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
52
+ await page.waitForSelector('[data-recent-row-text]', { timeout: 15000 });
53
+ await page.waitForTimeout(400);
54
+
55
+ const readAll = () => page.evaluate(() => {
56
+ const ts = [...document.querySelectorAll('[data-recent-row-text]')];
57
+ return ts.map(t => ({
58
+ key: t.getAttribute('data-recent-row-text'),
59
+ ls: t.style.letterSpacing,
60
+ pinned: t.getAttribute('data-recent-row-text-pinned'),
61
+ hovered: t.getAttribute('data-recent-row-text-hovered'),
62
+ }));
63
+ });
64
+
65
+ const rest = await readAll();
66
+ const firstKey = rest[0]?.key;
67
+
68
+ // Hover the data-recent-row <g> wrapper for the first row.
69
+ let hover = null;
70
+ if (firstKey) {
71
+ const box = await page.evaluate((key) => {
72
+ const g = document.querySelector(`[data-recent-row="${key}"]`);
73
+ if (!g) return null;
74
+ const b = g.getBoundingClientRect();
75
+ return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
76
+ }, firstKey);
77
+ if (box) {
78
+ await page.mouse.move(box.x, box.y);
79
+ await page.waitForTimeout(300);
80
+ hover = await readAll();
81
+ await page.mouse.move(0, 0);
82
+ }
83
+ }
84
+
85
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
86
+ const sourceWired = /letterSpacing:\s*isRowPinned\s*\?\s*'0\.5px'\s*:\s*\n?\s*isRowHovered\s*\?\s*'0\.25px'\s*:\s*'0px'/.test(fileText);
87
+
88
+ await browser.close();
89
+
90
+ const isRest = (ls) => ls === '0px' || ls === '' || ls === 'normal';
91
+
92
+ const hoveredEntry = hover?.find(r => r.key === firstKey);
93
+ const othersStayRest = hover ? hover.filter(r => r.key !== firstKey).every(r => isRest(r.ls) && r.hovered === 'false') : false;
94
+
95
+ const results = {
96
+ rest_count_gte_2: rest.length >= 2,
97
+ rest_all_zero: rest.every(r => isRest(r.ls)),
98
+ rest_all_pinned_false: rest.every(r => r.pinned === 'false'),
99
+ rest_all_hovered_false: rest.every(r => r.hovered === 'false'),
100
+ hover_target_ls_0_25: hoveredEntry?.ls === '0.25px',
101
+ hover_target_hovered_true: hoveredEntry?.hovered === 'true',
102
+ hover_others_stay_rest: othersStayRest,
103
+ source_three_tier_wired: sourceWired,
104
+ };
105
+ const ok = Object.values(results).every(Boolean);
106
+ console.log(`${ok ? '✅' : '❌'} recent-row 3-tier letter-spacing:`, JSON.stringify(results),
107
+ '\n rest count:', rest.length,
108
+ '\n hover target:', JSON.stringify(hoveredEntry));
109
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,104 @@
1
+ /* Round 447 verification: recent-row freshness pip radius hover lift —
2
+ * r 2.0 → 2.5 on (isRowHovered || isRowPinned).
3
+ *
4
+ * Contract:
5
+ * - rest: every pip reports data-recent-row-freshness-radius '2' +
6
+ * lifted='false'
7
+ * - hover one row: that row's pip lifts to '2.5' + lifted='true'
8
+ * - siblings stay rest
9
+ */
10
+ import { chromium } from 'playwright';
11
+ import { readFileSync } from 'node:fs';
12
+
13
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
14
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
15
+
16
+ const browser = await chromium.launch({ headless: true });
17
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
18
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
19
+ await ctx.addInitScript(() => {
20
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
21
+ });
22
+ await ctx.route('**/api/hub/status*', async (route) => {
23
+ const r = await route.fetch();
24
+ const b = await r.json();
25
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
26
+ const mk = (alias, status) => ({
27
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
28
+ network_id: nid, project_dir: null,
29
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
30
+ });
31
+ await route.fulfill({ response: r, json: { ...b, sessions: [
32
+ mk('alpha', 'working'),
33
+ mk('beta', 'idle'),
34
+ mk('gamma', 'idle'),
35
+ ] } });
36
+ });
37
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({
38
+ json: { messages: [
39
+ { id: 'm1', from_alias: 'alpha', to_alias: 'beta', content: 'ping', created_at: fresh, network_id: 'default' },
40
+ { id: 'm2', from_alias: 'alpha', to_alias: 'gamma', content: 'pong', created_at: fresh, network_id: 'default' },
41
+ ] },
42
+ }));
43
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
44
+
45
+ const page = await ctx.newPage();
46
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
47
+ await page.waitForSelector('[data-recent-row-freshness]', { timeout: 15000 });
48
+ await page.waitForTimeout(400);
49
+
50
+ const readAll = () => page.evaluate(() => {
51
+ const ps = [...document.querySelectorAll('[data-recent-row-freshness]')];
52
+ return ps.map(p => ({
53
+ key: p.getAttribute('data-recent-row-freshness'),
54
+ r: parseFloat(p.getAttribute('data-recent-row-freshness-radius') || '0'),
55
+ lifted: p.getAttribute('data-recent-row-freshness-lifted'),
56
+ }));
57
+ });
58
+
59
+ const rest = await readAll();
60
+ const firstKey = rest[0]?.key;
61
+
62
+ // Hover the corresponding data-recent-row wrapper
63
+ let hover = null;
64
+ if (firstKey) {
65
+ const box = await page.evaluate((key) => {
66
+ const g = document.querySelector(`[data-recent-row="${key}"]`);
67
+ if (!g) return null;
68
+ const b = g.getBoundingClientRect();
69
+ return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
70
+ }, firstKey);
71
+ if (box) {
72
+ await page.mouse.move(box.x, box.y);
73
+ await page.waitForTimeout(300);
74
+ hover = await readAll();
75
+ await page.mouse.move(0, 0);
76
+ }
77
+ }
78
+
79
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
80
+ const sourceWired = /r:\s*`\$\{\(isRowHovered \|\| isRowPinned\) \? 2\.5 : 2\.0\}px`/.test(fileText);
81
+
82
+ await browser.close();
83
+
84
+ const restAll_2_0 = rest.every(r => r.r === 2.0);
85
+ const restNoLifted = rest.every(r => r.lifted === 'false');
86
+ const hoveredEntry = hover?.find(r => r.key === firstKey);
87
+ const hoverR_2_5 = hoveredEntry?.r === 2.5;
88
+ const hoverLifted = hoveredEntry?.lifted === 'true';
89
+ const othersStayRest = hover ? hover.filter(r => r.key !== firstKey).every(r => r.r === 2.0 && r.lifted === 'false') : false;
90
+
91
+ const results = {
92
+ rest_count_ge_2: rest.length >= 2,
93
+ rest_all_r_2_0: restAll_2_0,
94
+ rest_no_lifted: restNoLifted,
95
+ hover_target_r_2_5: hoverR_2_5,
96
+ hover_target_lifted: hoverLifted,
97
+ hover_others_stay: othersStayRest,
98
+ source_wired: sourceWired,
99
+ };
100
+ const ok = Object.values(results).every(Boolean);
101
+ console.log(`${ok ? '✅' : '❌'} recent-row pip hover r:`, JSON.stringify(results),
102
+ '\n rest:', JSON.stringify(rest),
103
+ '\n hover target:', JSON.stringify(hoveredEntry));
104
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,90 @@
1
+ /* Round 363 verification: recent-signal row text fontWeight 400 → 500.
2
+ * At fontSize=9 the default-weight text was thin; R363 lifts to the
3
+ * font-medium tier (500) for better legibility against the panel
4
+ * chrome. R320 count tspan fw 600/700 override stays local, so the
5
+ * count-vs-alias hierarchy holds:
6
+ * alias fw 500 (R363)
7
+ * count fw 600/700 (R320)
8
+ *
9
+ * Contract:
10
+ * - data-recent-row-text element font-weight attr === '500'.
11
+ * - data-recent-row-text-font-weight === '500'.
12
+ * - The inner count tspan still has fw 600 (cold flow, R320 default).
13
+ * - Pre-R363 invariants:
14
+ * * fontSize=9 + fontFamily=monospace
15
+ * * R220 letter-spacing pin tween still wired (style.transition
16
+ * contains letter-spacing)
17
+ * * data-recent-row-text key surfaces the link key
18
+ */
19
+ import { chromium } from 'playwright';
20
+ import { readFileSync } from 'node:fs';
21
+
22
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
23
+ const browser = await chromium.launch({ headless: true });
24
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
25
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
26
+ await ctx.addInitScript(() => {
27
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
28
+ });
29
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
30
+ await ctx.route('**/api/hub/status*', async (route) => {
31
+ const r = await route.fetch();
32
+ const b = await r.json();
33
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
34
+ const mk = (alias) => ({
35
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
36
+ network_id: nid, project_dir: null,
37
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
38
+ });
39
+ await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b') ] } });
40
+ });
41
+ const now = Date.now();
42
+ // 3 messages on the same link → count = 3 (cold, below the 10 hot threshold).
43
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
44
+ { id: 'm1', from_alias: 'a', to_alias: 'b', content: 'ping',
45
+ network_id: 'default', created_at: new Date(now - 5000).toISOString() },
46
+ { id: 'm2', from_alias: 'a', to_alias: 'b', content: 'pong',
47
+ network_id: 'default', created_at: new Date(now - 4000).toISOString() },
48
+ { id: 'm3', from_alias: 'a', to_alias: 'b', content: 'ping2',
49
+ network_id: 'default', created_at: new Date(now - 3000).toISOString() },
50
+ ] } }));
51
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
52
+
53
+ const page = await ctx.newPage();
54
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
55
+ await page.waitForSelector('[data-recent-row-text]', { timeout: 15000 });
56
+ await page.waitForTimeout(300);
57
+
58
+ const probe = await page.evaluate(() => {
59
+ const t = document.querySelector('[data-recent-row-text]');
60
+ const count = document.querySelector('[data-recent-row-count]');
61
+ const cs = t ? getComputedStyle(t) : null;
62
+ return {
63
+ fontWeightAttr: t?.getAttribute('font-weight') ?? null,
64
+ fontWeightData: t?.getAttribute('data-recent-row-text-font-weight') ?? null,
65
+ fontSizeAttr: t?.getAttribute('font-size') ?? null,
66
+ fontFamily: t?.getAttribute('font-family') ?? null,
67
+ linkKey: t?.getAttribute('data-recent-row-text') ?? null,
68
+ transition: cs?.transition ?? null,
69
+ countFwAttr: count?.getAttribute('font-weight') ?? null, // R320 override
70
+ };
71
+ });
72
+
73
+ await browser.close();
74
+
75
+ const hasTrans = (s, prop) =>
76
+ new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
77
+
78
+ const results = {
79
+ font_weight_500: probe.fontWeightAttr === '500',
80
+ data_fw_500: probe.fontWeightData === '500',
81
+ font_size_9: probe.fontSizeAttr === '9',
82
+ font_family_mono: /monospace/i.test(probe.fontFamily || ''),
83
+ link_key_present: (probe.linkKey || '').length > 0,
84
+ trans_has_letterspacing: hasTrans(probe.transition, 'letter-spacing'), // R220
85
+ count_fw_600: probe.countFwAttr === '600', // R320 cold default
86
+ };
87
+ const ok = Object.values(results).every(Boolean);
88
+ console.log(`${ok ? '✅' : '❌'} recent-row text fw 400 → 500:`, JSON.stringify(results),
89
+ '\n probe:', probe);
90
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,96 @@
1
+ /* Round 443 verification: runtime badge inner-icon strokeWidth hover
2
+ * lift — 2.4 → 2.8 on node hover. Sibling to R208 outer ring sw lift.
3
+ *
4
+ * Contract:
5
+ * - rest: every runtime icon path reports stroke-width '2.4' +
6
+ * active='false'
7
+ * - hover one node: that icon path stroke-width '2.8' + active='true'
8
+ * - siblings stay rest
9
+ */
10
+ import { chromium } from 'playwright';
11
+ import { readFileSync } from 'node:fs';
12
+
13
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
14
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
15
+
16
+ const browser = await chromium.launch({ headless: true });
17
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
18
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
19
+ await ctx.addInitScript(() => {
20
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
21
+ });
22
+ await ctx.route('**/api/hub/status*', async (route) => {
23
+ const r = await route.fetch();
24
+ const b = await r.json();
25
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
26
+ const mk = (alias, status, runtime) => ({
27
+ alias, status, model: 'claude-opus-4', runtime,
28
+ network_id: nid, project_dir: null,
29
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
30
+ });
31
+ await route.fulfill({ response: r, json: { ...b, sessions: [
32
+ mk('alpha', 'working', 'claude-code-cli'),
33
+ mk('beta', 'idle', 'claude-code-cli'),
34
+ mk('gamma', 'working', 'claude-code-cli'),
35
+ ] } });
36
+ });
37
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
38
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
39
+
40
+ const page = await ctx.newPage();
41
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
42
+ await page.waitForSelector('[data-runtime-badge-icon]', { timeout: 15000 });
43
+ await page.waitForTimeout(400);
44
+
45
+ const readAll = () => page.evaluate(() => {
46
+ const ps = [...document.querySelectorAll('[data-runtime-badge-icon]')];
47
+ return ps.map(p => ({
48
+ alias: p.getAttribute('data-runtime-badge-icon'),
49
+ sw: p.getAttribute('stroke-width'),
50
+ active: p.getAttribute('data-runtime-badge-icon-active'),
51
+ }));
52
+ });
53
+
54
+ const rest = await readAll();
55
+ let hover = null;
56
+ const box = await page.evaluate(() => {
57
+ const t = document.querySelector('[data-node-alias-text="alpha"]');
58
+ if (!t) return null;
59
+ const node = t.closest('[data-node]');
60
+ const target = node || t;
61
+ const b = target.getBoundingClientRect();
62
+ return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
63
+ });
64
+ if (box) {
65
+ await page.mouse.move(box.x, box.y);
66
+ await page.waitForTimeout(300);
67
+ hover = await readAll();
68
+ await page.mouse.move(0, 0);
69
+ }
70
+
71
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
72
+ const sourceWired = /strokeWidth=\{isNodeActive \? '2\.8' : '2\.4'\}/.test(fileText);
73
+
74
+ await browser.close();
75
+
76
+ const restAll_2_4 = rest.every(r => r.sw === '2.4');
77
+ const restNoActive = rest.every(r => r.active === 'false');
78
+ const hoveredEntry = hover?.find(r => r.alias === 'alpha');
79
+ const hoverSw_2_8 = hoveredEntry?.sw === '2.8';
80
+ const hoverActive = hoveredEntry?.active === 'true';
81
+ const othersRest = hover ? hover.filter(r => r.alias !== 'alpha').every(r => r.sw === '2.4' && r.active === 'false') : false;
82
+
83
+ const results = {
84
+ rest_count_ge_3: rest.length >= 3,
85
+ rest_all_sw_2_4: restAll_2_4,
86
+ rest_all_active_false: restNoActive,
87
+ hover_target_sw_2_8: hoverSw_2_8,
88
+ hover_target_active: hoverActive,
89
+ hover_others_stay: othersRest,
90
+ source_wired: sourceWired,
91
+ };
92
+ const ok = Object.values(results).every(Boolean);
93
+ console.log(`${ok ? '✅' : '❌'} runtime icon hover sw:`, JSON.stringify(results),
94
+ '\n rest:', JSON.stringify(rest),
95
+ '\n hover target:', JSON.stringify(hoveredEntry));
96
+ process.exit(ok ? 0 : 1);