@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,75 @@
1
+ /* Round 379 verification: minimap viewport rect picks up
2
+ * strokeLinejoin='round'. Sibling SVG stroke-softening polish to
3
+ * R378 flow-rail linecap + R288 chrome icon linecap family.
4
+ *
5
+ * The minimap only mounts when view is non-default (zoom !== 1 ||
6
+ * pan), so test triggers zoom-in to mount it.
7
+ *
8
+ * Contract:
9
+ * - [data-topo-minimap-viewport] element stroke-linejoin attr === 'round'.
10
+ * - data-topo-minimap-viewport-linejoin === 'round'.
11
+ * - Pre-R379 invariants:
12
+ * * R287 strokeWidth=1.5 (cold rest)
13
+ * * R287 stroke=pal.legendAccent
14
+ * * R346 opacity=0.9 (cold rest)
15
+ * * R199 smoothView + R346 transition list still tween correctly
16
+ */
17
+ import { chromium } from 'playwright';
18
+ import { readFileSync } from 'node:fs';
19
+
20
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
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
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
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: 'working', 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('a'), mk('b'), mk('c') ] } });
38
+ });
39
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
40
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
41
+
42
+ const page = await ctx.newPage();
43
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
44
+ // Trigger zoom-in to mount minimap (R30 gate).
45
+ await page.waitForSelector('[data-topo-chrome-zoom-in]', { timeout: 15000 });
46
+ await page.click('[data-topo-chrome-zoom-in]');
47
+ await page.waitForTimeout(200);
48
+ await page.click('[data-topo-chrome-zoom-in]');
49
+ await page.waitForSelector('[data-topo-minimap-viewport]', { timeout: 5000 });
50
+ await page.waitForTimeout(400);
51
+
52
+ const probe = await page.evaluate(() => {
53
+ const r = document.querySelector('[data-topo-minimap-viewport]');
54
+ return {
55
+ linejoinAttr: r?.getAttribute('stroke-linejoin') ?? null,
56
+ linejoinData: r?.getAttribute('data-topo-minimap-viewport-linejoin') ?? null,
57
+ strokeWidthAttr: r?.getAttribute('stroke-width') ?? null,
58
+ opacityAttr: r?.getAttribute('opacity') ?? null,
59
+ fillAttr: r?.getAttribute('fill') ?? null,
60
+ };
61
+ });
62
+
63
+ await browser.close();
64
+
65
+ const results = {
66
+ linejoin_round: probe.linejoinAttr === 'round',
67
+ data_linejoin_round: probe.linejoinData === 'round',
68
+ stroke_width_1_5_rest: probe.strokeWidthAttr === '1.5', // R287 cold rest
69
+ opacity_0_9_rest: probe.opacityAttr === '0.9', // R346 cold rest
70
+ fill_none: probe.fillAttr === 'none', // viewport rect is stroke-only
71
+ };
72
+ const ok = Object.values(results).every(Boolean);
73
+ console.log(`${ok ? '✅' : '❌'} minimap viewport linejoin='round':`, JSON.stringify(results),
74
+ '\n probe:', probe);
75
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,96 @@
1
+ /* Round 450 verification: minimap viewport rect rest opacity 0.9 →
2
+ * 0.95. Closes half the alpha gap on the wayfinding indicator.
3
+ *
4
+ * Contract:
5
+ * - rest: viewport rect opacity '0.95'
6
+ * - hover the minimap container: viewport opacity '1' (R346 preserved)
7
+ * - strokeWidth invariants preserved (rest=1.5, hover=1.75)
8
+ */
9
+ import { chromium } from 'playwright';
10
+ import { readFileSync } from 'node:fs';
11
+
12
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
13
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
14
+
15
+ const browser = await chromium.launch({ headless: true });
16
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
17
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
18
+ await ctx.addInitScript(() => {
19
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
20
+ });
21
+ await ctx.route('**/api/hub/status*', async (route) => {
22
+ const r = await route.fetch();
23
+ const b = await r.json();
24
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
25
+ const mk = (alias, status) => ({
26
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
27
+ network_id: nid, project_dir: null,
28
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
29
+ });
30
+ // Need enough nodes for minimap to render — and a non-default view
31
+ await route.fulfill({ response: r, json: { ...b, sessions: [
32
+ mk('alpha', 'working'),
33
+ mk('beta', 'idle'),
34
+ mk('gamma', 'working'),
35
+ mk('delta', 'idle'),
36
+ ] } });
37
+ });
38
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
39
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
40
+
41
+ const page = await ctx.newPage();
42
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
43
+
44
+ // Trigger a zoom to mount the minimap (R30 gate: minimap only renders on non-default view)
45
+ await page.waitForSelector('[data-topo-chrome-zoom-in]', { timeout: 15000 });
46
+ await page.click('[data-topo-chrome-zoom-in]');
47
+ await page.waitForTimeout(300);
48
+
49
+ await page.waitForSelector('[data-topo-minimap-viewport]', { timeout: 5000 });
50
+ await page.waitForTimeout(300);
51
+
52
+ const rest = await page.evaluate(() => {
53
+ const v = document.querySelector('[data-topo-minimap-viewport]');
54
+ return v ? {
55
+ opacity: v.getAttribute('opacity'),
56
+ sw: v.getAttribute('stroke-width'),
57
+ hover: v.getAttribute('data-topo-minimap-viewport-hover'),
58
+ } : null;
59
+ });
60
+
61
+ // Hover the minimap container
62
+ let hover = null;
63
+ const minimap = await page.$('[data-topo-minimap]');
64
+ if (minimap) {
65
+ await minimap.hover({ force: true });
66
+ await page.waitForTimeout(250);
67
+ hover = await page.evaluate(() => {
68
+ const v = document.querySelector('[data-topo-minimap-viewport]');
69
+ return v ? {
70
+ opacity: v.getAttribute('opacity'),
71
+ sw: v.getAttribute('stroke-width'),
72
+ hover: v.getAttribute('data-topo-minimap-viewport-hover'),
73
+ } : null;
74
+ });
75
+ }
76
+
77
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
78
+ const sourceWired = /opacity=\{hoveredMinimap \? '1' : '0\.95'\}/.test(fileText);
79
+
80
+ await browser.close();
81
+
82
+ const results = {
83
+ rest_mounted: !!rest,
84
+ rest_opacity_0_95: rest?.opacity === '0.95',
85
+ rest_sw_1_5: rest?.sw === '1.5',
86
+ rest_hover_false: rest?.hover === 'false',
87
+ hover_opacity_1: hover?.opacity === '1',
88
+ hover_sw_1_75: hover?.sw === '1.75',
89
+ hover_hover_true: hover?.hover === 'true',
90
+ source_wired: sourceWired,
91
+ };
92
+ const ok = Object.values(results).every(Boolean);
93
+ console.log(`${ok ? '✅' : '❌'} minimap viewport rest opacity:`, JSON.stringify(results),
94
+ '\n rest:', JSON.stringify(rest),
95
+ '\n hover:', JSON.stringify(hover));
96
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,85 @@
1
+ /* Round 393 verification: minimap viewport rect rx 0 → 2.
2
+ * Corner-radius cascade family adds a sub-element tier (7th
3
+ * anchor) by softening the cyan-stroked viewport frame inside
4
+ * the R332 rounded minimap container. Small but visible polish
5
+ * — 2-px radius reads as "not sharp" without feeling pillowy on
6
+ * the 30-50px wide viewport rect.
7
+ *
8
+ * Contract:
9
+ * - Minimap only mounts on non-default view → zoom-in twice
10
+ * - viewport rect rx attr === '2'
11
+ * - data-topo-minimap-viewport-rx === '2'
12
+ * - Pre-R393 invariants preserved:
13
+ * * R346 hover-state strokeWidth tween (rest '1.5' / hover '1.75')
14
+ * * R346 opacity tween (rest '0.9' / hover '1')
15
+ * * R379 strokeLinejoin='round'
16
+ * * fill='none' + stroke=pal.legendAccent
17
+ */
18
+ import { chromium } from 'playwright';
19
+ import { readFileSync } from 'node:fs';
20
+
21
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
22
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
23
+
24
+ const browser = await chromium.launch({ headless: true });
25
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
26
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
27
+ await ctx.addInitScript(() => {
28
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
29
+ });
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: 'idle', 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('alpha'), mk('beta'), mk('gamma') ] } });
40
+ });
41
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
42
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
43
+
44
+ const page = await ctx.newPage();
45
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
46
+ await page.waitForSelector('g[data-node]', { timeout: 15000 });
47
+ await page.waitForTimeout(300);
48
+
49
+ await page.click('[data-topo-chrome-zoom-in]');
50
+ await page.click('[data-topo-chrome-zoom-in]');
51
+ await page.waitForSelector('[data-topo-minimap-viewport]', { timeout: 5000 });
52
+ await page.waitForTimeout(400);
53
+
54
+ const probe = await page.evaluate(() => {
55
+ const v = document.querySelector('[data-topo-minimap-viewport]');
56
+ if (!v) return null;
57
+ return {
58
+ rxAttr: v.getAttribute('rx'),
59
+ rxData: v.getAttribute('data-topo-minimap-viewport-rx'),
60
+ strokeWidth: v.getAttribute('stroke-width'),
61
+ opacity: v.getAttribute('opacity'),
62
+ linejoin: v.getAttribute('stroke-linejoin'),
63
+ fill: v.getAttribute('fill'),
64
+ strokePresent: !!v.getAttribute('stroke') && v.getAttribute('stroke') !== 'none',
65
+ };
66
+ });
67
+
68
+ await browser.close();
69
+
70
+ const results = {
71
+ // R393: rx 0 → 2
72
+ rx_attr_2: probe?.rxAttr === '2',
73
+ rx_data_2: probe?.rxData === '2',
74
+ // R346 hover-state rest invariants (no hover yet → rest values)
75
+ rest_strokeWidth_15: probe?.strokeWidth === '1.5',
76
+ rest_opacity_09: probe?.opacity === '0.9',
77
+ // R379 + base invariants
78
+ linejoin_round: probe?.linejoin === 'round',
79
+ fill_none: probe?.fill === 'none',
80
+ stroke_present: probe?.strokePresent === true,
81
+ };
82
+ const ok = Object.values(results).every(Boolean);
83
+ console.log(`${ok ? '✅' : '❌'} minimap viewport rect rx 0 → 2:`, JSON.stringify(results),
84
+ '\n probe:', JSON.stringify(probe, null, 2));
85
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,103 @@
1
+ /* Round 368 verification: recent-panel '+N more flows' footer text
2
+ * fontWeight 400 → 500. Sibling small-text fw lift family with R363
3
+ * recent-row alias + R364 legend-row label + R366 group-label count
4
+ * — all four lifts thicken small-fontSize text (9-11 px) against
5
+ * panel chrome from SVG-default 400 to the font-medium tier (500).
6
+ *
7
+ * The footer only renders when flowLinks.length > 3 (the panel shows
8
+ * the 3 most recent + a footer link to /messages for the overflow).
9
+ *
10
+ * Contract (cyber, 5+ flow links so the footer mounts):
11
+ * - data-recent-panel-more text element font-weight === '500'.
12
+ * - data-recent-panel-more-font-weight === '500'.
13
+ * - Pre-R368 invariants on the same element:
14
+ * * fontSize='9' + fontFamily='monospace' + fontStyle='italic'
15
+ * * letter-spacing='0.2' (R325 rest baseline)
16
+ * * opacity='0.55' (rest, R325 era)
17
+ * * inline transition lists opacity + letter-spacing + fill
18
+ * * data-recent-panel-more attr surfaces the count
19
+ * * data-recent-panel-more-unit tspan with opacity 0.7 (R340)
20
+ */
21
+ import { chromium } from 'playwright';
22
+ import { readFileSync } from 'node:fs';
23
+
24
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
25
+ const browser = await chromium.launch({ headless: true });
26
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
27
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
28
+ await ctx.addInitScript(() => {
29
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
30
+ });
31
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
32
+ await ctx.route('**/api/hub/status*', async (route) => {
33
+ const r = await route.fetch();
34
+ const b = await r.json();
35
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
36
+ const mk = (alias) => ({
37
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
38
+ network_id: nid, project_dir: null,
39
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
40
+ });
41
+ await route.fulfill({ response: r, json: { ...b, sessions: [
42
+ mk('a'), mk('b'), mk('c'), mk('d'), mk('e'),
43
+ ] } });
44
+ });
45
+ // 5 distinct flow links → panel shows 3 + footer "+ 2 more flows".
46
+ const now = Date.now();
47
+ const pairs = [['a','b'], ['c','d'], ['a','c'], ['b','d'], ['e','a']];
48
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages:
49
+ pairs.map(([from, to], idx) => ({
50
+ id: `m${idx}`, from_alias: from, to_alias: to, content: `ping ${idx}`,
51
+ network_id: 'default',
52
+ created_at: new Date(now - (5000 + idx * 1000)).toISOString(),
53
+ }))
54
+ } }));
55
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
56
+
57
+ const page = await ctx.newPage();
58
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
59
+ await page.waitForSelector('[data-recent-panel-more]', { timeout: 15000 });
60
+ await page.waitForTimeout(300);
61
+
62
+ const probe = await page.evaluate(() => {
63
+ const t = document.querySelector('[data-recent-panel-more]');
64
+ const unit = document.querySelector('[data-recent-panel-more-unit]');
65
+ const cs = t ? getComputedStyle(t) : null;
66
+ return {
67
+ fontWeightAttr: t?.getAttribute('font-weight') ?? null,
68
+ fontWeightData: t?.getAttribute('data-recent-panel-more-font-weight') ?? null,
69
+ fontSizeAttr: t?.getAttribute('font-size') ?? null,
70
+ fontFamily: t?.getAttribute('font-family') ?? null,
71
+ fontStyle: t?.getAttribute('font-style') ?? null,
72
+ letterSpacing: t?.getAttribute('letter-spacing') ?? null,
73
+ opacityAttr: t?.getAttribute('opacity') ?? null,
74
+ moreCount: t?.getAttribute('data-recent-panel-more') ?? null,
75
+ hoveredAttr: t?.getAttribute('data-recent-panel-more-hovered') ?? null,
76
+ transition: cs?.transition ?? null,
77
+ unitOpacity: unit?.getAttribute('opacity') ?? null,
78
+ };
79
+ });
80
+
81
+ await browser.close();
82
+
83
+ const hasTrans = (s, prop) =>
84
+ new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
85
+
86
+ const results = {
87
+ font_weight_500: probe.fontWeightAttr === '500',
88
+ data_fw_500: probe.fontWeightData === '500',
89
+ font_size_9: probe.fontSizeAttr === '9',
90
+ font_family_mono: /monospace/i.test(probe.fontFamily || ''),
91
+ font_style_italic: probe.fontStyle === 'italic', // pre-R368
92
+ letter_spacing_0_2: probe.letterSpacing === '0.2', // R325 rest
93
+ opacity_0_55_rest: probe.opacityAttr === '0.55', // R325 era rest
94
+ more_count_2: probe.moreCount === '2', // 5 links - 3 shown
95
+ hovered_false_rest: probe.hoveredAttr === 'false',
96
+ trans_has_letterspacing: hasTrans(probe.transition, 'letter-spacing'),
97
+ trans_has_fill: hasTrans(probe.transition, 'fill'),
98
+ unit_opacity_0_7: probe.unitOpacity === '0.7', // R340 unit split
99
+ };
100
+ const ok = Object.values(results).every(Boolean);
101
+ console.log(`${ok ? '✅' : '❌'} more-flows footer fw 400 → 500:`, JSON.stringify(results),
102
+ '\n probe:', probe);
103
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,112 @@
1
+ /* Round 427 verification: node alias label letter-spacing — 3-tier
2
+ * scale rest 0 / hover 0.3 / chat-target 0.5. Extends the hover-
3
+ * letter-spacing family (R344/R345/R347/R351/R420) to per-node alias
4
+ * scope.
5
+ *
6
+ * Contract:
7
+ * - rest: every visible alias-text reports letter-spacing 0px
8
+ * (resolves to keyword 'normal' in computed style on some engines —
9
+ * R218 trap; accept either form)
10
+ * - hover a node: that node's alias-text reports letter-spacing 0.3px,
11
+ * all others stay at rest 0
12
+ * - source-file probe confirms the 3-tier conditional + transition list
13
+ */
14
+ import { chromium } from 'playwright';
15
+ import { readFileSync } from 'node:fs';
16
+
17
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
18
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
19
+
20
+ const browser = await chromium.launch({ headless: true });
21
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
22
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
23
+ await ctx.addInitScript(() => {
24
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
25
+ });
26
+ await ctx.route('**/api/hub/status*', async (route) => {
27
+ const r = await route.fetch();
28
+ const b = await r.json();
29
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
30
+ const mk = (alias, status) => ({
31
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
32
+ network_id: nid, project_dir: null,
33
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
34
+ });
35
+ // small fleet (3 nodes) so labels are in card-mode, not dense
36
+ await route.fulfill({ response: r, json: { ...b, sessions: [
37
+ mk('alpha', 'working'),
38
+ mk('beta', 'idle'),
39
+ mk('gamma', 'working'),
40
+ ] } });
41
+ });
42
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
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-node-alias-text]', { timeout: 15000 });
48
+ await page.waitForTimeout(400);
49
+
50
+ const readAll = () => page.evaluate(() => {
51
+ const ts = [...document.querySelectorAll('[data-node-alias-text]')];
52
+ return ts.map(t => ({
53
+ alias: t.getAttribute('data-node-alias-text'),
54
+ ls: t.style.letterSpacing, // inline style
55
+ ls_c: getComputedStyle(t).letterSpacing, // computed (may be 'normal')
56
+ chat: t.getAttribute('data-node-alias-chat-target'),
57
+ hover: t.getAttribute('data-node-alias-hovered'),
58
+ }));
59
+ });
60
+
61
+ const rest = await readAll();
62
+
63
+ // Hover the first node's group (its bounding rect)
64
+ const firstAlias = rest[0]?.alias;
65
+ let hover = null;
66
+ if (firstAlias) {
67
+ const box = await page.evaluate((alias) => {
68
+ const t = document.querySelector(`[data-node-alias-text="${alias}"]`);
69
+ if (!t) return null;
70
+ // Walk up to the node group (data-node) for the group-hover hit.
71
+ const node = t.closest('[data-node]');
72
+ const target = node || t;
73
+ const b = target.getBoundingClientRect();
74
+ return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
75
+ }, firstAlias);
76
+ if (box) {
77
+ await page.mouse.move(box.x, box.y);
78
+ await page.waitForTimeout(300);
79
+ hover = await readAll();
80
+ await page.mouse.move(0, 0);
81
+ }
82
+ }
83
+
84
+ // Source-file probe
85
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
86
+ const sourceWired = /chatAlias\s*===\s*session\.alias\s*\?\s*'0\.5px'\s*:\s*\n\s*hoveredAlias\s*===\s*session\.alias\s*\?\s*'0\.3px'\s*:\s*'0px'/.test(fileText);
87
+ const sourceTransition = /transition: 'fill 300ms ease-out, letter-spacing 200ms ease-out'/.test(fileText);
88
+
89
+ await browser.close();
90
+
91
+ const isRest = (ls) => ls === '0px' || ls === '' || ls === 'normal';
92
+ const isMid = (ls) => ls === '0.3px';
93
+
94
+ const restAllZero = rest.length > 0 && rest.every(r => isRest(r.ls));
95
+ const hoveredEntry = hover?.find(r => r.alias === firstAlias);
96
+ const othersStillRest = hover ? hover.filter(r => r.alias !== firstAlias).every(r => isRest(r.ls)) : false;
97
+
98
+ const results = {
99
+ rest_mounted_count_gte_3: rest.length >= 3,
100
+ rest_chat_all_false: rest.every(r => r.chat === 'false'),
101
+ rest_hover_all_false: rest.every(r => r.hover === 'false'),
102
+ rest_letter_spacing_zero: restAllZero,
103
+ hover_target_ls_0_3: isMid(hoveredEntry?.ls || ''),
104
+ hover_target_hover_attr: hoveredEntry?.hover === 'true',
105
+ hover_others_stay_rest: othersStillRest,
106
+ source_three_tier_wired: sourceWired,
107
+ source_transition_kept: sourceTransition,
108
+ };
109
+ const ok = Object.values(results).every(Boolean);
110
+ console.log(`${ok ? '✅' : '❌'} node alias letter-spacing 3-tier:`, JSON.stringify(results),
111
+ '\n rest:', JSON.stringify(rest), '\n hover:', JSON.stringify(hover));
112
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,104 @@
1
+ /* Round 440 verification: node halo opacity hover lift —
2
+ * online cyber 0.65 → 0.80 on hoveredAlias match. Pure paint axis;
3
+ * no geometry change.
4
+ *
5
+ * Contract:
6
+ * - rest: every halo reports data-node-halo-hovered='false' and
7
+ * resolved-opacity matching its tier (online cyber 0.65)
8
+ * - hover one node: that halo flips to hovered='true' + resolved-
9
+ * opacity '0.8'
10
+ * - siblings stay rest
11
+ * - R51 sentinel intact (overlap test still finds nodes)
12
+ * - source-file probe confirms IIFE + conditional wired
13
+ */
14
+ import { chromium } from 'playwright';
15
+ import { readFileSync } from 'node:fs';
16
+
17
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
18
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
19
+
20
+ const browser = await chromium.launch({ headless: true });
21
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
22
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
23
+ await ctx.addInitScript(() => {
24
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
25
+ });
26
+ await ctx.route('**/api/hub/status*', async (route) => {
27
+ const r = await route.fetch();
28
+ const b = await r.json();
29
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
30
+ const mk = (alias, status) => ({
31
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
32
+ network_id: nid, project_dir: null,
33
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
34
+ });
35
+ await route.fulfill({ response: r, json: { ...b, sessions: [
36
+ mk('alpha', 'working'),
37
+ mk('beta', 'idle'),
38
+ mk('gamma', 'working'),
39
+ ] } });
40
+ });
41
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
42
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
43
+
44
+ const page = await ctx.newPage();
45
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
46
+ await page.waitForSelector('[data-node-halo-hovered]', { timeout: 15000 });
47
+ await page.waitForTimeout(400);
48
+
49
+ const readAll = () => page.evaluate(() => {
50
+ const cs = [...document.querySelectorAll('[data-node-halo-hovered]')];
51
+ return cs.map(c => {
52
+ const node = c.closest('[data-node]');
53
+ return {
54
+ alias: node?.getAttribute('data-node') || null,
55
+ hovered: c.getAttribute('data-node-halo-hovered'),
56
+ opacity: parseFloat(c.getAttribute('data-node-halo-resolved-opacity') || '0'),
57
+ };
58
+ });
59
+ });
60
+
61
+ const rest = await readAll();
62
+
63
+ let hover = null;
64
+ const box = await page.evaluate(() => {
65
+ const t = document.querySelector('[data-node-alias-text="alpha"]');
66
+ if (!t) return null;
67
+ const node = t.closest('[data-node]');
68
+ const target = node || t;
69
+ const b = target.getBoundingClientRect();
70
+ return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
71
+ });
72
+ if (box) {
73
+ await page.mouse.move(box.x, box.y);
74
+ await page.waitForTimeout(300);
75
+ hover = await readAll();
76
+ await page.mouse.move(0, 0);
77
+ }
78
+
79
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
80
+ const sourceIIFE = /const isHaloHovered = !reducedMotion && hoveredAlias === session\.alias/.test(fileText);
81
+ const sourceCyberOnline = /isLight \? \(isHaloHovered \? 1\s*:\s*0\.85\) : \(isHaloHovered \? 0\.80 : 0\.65\)/.test(fileText);
82
+
83
+ await browser.close();
84
+
85
+ const restAllRest = rest.length > 0 && rest.every(r => r.hovered === 'false' && r.opacity === 0.65);
86
+ const hoveredEntry = hover?.find(r => r.alias === 'alpha');
87
+ const hoverTarget08 = hoveredEntry?.opacity === 0.80;
88
+ const hoverTargetFlag = hoveredEntry?.hovered === 'true';
89
+ const othersStayRest = hover ? hover.filter(r => r.alias !== 'alpha').every(r => r.hovered === 'false' && r.opacity === 0.65) : false;
90
+
91
+ const results = {
92
+ rest_halo_count_ge_3: rest.length >= 3,
93
+ rest_all_rest_state: restAllRest,
94
+ hover_target_opacity_0_8: hoverTarget08,
95
+ hover_target_attr_true: hoverTargetFlag,
96
+ hover_others_stay_rest: othersStayRest,
97
+ source_IIFE_wired: sourceIIFE,
98
+ source_cyber_online_wired: sourceCyberOnline,
99
+ };
100
+ const ok = Object.values(results).every(Boolean);
101
+ console.log(`${ok ? '✅' : '❌'} node-halo hover opacity:`, 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,80 @@
1
+ /* Round 456 verification: node halo light-theme offline rest opacity
2
+ * 0.45 → 0.50. Stale-state legibility lift family extension.
3
+ *
4
+ * Contract:
5
+ * - light theme, fixture has offline nodes
6
+ * - every offline halo reports data-node-halo-resolved-opacity '0.5'
7
+ * (was 0.45)
8
+ * - online halos still at light 0.85 (R407 + R440 preserved)
9
+ * - source-file probe confirms the conditional
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
+ // Use LIGHT theme for this test
21
+ await ctx.addInitScript(() => {
22
+ try { localStorage.setItem('anet-theme', 'light'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
23
+ });
24
+ await ctx.route('**/api/hub/status*', async (route) => {
25
+ const r = await route.fetch();
26
+ const b = await r.json();
27
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
28
+ const mk = (alias, status) => ({
29
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
30
+ network_id: nid, project_dir: null,
31
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
32
+ });
33
+ await route.fulfill({ response: r, json: { ...b, sessions: [
34
+ mk('alpha', 'working'), // online
35
+ mk('beta', 'offline'), // offline
36
+ mk('gamma', 'offline'), // offline
37
+ ] } });
38
+ });
39
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
40
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
41
+
42
+ const page = await ctx.newPage();
43
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
44
+ await page.waitForSelector('[data-node-halo-hovered]', { timeout: 15000 });
45
+ await page.waitForTimeout(400);
46
+
47
+ const halos = await page.evaluate(() => {
48
+ const cs = [...document.querySelectorAll('[data-node-halo-hovered]')];
49
+ return cs.map(c => {
50
+ const node = c.closest('[data-node]');
51
+ const sub = node?.querySelector('[data-node-sub-text]')?.textContent || '';
52
+ return {
53
+ alias: node?.getAttribute('data-node') || null,
54
+ status: sub.trim(),
55
+ opacity: parseFloat(c.getAttribute('data-node-halo-resolved-opacity') || '0'),
56
+ offline_attr_alpha: c.getAttribute('data-node-halo-offline-opacity'),
57
+ };
58
+ });
59
+ });
60
+
61
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
62
+ const sourceWired = /isLight \? \(isHaloHovered \? 0\.60 : 0\.50\) : \(isHaloHovered \? 0\.45 : 0\.30\)/.test(fileText);
63
+
64
+ await browser.close();
65
+
66
+ const onlineHalo = halos.find(h => h.status.includes('working'));
67
+ const offlineHalos = halos.filter(h => h.status.includes('offline'));
68
+
69
+ const results = {
70
+ any_mounted: halos.length > 0,
71
+ online_opacity_0_85: onlineHalo?.opacity === 0.85,
72
+ offline_count_ge_2: offlineHalos.length >= 2,
73
+ offline_all_0_5: offlineHalos.every(h => h.opacity === 0.5),
74
+ offline_attr_0_45_preserved: offlineHalos.every(h => h.offline_attr_alpha === '0.45'), /* attr exposes the LEGACY R407 light value 0.45; the resolved-opacity is the live 0.50; this preserves R407 test compat */
75
+ source_wired: sourceWired,
76
+ };
77
+ const ok = Object.values(results).every(Boolean);
78
+ console.log(`${ok ? '✅' : '❌'} node halo light offline 0.5:`, JSON.stringify(results),
79
+ '\n halos:', JSON.stringify(halos));
80
+ process.exit(ok ? 0 : 1);