@sleep2agi/agent-network-dashboard 0.5.1-preview.13 → 0.5.1-preview.131

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 (276) 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 +7 -7
  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 +2 -2
  16. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  17. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  18. package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  19. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  20. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  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 +2 -2
  25. package/.next/server/app/admin.segments/_full.segment.rsc +2 -2
  26. package/.next/server/app/admin.segments/_head.segment.rsc +1 -1
  27. package/.next/server/app/admin.segments/_index.segment.rsc +2 -2
  28. package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
  29. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +1 -1
  30. package/.next/server/app/admin.segments/admin.segment.rsc +1 -1
  31. package/.next/server/app/index.html +2 -2
  32. package/.next/server/app/index.rsc +3 -3
  33. package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  34. package/.next/server/app/index.segments/_full.segment.rsc +3 -3
  35. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  36. package/.next/server/app/index.segments/_index.segment.rsc +2 -2
  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 +3 -3
  41. package/.next/server/app/login.segments/_full.segment.rsc +3 -3
  42. package/.next/server/app/login.segments/_head.segment.rsc +1 -1
  43. package/.next/server/app/login.segments/_index.segment.rsc +2 -2
  44. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  45. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
  46. package/.next/server/app/login.segments/login.segment.rsc +1 -1
  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 +2 -2
  50. package/.next/server/app/logs.segments/_full.segment.rsc +2 -2
  51. package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
  52. package/.next/server/app/logs.segments/_index.segment.rsc +2 -2
  53. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  54. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +1 -1
  55. package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
  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 +2 -2
  59. package/.next/server/app/messages.segments/_full.segment.rsc +2 -2
  60. package/.next/server/app/messages.segments/_head.segment.rsc +1 -1
  61. package/.next/server/app/messages.segments/_index.segment.rsc +2 -2
  62. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  63. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +1 -1
  64. package/.next/server/app/messages.segments/messages.segment.rsc +1 -1
  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 +2 -2
  68. package/.next/server/app/node.segments/_full.segment.rsc +2 -2
  69. package/.next/server/app/node.segments/_head.segment.rsc +1 -1
  70. package/.next/server/app/node.segments/_index.segment.rsc +2 -2
  71. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  72. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +1 -1
  73. package/.next/server/app/node.segments/node.segment.rsc +1 -1
  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 +2 -2
  77. package/.next/server/app/nodes.segments/_full.segment.rsc +2 -2
  78. package/.next/server/app/nodes.segments/_head.segment.rsc +1 -1
  79. package/.next/server/app/nodes.segments/_index.segment.rsc +2 -2
  80. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  81. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +1 -1
  82. package/.next/server/app/nodes.segments/nodes.segment.rsc +1 -1
  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 +2 -2
  88. package/.next/server/app/server-logs.segments/_full.segment.rsc +2 -2
  89. package/.next/server/app/server-logs.segments/_head.segment.rsc +1 -1
  90. package/.next/server/app/server-logs.segments/_index.segment.rsc +2 -2
  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 +1 -1
  93. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +1 -1
  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 +2 -2
  97. package/.next/server/app/settings/networks.segments/_full.segment.rsc +2 -2
  98. package/.next/server/app/settings/networks.segments/_head.segment.rsc +1 -1
  99. package/.next/server/app/settings/networks.segments/_index.segment.rsc +2 -2
  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 +1 -1
  102. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +1 -1
  103. package/.next/server/app/settings/networks.segments/settings.segment.rsc +1 -1
  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 +2 -2
  108. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +2 -2
  109. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +1 -1
  110. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +2 -2
  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 +1 -1
  113. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +1 -1
  114. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +1 -1
  115. package/.next/server/app/settings.html +2 -2
  116. package/.next/server/app/settings.rsc +3 -3
  117. package/.next/server/app/settings.segments/_full.segment.rsc +3 -3
  118. package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  119. package/.next/server/app/settings.segments/_index.segment.rsc +2 -2
  120. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  121. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
  122. package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
  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 +2 -2
  127. package/.next/server/app/tasks.segments/_full.segment.rsc +2 -2
  128. package/.next/server/app/tasks.segments/_head.segment.rsc +1 -1
  129. package/.next/server/app/tasks.segments/_index.segment.rsc +2 -2
  130. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  131. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +1 -1
  132. package/.next/server/app/tasks.segments/tasks.segment.rsc +1 -1
  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/middleware-build-manifest.js +3 -3
  142. package/.next/server/pages/404.html +2 -2
  143. package/.next/server/pages/500.html +1 -1
  144. package/.next/server/server-reference-manifest.js +1 -1
  145. package/.next/server/server-reference-manifest.json +1 -1
  146. package/.next/static/chunks/{03~~oirxz7~vc.js → 09dd~tmai-uvf.js} +1 -1
  147. package/.next/static/chunks/0a4hmfvj-81x5.css +2 -0
  148. package/.next/static/chunks/0d3qvaags566q.js +1 -0
  149. package/.next/static/chunks/0y6xhu~ffpycg.js +1 -0
  150. package/.next/static/chunks/11xegdodzgys0.js +4 -0
  151. package/.next/trace +2 -2
  152. package/.next/trace-build +1 -1
  153. package/app/components/TopoGraph.tsx +2528 -186
  154. package/package.json +1 -1
  155. package/screenshots/v0.11.0-147/after.png +0 -0
  156. package/scripts/p0-147-screenshot.mjs +53 -0
  157. package/scripts/topo-active-links-chip-hover-lift-test.mjs +93 -0
  158. package/scripts/topo-chip-digit-fontweight-test.mjs +105 -0
  159. package/scripts/topo-chip-digit-hover-bold-test.mjs +94 -0
  160. package/scripts/topo-chip-row-group-hover-brighten-test.mjs +107 -0
  161. package/scripts/topo-chip-row-hover-lift-test.mjs +95 -0
  162. package/scripts/topo-chrome-button-hover-lift-test.mjs +94 -0
  163. package/scripts/topo-chrome-fullscreen-icon-sw-test.mjs +92 -0
  164. package/scripts/topo-chrome-reset-icon-sw-test.mjs +80 -0
  165. package/scripts/topo-chrome-segmented-radius-test.mjs +100 -0
  166. package/scripts/topo-chrome-zoom-icon-sw-test.mjs +90 -0
  167. package/scripts/topo-click-ripple-opacity-test.mjs +99 -0
  168. package/scripts/topo-dashboard-version-attr-test.mjs +69 -0
  169. package/scripts/topo-dense-alias-opacity-test.mjs +68 -0
  170. package/scripts/topo-edge-badge-digit-fw-test.mjs +103 -0
  171. package/scripts/topo-edge-badge-fontsize-test.mjs +90 -0
  172. package/scripts/topo-edge-badge-hover-opacity-test.mjs +94 -0
  173. package/scripts/topo-edge-badge-hover-stroke-test.mjs +92 -0
  174. package/scripts/topo-edge-badge-letter-spacing-test.mjs +86 -0
  175. package/scripts/topo-edge-badge-opacity-test.mjs +80 -0
  176. package/scripts/topo-edge-badge-pin-opacity-test.mjs +86 -0
  177. package/scripts/topo-edge-badge-stroke-test.mjs +92 -0
  178. package/scripts/topo-edge-endpoint-hover-sw-test.mjs +127 -0
  179. package/scripts/topo-edge-freshness-floor-test.mjs +99 -0
  180. package/scripts/topo-edge-particle-hover-r-test.mjs +113 -0
  181. package/scripts/topo-edge-particle-radius-test.mjs +76 -0
  182. package/scripts/topo-edge-visible-linecap-test.mjs +89 -0
  183. package/scripts/topo-endpoint-ring-r-hover-test.mjs +89 -0
  184. package/scripts/topo-filter-pill-hover-lift-test.mjs +101 -0
  185. package/scripts/topo-filter-pill-hover-opacity-test.mjs +110 -0
  186. package/scripts/topo-filter-pill-value-fw-test.mjs +88 -0
  187. package/scripts/topo-filter-pill-x-hover-scale-test.mjs +99 -0
  188. package/scripts/topo-flow-rail-hover-sw-test.mjs +113 -0
  189. package/scripts/topo-flow-rail-linecap-test.mjs +79 -0
  190. package/scripts/topo-freshness-chip-hierarchy-test.mjs +93 -0
  191. package/scripts/topo-freshness-chip-tabular-test.mjs +41 -0
  192. package/scripts/topo-freshness-floor-lift-test.mjs +92 -0
  193. package/scripts/topo-freshness-suffix-tabular-test.mjs +88 -0
  194. package/scripts/topo-fullscreen-icon-hover-scale-test.mjs +91 -0
  195. package/scripts/topo-group-box-geom-transition-test.mjs +110 -0
  196. package/scripts/topo-group-box-stroke-test.mjs +105 -0
  197. package/scripts/topo-group-label-count-fontweight-test.mjs +108 -0
  198. package/scripts/topo-group-label-count-fw-test.mjs +100 -0
  199. package/scripts/topo-group-label-fw-pin-test.mjs +99 -0
  200. package/scripts/topo-group-label-letter-spacing-test.mjs +113 -0
  201. package/scripts/topo-group-label-tint-geom-test.mjs +94 -0
  202. package/scripts/topo-group-label-tint-transition-test.mjs +97 -0
  203. package/scripts/topo-group-pip-fontsize-test.mjs +106 -0
  204. package/scripts/topo-hover-detail-body-fw-test.mjs +101 -0
  205. package/scripts/topo-hover-detail-model-fw-test.mjs +98 -0
  206. package/scripts/topo-hover-detail-opacity-test.mjs +98 -0
  207. package/scripts/topo-hover-detail-rx-test.mjs +81 -0
  208. package/scripts/topo-hub-core-fill-hover-test.mjs +85 -0
  209. package/scripts/topo-hub-digit-fontsize-test.mjs +86 -0
  210. package/scripts/topo-hub-digit-fw-hover-test.mjs +102 -0
  211. package/scripts/topo-hub-halo-light-trough-test.mjs +88 -0
  212. package/scripts/topo-hub-halo-r-hover-test.mjs +82 -0
  213. package/scripts/topo-hub-halo-radius-test.mjs +86 -0
  214. package/scripts/topo-hub-halo-trough-test.mjs +83 -0
  215. package/scripts/topo-hub-highlight-opacity-test.mjs +88 -0
  216. package/scripts/topo-hub-highlight-radius-test.mjs +90 -0
  217. package/scripts/topo-hub-hover-ring-opacity-test.mjs +96 -0
  218. package/scripts/topo-hub-hover-ring-stroke-test.mjs +86 -0
  219. package/scripts/topo-hub-spoke-hover-opacity-test.mjs +119 -0
  220. package/scripts/topo-hub-spoke-hover-sw-test.mjs +121 -0
  221. package/scripts/topo-hub-spoke-linecap-test.mjs +80 -0
  222. package/scripts/topo-label-card-opacity-hover-test.mjs +99 -0
  223. package/scripts/topo-layout-toggle-radius-test.mjs +87 -0
  224. package/scripts/topo-legend-count-active-opacity-test.mjs +102 -0
  225. package/scripts/topo-legend-count-pin-fw-test.mjs +90 -0
  226. package/scripts/topo-legend-label-fontweight-test.mjs +94 -0
  227. package/scripts/topo-legend-pin-ring-stroke-test.mjs +101 -0
  228. package/scripts/topo-legend-row-letter-spacing-test.mjs +101 -0
  229. package/scripts/topo-minimap-offline-opacity-test.mjs +90 -0
  230. package/scripts/topo-minimap-online-hover-opacity-test.mjs +92 -0
  231. package/scripts/topo-minimap-online-opacity-test.mjs +93 -0
  232. package/scripts/topo-minimap-online-radius-test.mjs +85 -0
  233. package/scripts/topo-minimap-viewport-linejoin-test.mjs +75 -0
  234. package/scripts/topo-minimap-viewport-opacity-test.mjs +96 -0
  235. package/scripts/topo-minimap-viewport-rx-test.mjs +85 -0
  236. package/scripts/topo-more-flows-fontweight-test.mjs +103 -0
  237. package/scripts/topo-node-alias-letter-spacing-test.mjs +112 -0
  238. package/scripts/topo-node-halo-hover-opacity-test.mjs +104 -0
  239. package/scripts/topo-node-halo-light-offline-test.mjs +80 -0
  240. package/scripts/topo-node-halo-offline-opacity-test.mjs +87 -0
  241. package/scripts/topo-node-label-card-rx-test.mjs +85 -0
  242. package/scripts/topo-node-pulse-peak-test.mjs +89 -0
  243. package/scripts/topo-node-pulse-trough-test.mjs +91 -0
  244. package/scripts/topo-node-sub-text-fw-test.mjs +75 -0
  245. package/scripts/topo-node-sub-text-letter-spacing-test.mjs +115 -0
  246. package/scripts/topo-overlap-stale-build-guard-test.mjs +66 -0
  247. package/scripts/topo-overlap-test.mjs +42 -1
  248. package/scripts/topo-panel-count-fw-hover-test.mjs +105 -0
  249. package/scripts/topo-panel-stroke-hover-test.mjs +110 -0
  250. package/scripts/topo-pressure-bar-height-test.mjs +92 -0
  251. package/scripts/topo-pressure-kicker-fontweight-test.mjs +76 -0
  252. package/scripts/topo-recent-pip-radius-2-test.mjs +72 -0
  253. package/scripts/topo-recent-pip-radius-test.mjs +76 -0
  254. package/scripts/topo-recent-row-content-opacity-test.mjs +81 -0
  255. package/scripts/topo-recent-row-count-pin-fw-test.mjs +106 -0
  256. package/scripts/topo-recent-row-letter-spacing-test.mjs +109 -0
  257. package/scripts/topo-recent-row-pip-hover-r-test.mjs +104 -0
  258. package/scripts/topo-recent-row-text-fontweight-test.mjs +90 -0
  259. package/scripts/topo-runtime-icon-hover-test.mjs +96 -0
  260. package/scripts/topo-spoke-active-opacity-test.mjs +104 -0
  261. package/scripts/topo-spoke-active-stroke-test.mjs +95 -0
  262. package/scripts/topo-spoke-idle-opacity-test.mjs +91 -0
  263. package/scripts/topo-status-ring-hover-sw-test.mjs +105 -0
  264. package/scripts/topo-vendor-chip-hover-lift-test.mjs +87 -0
  265. package/scripts/topo-vendor-glyph-fontweight-test.mjs +102 -0
  266. package/scripts/topo-vendor-letter-hover-scale-test.mjs +129 -0
  267. package/scripts/topo-vendor-suffix-hover-brighten-test.mjs +87 -0
  268. package/scripts/topo-zoom-level-hover-fw-test.mjs +95 -0
  269. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +0 -1
  270. package/.next/static/chunks/0is7s1ik8dy24.js +0 -4
  271. package/.next/static/chunks/0kt7qs66ra2.2.css +0 -2
  272. package/.next/static/chunks/0yl__lz0pv5_7.js +0 -1
  273. package/.next/static/chunks/1090s4vyw-jrc.js +0 -1
  274. /package/.next/static/{Fa8EZgmgYGwvcoaGKpSmo → aNROJZBaF0IgNbCcEynAI}/_buildManifest.js +0 -0
  275. /package/.next/static/{Fa8EZgmgYGwvcoaGKpSmo → aNROJZBaF0IgNbCcEynAI}/_clientMiddlewareManifest.js +0 -0
  276. /package/.next/static/{Fa8EZgmgYGwvcoaGKpSmo → aNROJZBaF0IgNbCcEynAI}/_ssgManifest.js +0 -0
@@ -0,0 +1,92 @@
1
+ /* Round 367 verification: edge midpoint badge rest stroke-width
2
+ * 1 → 1.25. Sibling visual-weight bump (7th canvas anchor in the
3
+ * R287/R295/R359/R360/R361/R365/R367 family).
4
+ *
5
+ * Cold edge badges gain ~25 % stroke presence. Stays clear of the
6
+ * R51 overlap-test sentinel values (1.5 / 3 reserved for node
7
+ * strokes — the test selector is gated to g[data-node] ancestors
8
+ * so this edge-internal circle is invisible to that probe anyway).
9
+ * R188 transition list 'stroke-width 300ms ease-out' still smoothes
10
+ * the hot/pin flip — now 1.25 → 2 instead of 1 → 2.
11
+ *
12
+ * Contract (cyber, fixture with cold flow):
13
+ * - Edge badge circle stroke-width attr === '1.25'.
14
+ * - data-edge-badge-stroke-width-rest === '1.25'.
15
+ * - data-edge-badge-lifted === 'false' (rest, not hovered/pinned).
16
+ * - data-edge-badge-text-pin === 'false' (cold flow, link.count<10).
17
+ * - Inline transition contains 'stroke-width' (R188 preserved).
18
+ * - Pre-R367 invariants: r=9 (cold, R164), fill=pal.legendBox.fill,
19
+ * stroke=pal.flowEdge (cold), opacity 0.82 (cyber).
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: [ mk('a'), mk('b') ] } });
42
+ });
43
+ // 1 message → 1 cold flow (count=1, well below R129 isHot=10).
44
+ const now = Date.now();
45
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
46
+ { id: 'm1', from_alias: 'a', to_alias: 'b', content: 'ping',
47
+ network_id: 'default', created_at: new Date(now - 5000).toISOString() },
48
+ ] } }));
49
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
50
+
51
+ const page = await ctx.newPage();
52
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
53
+ await page.waitForSelector('[data-edge-badge-stroke-width-rest]', { timeout: 15000 });
54
+ await page.waitForTimeout(300);
55
+
56
+ const probe = await page.evaluate(() => {
57
+ // Probe the cold/rest edge badge circle (the parent of data-edge-badge-text
58
+ // is the <g> wrapping circle + text; we want the circle sibling).
59
+ const circle = document.querySelector('[data-edge-badge-stroke-width-rest]');
60
+ const cs = circle ? getComputedStyle(circle) : null;
61
+ // Find the text in the same group to check pin state.
62
+ const grp = circle?.parentElement;
63
+ const text = grp?.querySelector('[data-edge-badge-text]');
64
+ return {
65
+ strokeWidthAttr: circle?.getAttribute('stroke-width') ?? null,
66
+ strokeWidthData: circle?.getAttribute('data-edge-badge-stroke-width-rest') ?? null,
67
+ liftedAttr: circle?.getAttribute('data-edge-badge-lifted') ?? null,
68
+ rAttr: circle?.getAttribute('r') ?? null,
69
+ opacityAttr: circle?.getAttribute('opacity') ?? null,
70
+ transition: cs?.transition ?? null,
71
+ pinAttr: text?.getAttribute('data-edge-badge-text-pin') ?? null,
72
+ };
73
+ });
74
+
75
+ await browser.close();
76
+
77
+ const hasTrans = (s, prop) =>
78
+ new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
79
+
80
+ const results = {
81
+ stroke_width_1_25: probe.strokeWidthAttr === '1.25',
82
+ data_stroke_1_25: probe.strokeWidthData === '1.25',
83
+ lifted_false_rest: probe.liftedAttr === 'false',
84
+ r_9_cold: probe.rAttr === '9', // R164 cold
85
+ opacity_0_82_cyber: probe.opacityAttr === '0.82', // R251 cyber theme
86
+ trans_has_strokew: hasTrans(probe.transition, 'stroke-width'), // R188
87
+ cold_flow_not_hot: probe.pinAttr === 'false', // R220 isHot/isPinned both false
88
+ };
89
+ const ok = Object.values(results).every(Boolean);
90
+ console.log(`${ok ? '✅' : '❌'} edge badge stroke 1 → 1.25:`, JSON.stringify(results),
91
+ '\n probe:', probe);
92
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,127 @@
1
+ /* Round 436 verification: edge visible-path stroke-width lift on
2
+ * hoveredAlias endpoint. Mirrors R430/R435 hub-spoke pattern at edge
3
+ * scope — when hoveredAlias matches one of the edge's endpoints, the
4
+ * visible path thickens by 1.15× alongside the existing edgeOpacityMul
5
+ * 1.7× lift (now BOTH paint and geometry lift on endpoint hover).
6
+ *
7
+ * Contract:
8
+ * - rest: no edge reports data-edge-visible-endpoint-hovered='true'
9
+ * - hover one node alias: exactly the edges incident on that alias
10
+ * report endpoint-hovered='true' AND their renderWidth > rest base
11
+ * - siblings unaffected
12
+ * - source-file probe confirms the new conditional + endpoint cap
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', 'idle'),
39
+ ] } });
40
+ });
41
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({
42
+ json: { messages: [
43
+ // alpha is incident on both edges; gamma↔beta is the non-incident edge
44
+ { id: 'm1', from_alias: 'alpha', to_alias: 'beta', content: 'ping', created_at: fresh, network_id: 'default' },
45
+ { id: 'm2', from_alias: 'alpha', to_alias: 'gamma', content: 'pong', created_at: fresh, network_id: 'default' },
46
+ { id: 'm3', from_alias: 'gamma', to_alias: 'beta', content: 'pang', created_at: fresh, network_id: 'default' },
47
+ ] },
48
+ }));
49
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
50
+
51
+ const page = await ctx.newPage();
52
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
53
+ await page.waitForSelector('[data-edge-visible]', { timeout: 15000 });
54
+ await page.waitForTimeout(400);
55
+
56
+ const readAll = () => page.evaluate(() => {
57
+ const ps = [...document.querySelectorAll('[data-edge-visible]')];
58
+ return ps.map(p => ({
59
+ key: p.getAttribute('data-edge-visible'),
60
+ endHover: p.getAttribute('data-edge-visible-endpoint-hovered'),
61
+ sw: parseFloat(p.getAttribute('data-edge-visible-stroke-width') || '0'),
62
+ }));
63
+ });
64
+
65
+ const rest = await readAll();
66
+
67
+ // Hover alpha
68
+ let hover = null;
69
+ const box = await page.evaluate(() => {
70
+ const t = document.querySelector('[data-node-alias-text="alpha"]');
71
+ if (!t) return null;
72
+ const node = t.closest('[data-node]');
73
+ const target = node || t;
74
+ const b = target.getBoundingClientRect();
75
+ return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
76
+ });
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
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
85
+ const sourceWired = /isEndpointHoveredEdge\s*\?\s*Math\.min\(width \* 1\.15,\s*8\)\s*\n?\s*:\s*width/.test(fileText);
86
+ const sourceIsEndpoint = /const isEndpointHoveredEdge = !!hoveredAlias && \(link\.from === hoveredAlias \|\| link\.to === hoveredAlias\)/.test(fileText);
87
+
88
+ await browser.close();
89
+
90
+ // rest: no edge should report endpoint-hover=true
91
+ const restNoneEndHover = rest.every(r => r.endHover === 'false');
92
+
93
+ // hover state: edges where alpha is an endpoint should report endpoint=true.
94
+ // fixture edges: alpha↔beta, alpha↔gamma, gamma↔beta (deduped keys).
95
+ const hoverByKey = new Map((hover || []).map(h => [h.key, h]));
96
+ const restByKey = new Map((rest || []).map(r => [r.key, r]));
97
+
98
+ // flow link keys are e.g. "alpha->beta" or "alpha-beta" — we don't know the
99
+ // exact format. Detect alpha-incident edges by key substring.
100
+ const isAlphaEdge = (key) => key && /alpha/i.test(key);
101
+ const alphaEdges = [...hoverByKey.entries()].filter(([k]) => isAlphaEdge(k));
102
+ const nonAlphaEdges = [...hoverByKey.entries()].filter(([k]) => !isAlphaEdge(k));
103
+
104
+ const alphaAllEndHover = alphaEdges.length > 0 && alphaEdges.every(([, h]) => h.endHover === 'true');
105
+ const nonAlphaNoEndHover = nonAlphaEdges.every(([, h]) => h.endHover === 'false');
106
+
107
+ // width lifted for alpha edges (compare to rest sw for same key)
108
+ const alphaSwLifted = alphaEdges.every(([k, h]) => {
109
+ const r = restByKey.get(k);
110
+ return r && h.sw > r.sw;
111
+ });
112
+
113
+ const results = {
114
+ rest_edge_count_ge_2: rest.length >= 2,
115
+ rest_no_endpoint_hover: restNoneEndHover,
116
+ hover_alpha_edges_present: alphaEdges.length >= 1,
117
+ hover_alpha_all_endHover: alphaAllEndHover,
118
+ hover_alpha_sw_lifted: alphaSwLifted,
119
+ hover_non_alpha_unaffected: nonAlphaNoEndHover,
120
+ source_renderWidth_wired: sourceWired,
121
+ source_isEndpoint_wired: sourceIsEndpoint,
122
+ };
123
+ const ok = Object.values(results).every(Boolean);
124
+ console.log(`${ok ? '✅' : '❌'} edge endpoint-hover sw lift:`, JSON.stringify(results),
125
+ '\n rest:', JSON.stringify(rest),
126
+ '\n hover:', JSON.stringify(hover));
127
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,99 @@
1
+ /* Round 406 verification: edge freshness fade floor 0.35 → 0.40.
2
+ * Stale-state legibility lift family (6th anchor) — stale edges
3
+ * (>5min) now sit at 40% intensity instead of 35%. The decay
4
+ * curve shape (linear 1 - ageMs/300s) unchanged.
5
+ *
6
+ * Contract:
7
+ * - Source-file verification of `Math.max(0.40, 1 - ageMs / (5 * 60 * 1000))`
8
+ * - DOM probe: seed a STALE edge (ageMs > 5min) so fresh hits the
9
+ * floor; assert the visible flow path's opacity reflects the
10
+ * lifted floor (computed as 0.40 × edge_opacity_mul × base).
11
+ * - Compare against pre-R406 expectation (0.35 floor): the lifted
12
+ * floor produces ~14.3 % higher edge opacity on stale edges.
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
+ // 10 minutes ago — well past the 5-min decay window; freshness hits floor.
20
+ const stale = new Date(Date.now() - 10 * 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'), mk('gamma') ] } });
38
+ });
39
+ // Seed a stale flow link 10 minutes ago — past the 5-min decay so floor binds.
40
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({
41
+ json: { messages: [
42
+ { id: 'm1', from_alias: 'alpha', to_alias: 'beta', content: 'old', created_at: stale, 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-edge-visible]', { timeout: 15000 });
50
+ await page.waitForTimeout(600);
51
+
52
+ const probe = await page.evaluate(() => {
53
+ const edge = document.querySelector('[data-edge-visible]');
54
+ if (!edge) return null;
55
+ return {
56
+ opacityAttr: edge.getAttribute('opacity'),
57
+ edgeKey: edge.getAttribute('data-edge-visible'),
58
+ };
59
+ });
60
+
61
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
62
+ const sourceHasFloor040 = /Math\.max\(0\.40, 1 - ageMs/.test(fileText);
63
+ const sourceLacksFloor035 = !/Math\.max\(0\.35, 1 - ageMs/.test(fileText);
64
+
65
+ await browser.close();
66
+
67
+ // Pre-R406 expected: (0.28 cyber base) × 0.35 floor × 1 mul = 0.098
68
+ // Post-R406 expected: (0.28 cyber base) × 0.40 floor × 1 mul = 0.112
69
+ // On the visible flow path the wire is:
70
+ // opacity = Math.min(1, (isLight ? 0.22 : 0.28) * fresh * edgeOpacityMul)
71
+ // For an unhovered edge in a fresh sessions context (no other hover/active):
72
+ // edgeOpacityMul = !hoveredAlias && !activeGroup ? (hoveredActiveLinks ? 1.5 : 1) : 0.35
73
+ // = 1 in this probe → opacity ≈ 0.112 post-R406
74
+ // Browser may serialize this as "0.112" or "0.11" — accept either with
75
+ // a tolerance probe.
76
+ const opacityNum = probe ? parseFloat(probe.opacityAttr || '0') : 0;
77
+ const expectedPost = 0.28 * 0.40 * 1; // 0.112
78
+ const expectedPre = 0.28 * 0.35 * 1; // 0.098
79
+ const distToPost = Math.abs(opacityNum - expectedPost);
80
+ const distToPre = Math.abs(opacityNum - expectedPre);
81
+
82
+ const results = {
83
+ // Edge mounted from seeded stale flow
84
+ edge_mounted: !!probe,
85
+ edge_key_present: !!probe?.edgeKey,
86
+ // Post-R406 opacity is CLOSER to 0.112 than 0.098
87
+ opacity_close_to_post_r406: distToPost < 0.01,
88
+ // Opacity is FURTHER from the pre-R406 0.098 baseline
89
+ opacity_not_pre_r406: distToPre > distToPost,
90
+ // Source-file canonical wire
91
+ source_floor_040: sourceHasFloor040,
92
+ source_no_floor_035: sourceLacksFloor035,
93
+ };
94
+ const ok = Object.values(results).every(Boolean);
95
+ console.log(`${ok ? '✅' : '❌'} edge freshness floor 0.35 → 0.40:`, JSON.stringify(results),
96
+ '\n probe:', probe,
97
+ '\n opacityNum:', opacityNum, '/ expectedPost:', expectedPost, '/ distToPost:', distToPost.toFixed(4),
98
+ '\n expectedPre:', expectedPre, '/ distToPre:', distToPre.toFixed(4));
99
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,113 @@
1
+ /* Round 439 verification: edge flow particle radius hover lift —
2
+ * r 4.5 → 5.5 on (isHoveredEdge || isEndpointHoveredEdge). Continues
3
+ * the edge paint-layer parity arc (R436 visible path / R437 flow rail
4
+ * / R439 particle).
5
+ *
6
+ * Contract:
7
+ * - rest: every particle reports data-edge-particle-radius '4.5' +
8
+ * lifted='false'
9
+ * - hover one node alias: particles on edges incident to that node
10
+ * report radius '5.5' + lifted='true'
11
+ * - siblings unaffected
12
+ * - source-file probe confirms conditional + transition extension
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', 'idle'),
39
+ ] } });
40
+ });
41
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({
42
+ json: { messages: [
43
+ { id: 'm1', from_alias: 'alpha', to_alias: 'beta', content: 'ping', created_at: fresh, network_id: 'default' },
44
+ { id: 'm2', from_alias: 'alpha', to_alias: 'gamma', content: 'pong', created_at: fresh, network_id: 'default' },
45
+ { id: 'm3', from_alias: 'gamma', to_alias: 'beta', content: 'pang', 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-edge-particle]', { timeout: 15000 });
53
+ await page.waitForTimeout(400);
54
+
55
+ const readAll = () => page.evaluate(() => {
56
+ const ps = [...document.querySelectorAll('[data-edge-particle]')];
57
+ return ps.map(p => ({
58
+ key: p.getAttribute('data-edge-particle'),
59
+ r_attr: p.getAttribute('r'),
60
+ r_data: parseFloat(p.getAttribute('data-edge-particle-radius') || '0'),
61
+ lifted: p.getAttribute('data-edge-particle-lifted'),
62
+ }));
63
+ });
64
+
65
+ const rest = await readAll();
66
+
67
+ let hover = null;
68
+ const box = await page.evaluate(() => {
69
+ const t = document.querySelector('[data-node-alias-text="alpha"]');
70
+ if (!t) return null;
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
+ });
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
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
84
+ const sourceWired = /r=\{\(isHoveredEdge \|\| isEndpointHoveredEdge\) \? 5\.5 : 4\.5\}/.test(fileText);
85
+ const sourceTransition = /transition: 'fill 200ms ease-out, opacity 200ms ease-out, r 200ms ease-out'/.test(fileText);
86
+
87
+ await browser.close();
88
+
89
+ const isAlphaKey = (k) => /alpha/i.test(k);
90
+ const hoverByKey = new Map((hover || []).map(h => [h.key, h]));
91
+ const alphaEntries = [...hoverByKey.entries()].filter(([k]) => isAlphaKey(k));
92
+ const nonAlphaEntries = [...hoverByKey.entries()].filter(([k]) => !isAlphaKey(k));
93
+
94
+ const restAllR_4_5 = rest.every(r => r.r_data === 4.5);
95
+ const restNoneLifted = rest.every(r => r.lifted === 'false');
96
+ const alphaAllR_5_5 = alphaEntries.length > 0 && alphaEntries.every(([, h]) => h.r_data === 5.5 && h.lifted === 'true');
97
+ const nonAlphaRestR = nonAlphaEntries.every(([, h]) => h.r_data === 4.5 && h.lifted === 'false');
98
+
99
+ const results = {
100
+ rest_particle_count_ge_2: rest.length >= 2,
101
+ rest_all_r_4_5: restAllR_4_5,
102
+ rest_none_lifted: restNoneLifted,
103
+ hover_alpha_count_ge_2: alphaEntries.length >= 2,
104
+ hover_alpha_all_lifted_5_5: alphaAllR_5_5,
105
+ hover_non_alpha_unaffected: nonAlphaRestR,
106
+ source_r_wired: sourceWired,
107
+ source_transition_wired: sourceTransition,
108
+ };
109
+ const ok = Object.values(results).every(Boolean);
110
+ console.log(`${ok ? '✅' : '❌'} edge-particle r hover lift:`, JSON.stringify(results),
111
+ '\n rest:', JSON.stringify(rest),
112
+ '\n hover:', JSON.stringify(hover));
113
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,76 @@
1
+ /* Round 422 verification: edge flow particle radius 4 → 4.5.
2
+ * Visual-weight bump family 15th anchor — particles riding along
3
+ * edge animateMotion paths get +0.5px lift (~27% area increase).
4
+ *
5
+ * Contract:
6
+ * - Seed a flow alpha→beta so an edge mounts → particle mounts
7
+ * - The <circle data-edge-particle> has:
8
+ * * r attr === '4.5'
9
+ * * data-edge-particle-radius === '4.5'
10
+ * - Pre-R422 invariants preserved:
11
+ * * R251 fill present (pal.flowParticle)
12
+ * * R252 transition list (fill 200ms + opacity 200ms)
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) => ({
31
+ alias, status: 'idle', 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: [ mk('alpha'), mk('beta'), mk('gamma') ] } });
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
+ ] },
41
+ }));
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-edge-particle]', { timeout: 15000 });
47
+ await page.waitForTimeout(500);
48
+
49
+ const probe = await page.evaluate(() => {
50
+ const particle = document.querySelector('[data-edge-particle]');
51
+ if (!particle) return null;
52
+ return {
53
+ rAttr: particle.getAttribute('r'),
54
+ rData: particle.getAttribute('data-edge-particle-radius'),
55
+ fill: particle.getAttribute('fill'),
56
+ fillPresent: !!particle.getAttribute('fill'),
57
+ styleAttr: particle.getAttribute('style') || '',
58
+ };
59
+ });
60
+
61
+ await browser.close();
62
+
63
+ const results = {
64
+ // R422 wire
65
+ r_attr_4_5: probe?.rAttr === '4.5',
66
+ r_data_4_5: probe?.rData === '4.5',
67
+ // R251 fill invariant
68
+ fill_present: probe?.fillPresent === true,
69
+ // R252 transition invariant
70
+ transition_fill: probe?.styleAttr.includes('fill') === true,
71
+ transition_opacity: probe?.styleAttr.includes('opacity') === true,
72
+ };
73
+ const ok = Object.values(results).every(Boolean);
74
+ console.log(`${ok ? '✅' : '❌'} edge flow particle radius 4 → 4.5:`, JSON.stringify(results),
75
+ '\n probe:', probe);
76
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,89 @@
1
+ /* Round 381 verification: edge visible flow path strokeLinecap='round'.
2
+ * Sibling SVG stroke-softening polish to R378 flow-rail linecap — both
3
+ * flow-element paths now share 'round' linecap. R380 group box closed
4
+ * dash-cap + corner softening; R381 closes the flow-path family at the
5
+ * visible primary path.
6
+ *
7
+ * Stroke-softening family snapshot post-R381:
8
+ * R288 chrome icons strokeLinecap='round'
9
+ * R378 flow-rail dashes strokeLinecap='round'
10
+ * R380 group box dashes strokeLinecap='round'
11
+ * R381 flow visible path strokeLinecap='round' (this round)
12
+ * R379 viewport rect strokeLinejoin='round'
13
+ * R380 group box corners strokeLinejoin='round'
14
+ *
15
+ * Contract:
16
+ * - [data-edge-visible] element stroke-linecap attr === 'round'.
17
+ * - data-edge-visible-linecap === 'round'.
18
+ * - Pre-R381 invariants:
19
+ * * fill='none', stroke=pal.flowEdge
20
+ * * markerEnd referencing arrow id
21
+ * * opacity-based freshness preserved (any value in 0..1)
22
+ * * transition list contains opacity + stroke-width + stroke
23
+ */
24
+ import { chromium } from 'playwright';
25
+ import { readFileSync } from 'node:fs';
26
+
27
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
28
+ const browser = await chromium.launch({ headless: true });
29
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
30
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
31
+ await ctx.addInitScript(() => {
32
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
33
+ });
34
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
35
+ await ctx.route('**/api/hub/status*', async (route) => {
36
+ const r = await route.fetch();
37
+ const b = await r.json();
38
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
39
+ const mk = (alias) => ({
40
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
41
+ network_id: nid, project_dir: null,
42
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
43
+ });
44
+ await route.fulfill({ response: r, json: { ...b, sessions: [ mk('a'), mk('b') ] } });
45
+ });
46
+ const now = Date.now();
47
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
48
+ { id: 'm1', from_alias: 'a', to_alias: 'b', content: 'ping',
49
+ network_id: 'default', created_at: new Date(now - 5000).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-edge-visible]', { timeout: 15000 });
56
+ await page.waitForTimeout(300);
57
+
58
+ const probe = await page.evaluate(() => {
59
+ const p = document.querySelector('[data-edge-visible]');
60
+ const cs = p ? getComputedStyle(p) : null;
61
+ return {
62
+ linecapAttr: p?.getAttribute('stroke-linecap') ?? null,
63
+ linecapData: p?.getAttribute('data-edge-visible-linecap') ?? null,
64
+ fillAttr: p?.getAttribute('fill') ?? null,
65
+ markerEndAttr: p?.getAttribute('marker-end') ?? null,
66
+ transition: cs?.transition ?? null,
67
+ linkKey: p?.getAttribute('data-edge-visible') ?? null,
68
+ };
69
+ });
70
+
71
+ await browser.close();
72
+
73
+ const hasTrans = (s, prop) =>
74
+ new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
75
+
76
+ const results = {
77
+ linecap_round: probe.linecapAttr === 'round',
78
+ data_linecap_round: probe.linecapData === 'round',
79
+ fill_none: probe.fillAttr === 'none',
80
+ marker_end_arrow: /^url\(#topo-arrow-/.test(probe.markerEndAttr || ''),
81
+ trans_has_opacity: hasTrans(probe.transition, 'opacity'),
82
+ trans_has_stroke_w: hasTrans(probe.transition, 'stroke-width'),
83
+ trans_has_stroke: hasTrans(probe.transition, 'stroke'),
84
+ link_key_present: (probe.linkKey || '').length > 0,
85
+ };
86
+ const ok = Object.values(results).every(Boolean);
87
+ console.log(`${ok ? '✅' : '❌'} edge visible path strokeLinecap='round':`, JSON.stringify(results),
88
+ '\n probe:', probe);
89
+ process.exit(ok ? 0 : 1);