@sleep2agi/agent-network-dashboard 0.5.1-preview.98 → 0.5.1

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 (202) 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/05admfiu6qfp2.js +1 -0
  150. package/.next/static/chunks/0a4hmfvj-81x5.css +2 -0
  151. package/.next/static/chunks/0d9mlqf.rjey5.js +1 -0
  152. package/.next/static/chunks/11puuje6at2jt.js +4 -0
  153. package/.next/static/chunks/{03~~oirxz7~vc.js → 15ub0_3b099x1.js} +1 -1
  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 +778 -90
  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-any-hover-attr-test.mjs +83 -0
  165. package/scripts/topo-any-pinned-attr-test.mjs +86 -0
  166. package/scripts/topo-chrome-fullscreen-icon-sw-test.mjs +92 -0
  167. package/scripts/topo-chrome-reset-icon-sw-test.mjs +80 -0
  168. package/scripts/topo-chrome-zoom-icon-sw-test.mjs +90 -0
  169. package/scripts/topo-dashboard-version-attr-test.mjs +69 -0
  170. package/scripts/topo-dense-alias-opacity-test.mjs +68 -0
  171. package/scripts/topo-edge-particle-hover-r-test.mjs +113 -0
  172. package/scripts/topo-endpoint-ring-r-hover-test.mjs +89 -0
  173. package/scripts/topo-group-box-geom-transition-test.mjs +110 -0
  174. package/scripts/topo-group-box-rx-pin-test.mjs +103 -0
  175. package/scripts/topo-group-label-count-fw-test.mjs +100 -0
  176. package/scripts/topo-group-label-fw-pin-test.mjs +99 -0
  177. package/scripts/topo-group-label-tint-geom-test.mjs +94 -0
  178. package/scripts/topo-group-label-tint-transition-test.mjs +97 -0
  179. package/scripts/topo-group-pip-fontsize-test.mjs +106 -0
  180. package/scripts/topo-group-tint-rx-pin-test.mjs +107 -0
  181. package/scripts/topo-hub-core-fill-hover-test.mjs +85 -0
  182. package/scripts/topo-hub-halo-r-hover-test.mjs +82 -0
  183. package/scripts/topo-legend-count-active-opacity-test.mjs +102 -0
  184. package/scripts/topo-legend-count-pin-fw-test.mjs +90 -0
  185. package/scripts/topo-minimap-viewport-opacity-test.mjs +96 -0
  186. package/scripts/topo-node-halo-hover-opacity-test.mjs +104 -0
  187. package/scripts/topo-node-halo-light-offline-test.mjs +80 -0
  188. package/scripts/topo-node-sub-text-fw-test.mjs +75 -0
  189. package/scripts/topo-overlap-stale-build-guard-test.mjs +66 -0
  190. package/scripts/topo-overlap-test.mjs +42 -1
  191. package/scripts/topo-recent-row-count-pin-fw-test.mjs +106 -0
  192. package/scripts/topo-recent-row-pip-hover-r-test.mjs +104 -0
  193. package/scripts/topo-runtime-icon-hover-test.mjs +96 -0
  194. package/scripts/topo-status-ring-hover-sw-test.mjs +105 -0
  195. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +0 -1
  196. package/.next/static/chunks/017hq2-5l~_98.css +0 -2
  197. package/.next/static/chunks/0kmjjpdppowr2.js +0 -1
  198. package/.next/static/chunks/11-cqnshuwwij.js +0 -4
  199. package/.next/static/chunks/14myvippibo7y.js +0 -1
  200. /package/.next/static/{4VuFWzQ7d8yfyNm5pNIB0 → gowHgWkn_kpMCSBn8RFaa}/_buildManifest.js +0 -0
  201. /package/.next/static/{4VuFWzQ7d8yfyNm5pNIB0 → gowHgWkn_kpMCSBn8RFaa}/_clientMiddlewareManifest.js +0 -0
  202. /package/.next/static/{4VuFWzQ7d8yfyNm5pNIB0 → gowHgWkn_kpMCSBn8RFaa}/_ssgManifest.js +0 -0
@@ -0,0 +1,90 @@
1
+ /* Round 446 verification: legend-row count fontWeight lift on isPinned.
2
+ * 600 → 700 when the status tier is pinned. Mirror of R444/R445 at
3
+ * legend-row scope.
4
+ *
5
+ * Contract:
6
+ * - rest: every legend-count reports font-weight '600' + pinned='false'
7
+ * - click a legend-status group: that row's count flips to '700' +
8
+ * pinned='true'; siblings stay rest
9
+ */
10
+ import { chromium } from 'playwright';
11
+ import { readFileSync } from 'node:fs';
12
+
13
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
14
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
15
+
16
+ const browser = await chromium.launch({ headless: true });
17
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
18
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
19
+ await ctx.addInitScript(() => {
20
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
21
+ });
22
+ await ctx.route('**/api/hub/status*', async (route) => {
23
+ const r = await route.fetch();
24
+ const b = await r.json();
25
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
26
+ const mk = (alias, status) => ({
27
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
28
+ network_id: nid, project_dir: null,
29
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
30
+ });
31
+ await route.fulfill({ response: r, json: { ...b, sessions: [
32
+ mk('alpha', 'working'),
33
+ mk('beta', 'working'),
34
+ mk('gamma', 'idle'),
35
+ ] } });
36
+ });
37
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
38
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
39
+
40
+ const page = await ctx.newPage();
41
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
42
+ await page.waitForSelector('[data-legend-count]', { timeout: 15000 });
43
+ await page.waitForTimeout(400);
44
+
45
+ const readAll = () => page.evaluate(() => {
46
+ const ts = [...document.querySelectorAll('[data-legend-count]')];
47
+ return ts.map(t => ({
48
+ key: t.getAttribute('data-legend-count'),
49
+ fw: t.getAttribute('font-weight'),
50
+ pinned: t.getAttribute('data-legend-count-pinned'),
51
+ }));
52
+ });
53
+
54
+ const rest = await readAll();
55
+
56
+ // Click the 'working' status row to pin it
57
+ let pinned = null;
58
+ const workingGroup = await page.$(`[data-legend-status="working"]`);
59
+ if (workingGroup) {
60
+ await workingGroup.click();
61
+ await page.waitForTimeout(250);
62
+ pinned = await readAll();
63
+ }
64
+
65
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
66
+ const sourceWired = /fontWeight=\{isPinned \? '700' : '600'\}/.test(fileText);
67
+
68
+ await browser.close();
69
+
70
+ const restAll600 = rest.every(r => r.fw === '600');
71
+ const restNoPin = rest.every(r => r.pinned === 'false');
72
+ const pinnedEntry = pinned?.find(r => r.key === 'working');
73
+ const pinFw_700 = pinnedEntry?.fw === '700';
74
+ const pinFlag = pinnedEntry?.pinned === 'true';
75
+ const othersStayRest = pinned ? pinned.filter(r => r.key !== 'working').every(r => r.fw === '600' && r.pinned === 'false') : false;
76
+
77
+ const results = {
78
+ rest_three_rows: rest.length === 3,
79
+ rest_all_fw_600: restAll600,
80
+ rest_no_pin: restNoPin,
81
+ pinned_target_fw_700: pinFw_700,
82
+ pinned_target_flag: pinFlag,
83
+ pinned_others_stay: othersStayRest,
84
+ source_wired: sourceWired,
85
+ };
86
+ const ok = Object.values(results).every(Boolean);
87
+ console.log(`${ok ? '✅' : '❌'} legend count fw pin:`, JSON.stringify(results),
88
+ '\n rest:', JSON.stringify(rest),
89
+ '\n pinned target:', JSON.stringify(pinnedEntry));
90
+ 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,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);
@@ -0,0 +1,75 @@
1
+ /* Round 448 verification: node sub-text fontWeight 400 → 500.
2
+ * Static legibility lift for small mono text at fontSize=9.
3
+ * Sibling to R363/R364 small-text fw lifts.
4
+ *
5
+ * Contract:
6
+ * - every node sub-text reports font-weight '500'
7
+ * - geometry invariants preserved (R428 hover ls + R211 fill
8
+ * transition still intact)
9
+ */
10
+ import { chromium } from 'playwright';
11
+ import { readFileSync } from 'node:fs';
12
+
13
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
14
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
15
+
16
+ const browser = await chromium.launch({ headless: true });
17
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
18
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
19
+ await ctx.addInitScript(() => {
20
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
21
+ });
22
+ await ctx.route('**/api/hub/status*', async (route) => {
23
+ const r = await route.fetch();
24
+ const b = await r.json();
25
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
26
+ const mk = (alias, status) => ({
27
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
28
+ network_id: nid, project_dir: null,
29
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
30
+ });
31
+ await route.fulfill({ response: r, json: { ...b, sessions: [
32
+ mk('alpha', 'working'),
33
+ mk('beta', 'idle'),
34
+ mk('gamma', 'working'),
35
+ ] } });
36
+ });
37
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
38
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
39
+
40
+ const page = await ctx.newPage();
41
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
42
+ await page.waitForSelector('[data-node-sub-text]', { timeout: 15000 });
43
+ await page.waitForTimeout(400);
44
+
45
+ const subs = await page.evaluate(() => {
46
+ const ts = [...document.querySelectorAll('[data-node-sub-text]')];
47
+ return ts.map(t => ({
48
+ alias: t.getAttribute('data-node-sub-text'),
49
+ fw: t.getAttribute('font-weight'),
50
+ fw_data: t.getAttribute('data-node-sub-text-font-weight'),
51
+ text: t.textContent,
52
+ }));
53
+ });
54
+
55
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
56
+ const sourceWired = /fontWeight="500"\s*\n\s*data-node-sub-text=\{session\.alias\}/.test(fileText);
57
+
58
+ await browser.close();
59
+
60
+ const restAllFw500 = subs.every(s => s.fw === '500');
61
+ const restAllAttr500 = subs.every(s => s.fw_data === '500');
62
+ const restCountGe3 = subs.length >= 3;
63
+ const restAllHaveText = subs.every(s => s.text && s.text.length > 0);
64
+
65
+ const results = {
66
+ sub_count_ge_3: restCountGe3,
67
+ all_fw_500_attr: restAllFw500,
68
+ all_fw_data_attr_500: restAllAttr500,
69
+ all_have_text: restAllHaveText,
70
+ source_wired: sourceWired,
71
+ };
72
+ const ok = Object.values(results).every(Boolean);
73
+ console.log(`${ok ? '✅' : '❌'} node sub-text fw 500:`, JSON.stringify(results),
74
+ '\n subs:', JSON.stringify(subs));
75
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,66 @@
1
+ /* Round 463 verification: topo-overlap-test.mjs grew a zombie-
2
+ * build guard that consumes R462's svg[data-dashboard-version]
3
+ * surface. Pre-R463 a stale next-server (dash zombie holding
4
+ * cached chunks from an earlier preview) would let overlap-test
5
+ * report fake green — bit us in R441 + R460. R463 closes that
6
+ * by reading the DOM-side version and comparing to the package.
7
+ * json version, failing fast with exit code 2 on mismatch.
8
+ *
9
+ * This test verifies:
10
+ * 1. The augmented topo-overlap-test source contains the
11
+ * guard machinery (regex assertions on the actual file).
12
+ * 2. The live dash agrees with package.json (sanity-check
13
+ * the current run state matches the guard's expectation).
14
+ * 3. The guard sentinel path is wired in the caller (sentinel
15
+ * object with .stale === true triggers exit code 2).
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 expected = JSON.parse(readFileSync('/home/vansin/agent-network-dashboard/package.json', 'utf8')).version;
22
+
23
+ const src = readFileSync('/home/vansin/agent-network-dashboard/scripts/topo-overlap-test.mjs', 'utf8');
24
+ const sourceHasGuardComment = /Round 463.*zombie-build guard/.test(src);
25
+ const sourceHasLiveProbe = /svg\.getAttribute\('data-dashboard-version'\)/.test(src);
26
+ const sourceHasMismatchBail = /STALE BUILD/.test(src);
27
+ const sourceHasSentinelObj = /\{ stale: true, live, expected \}/.test(src);
28
+ const sourceHasCallerCheck = /typeof r === 'object' && r\.stale/.test(src);
29
+ const sourceHasExit2 = /process\.exit\(2\)/.test(src);
30
+
31
+ // Run a live probe to confirm the dash + pkg are aligned (we're in
32
+ // a clean state where the guard would PASS silently).
33
+ const browser = await chromium.launch({ headless: true });
34
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 920 } });
35
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
36
+ await ctx.addInitScript(() => {
37
+ try {
38
+ localStorage.setItem('anet-theme', 'cyber');
39
+ localStorage.setItem('anet-topo-layout', 'grid');
40
+ sessionStorage.setItem('anet_v3_auth', '1');
41
+ } catch {}
42
+ });
43
+ const page = await ctx.newPage();
44
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
45
+ await page.waitForSelector('svg[viewBox="0 0 1000 680"]', { timeout: 15000 });
46
+ const live = await page.evaluate(() =>
47
+ document.querySelector('svg[viewBox="0 0 1000 680"]')?.getAttribute('data-dashboard-version')
48
+ );
49
+ await browser.close();
50
+
51
+ const liveMatchesPkg = live === expected;
52
+
53
+ const results = {
54
+ source_has_guard_comment: sourceHasGuardComment,
55
+ source_has_live_probe: sourceHasLiveProbe,
56
+ source_has_mismatch_bail: sourceHasMismatchBail,
57
+ source_has_sentinel_object: sourceHasSentinelObj,
58
+ source_has_caller_check: sourceHasCallerCheck,
59
+ source_has_exit_2: sourceHasExit2,
60
+ dash_live_matches_package: liveMatchesPkg,
61
+ };
62
+ const ok = Object.values(results).every(Boolean);
63
+ console.log(`${ok ? '✅' : '❌'} overlap-test stale-build guard:`, JSON.stringify(results),
64
+ '\n expected pkg:', expected,
65
+ '\n live DOM: ', live);
66
+ process.exit(ok ? 0 : 1);
@@ -59,6 +59,37 @@ async function check(layout) {
59
59
  }, { timeout: 30000 }).catch(() => {});
60
60
  await page.waitForTimeout(800);
61
61
 
62
+ // Round 463 / Loop: zombie-build guard — consumes R462's
63
+ // svg[data-dashboard-version] surface (preview.130+). Before
64
+ // reporting collision metrics, verify the dash is actually
65
+ // serving the build that matches package.json. This catches
66
+ // the dash-zombie-port-3000 failure mode (next-server cached
67
+ // chunks from earlier preview) that bit R441 + R460 — those
68
+ // rounds reported false-positive overlap-test green because
69
+ // probes ran against stale code. Test scripts shipping before
70
+ // R462 don't have the attr; we tolerate its absence (older
71
+ // dash) but fail hard on mismatch. The `expected` value comes
72
+ // from this script's CWD package.json — co-located with the
73
+ // working tree → it reflects whatever version was bumped this
74
+ // round.
75
+ const expected = JSON.parse(readFileSync('/home/vansin/agent-network-dashboard/package.json', 'utf8')).version;
76
+ const live = await page.evaluate(() => {
77
+ const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
78
+ return svg ? svg.getAttribute('data-dashboard-version') : null;
79
+ });
80
+ if (live && live !== expected) {
81
+ console.error(`❌ [${layout}] STALE BUILD — dash serves ${live} but package.json says ${expected}.`);
82
+ console.error(` Likely cause: zombie next-server (pkill -9 + restart). See feedback_dash_zombie_port_3000.md.`);
83
+ await ctx.close();
84
+ return { stale: true, live, expected };
85
+ }
86
+ if (!live) {
87
+ // R462 attr not present — dash is on an older build than the
88
+ // R462 surface ships in. Don't fail; just warn so the loop
89
+ // operator notices. Real overlap metrics still run.
90
+ console.warn(`⚠️ [${layout}] data-dashboard-version absent (dash pre-R462? expected ${expected}). Skipping zombie-guard.`);
91
+ }
92
+
62
93
  const data = await page.evaluate(() => {
63
94
  const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
64
95
  // node status rings: stroke-width 3 (online) / 1.5 (offline), centred on the node
@@ -159,6 +190,16 @@ const all = [];
159
190
  all.push(await check('grid'));
160
191
  all.push(await check('ring'));
161
192
  await browser.close();
162
- const pass = all.every(Boolean);
193
+ // R463: any non-boolean return is a sentinel (e.g. { stale: true }
194
+ // from the zombie-build guard). Treat as a hard fail — collision
195
+ // metrics from a stale build are meaningless, and reporting them
196
+ // as green would re-create the R441/R460 false-positive that hit
197
+ // us before the data-dashboard-version surface existed.
198
+ const stale = all.find(r => r && typeof r === 'object' && r.stale);
199
+ if (stale) {
200
+ console.error(`\n❌ STALE BUILD — dash served ${stale.live}, expected ${stale.expected}. Restart dash + retry.`);
201
+ process.exit(2);
202
+ }
203
+ const pass = all.every(r => r === true);
163
204
  console.log(pass ? '\n✅ ZERO OVERLAP' : '\n❌ OVERLAP DETECTED');
164
205
  process.exit(pass ? 0 : 1);