@sleep2agi/agent-network-dashboard 0.5.1-preview.70 → 0.5.1-preview.72

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 (141) 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 +4 -4
  4. package/.next/fallback-build-manifest.json +3 -3
  5. package/.next/server/app/_global-error.html +1 -1
  6. package/.next/server/app/_global-error.rsc +1 -1
  7. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  12. package/.next/server/app/_not-found.html +1 -1
  13. package/.next/server/app/_not-found.rsc +1 -1
  14. package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  15. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  16. package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  17. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  18. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  19. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  20. package/.next/server/app/admin.html +1 -1
  21. package/.next/server/app/admin.rsc +1 -1
  22. package/.next/server/app/admin.segments/_full.segment.rsc +1 -1
  23. package/.next/server/app/admin.segments/_head.segment.rsc +1 -1
  24. package/.next/server/app/admin.segments/_index.segment.rsc +1 -1
  25. package/.next/server/app/admin.segments/_tree.segment.rsc +1 -1
  26. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +1 -1
  27. package/.next/server/app/admin.segments/admin.segment.rsc +1 -1
  28. package/.next/server/app/index.html +2 -2
  29. package/.next/server/app/index.rsc +2 -2
  30. package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  31. package/.next/server/app/index.segments/_full.segment.rsc +2 -2
  32. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  33. package/.next/server/app/index.segments/_index.segment.rsc +1 -1
  34. package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  35. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  36. package/.next/server/app/login.html +2 -2
  37. package/.next/server/app/login.rsc +2 -2
  38. package/.next/server/app/login.segments/_full.segment.rsc +2 -2
  39. package/.next/server/app/login.segments/_head.segment.rsc +1 -1
  40. package/.next/server/app/login.segments/_index.segment.rsc +1 -1
  41. package/.next/server/app/login.segments/_tree.segment.rsc +1 -1
  42. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
  43. package/.next/server/app/login.segments/login.segment.rsc +1 -1
  44. package/.next/server/app/logs.html +1 -1
  45. package/.next/server/app/logs.rsc +1 -1
  46. package/.next/server/app/logs.segments/_full.segment.rsc +1 -1
  47. package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
  48. package/.next/server/app/logs.segments/_index.segment.rsc +1 -1
  49. package/.next/server/app/logs.segments/_tree.segment.rsc +1 -1
  50. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +1 -1
  51. package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
  52. package/.next/server/app/messages.html +1 -1
  53. package/.next/server/app/messages.rsc +1 -1
  54. package/.next/server/app/messages.segments/_full.segment.rsc +1 -1
  55. package/.next/server/app/messages.segments/_head.segment.rsc +1 -1
  56. package/.next/server/app/messages.segments/_index.segment.rsc +1 -1
  57. package/.next/server/app/messages.segments/_tree.segment.rsc +1 -1
  58. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +1 -1
  59. package/.next/server/app/messages.segments/messages.segment.rsc +1 -1
  60. package/.next/server/app/node.html +1 -1
  61. package/.next/server/app/node.rsc +1 -1
  62. package/.next/server/app/node.segments/_full.segment.rsc +1 -1
  63. package/.next/server/app/node.segments/_head.segment.rsc +1 -1
  64. package/.next/server/app/node.segments/_index.segment.rsc +1 -1
  65. package/.next/server/app/node.segments/_tree.segment.rsc +1 -1
  66. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +1 -1
  67. package/.next/server/app/node.segments/node.segment.rsc +1 -1
  68. package/.next/server/app/nodes.html +1 -1
  69. package/.next/server/app/nodes.rsc +1 -1
  70. package/.next/server/app/nodes.segments/_full.segment.rsc +1 -1
  71. package/.next/server/app/nodes.segments/_head.segment.rsc +1 -1
  72. package/.next/server/app/nodes.segments/_index.segment.rsc +1 -1
  73. package/.next/server/app/nodes.segments/_tree.segment.rsc +1 -1
  74. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +1 -1
  75. package/.next/server/app/nodes.segments/nodes.segment.rsc +1 -1
  76. package/.next/server/app/page_client-reference-manifest.js +1 -1
  77. package/.next/server/app/server-logs.html +1 -1
  78. package/.next/server/app/server-logs.rsc +1 -1
  79. package/.next/server/app/server-logs.segments/_full.segment.rsc +1 -1
  80. package/.next/server/app/server-logs.segments/_head.segment.rsc +1 -1
  81. package/.next/server/app/server-logs.segments/_index.segment.rsc +1 -1
  82. package/.next/server/app/server-logs.segments/_tree.segment.rsc +1 -1
  83. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +1 -1
  84. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +1 -1
  85. package/.next/server/app/settings/networks.html +1 -1
  86. package/.next/server/app/settings/networks.rsc +1 -1
  87. package/.next/server/app/settings/networks.segments/_full.segment.rsc +1 -1
  88. package/.next/server/app/settings/networks.segments/_head.segment.rsc +1 -1
  89. package/.next/server/app/settings/networks.segments/_index.segment.rsc +1 -1
  90. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +1 -1
  91. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +1 -1
  92. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +1 -1
  93. package/.next/server/app/settings/networks.segments/settings.segment.rsc +1 -1
  94. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  95. package/.next/server/app/settings/tokens.html +1 -1
  96. package/.next/server/app/settings/tokens.rsc +1 -1
  97. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +1 -1
  98. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +1 -1
  99. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +1 -1
  100. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +1 -1
  101. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +1 -1
  102. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +1 -1
  103. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +1 -1
  104. package/.next/server/app/settings.html +2 -2
  105. package/.next/server/app/settings.rsc +2 -2
  106. package/.next/server/app/settings.segments/_full.segment.rsc +2 -2
  107. package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  108. package/.next/server/app/settings.segments/_index.segment.rsc +1 -1
  109. package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  110. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
  111. package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
  112. package/.next/server/app/tasks.html +1 -1
  113. package/.next/server/app/tasks.rsc +1 -1
  114. package/.next/server/app/tasks.segments/_full.segment.rsc +1 -1
  115. package/.next/server/app/tasks.segments/_head.segment.rsc +1 -1
  116. package/.next/server/app/tasks.segments/_index.segment.rsc +1 -1
  117. package/.next/server/app/tasks.segments/_tree.segment.rsc +1 -1
  118. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +1 -1
  119. package/.next/server/app/tasks.segments/tasks.segment.rsc +1 -1
  120. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +2 -2
  121. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  122. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  123. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  124. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  125. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  126. package/.next/server/middleware-build-manifest.js +3 -3
  127. package/.next/server/pages/404.html +1 -1
  128. package/.next/server/pages/500.html +1 -1
  129. package/.next/static/chunks/{15njs76t5041a.js → 08nxwmeon0xj8.js} +1 -1
  130. package/.next/static/chunks/{0i6t8g~8ffaw9.js → 09qnklkne9b4y.js} +1 -1
  131. package/.next/static/chunks/0s4~.i4o2r5vv.js +4 -0
  132. package/.next/trace +2 -2
  133. package/.next/trace-build +1 -1
  134. package/app/components/TopoGraph.tsx +47 -2
  135. package/package.json +1 -1
  136. package/scripts/topo-freshness-chip-hierarchy-test.mjs +93 -0
  137. package/scripts/topo-node-label-card-rx-test.mjs +85 -0
  138. package/.next/static/chunks/13cp6he5qwrwb.js +0 -4
  139. /package/.next/static/{rlPM5o1PeXl3t_WnszW5b → Ku2y1OWWBq4S_S3YMT_fD}/_buildManifest.js +0 -0
  140. /package/.next/static/{rlPM5o1PeXl3t_WnszW5b → Ku2y1OWWBq4S_S3YMT_fD}/_clientMiddlewareManifest.js +0 -0
  141. /package/.next/static/{rlPM5o1PeXl3t_WnszW5b → Ku2y1OWWBq4S_S3YMT_fD}/_ssgManifest.js +0 -0
@@ -257,7 +257,21 @@ function FreshnessChip({ sessions }: { sessions: unknown }) {
257
257
  duration-300 still eases the bg/color flip. Title (hover
258
258
  tooltip) still spells out the full meaning in either
259
259
  state. */}
260
- {stale ? 'lag' : 'live'} · {sec}s
260
+ {/* Round 410 / Loop: FreshnessChip body picks up the chip-
261
+ internal-hierarchy arc. Pre-R410 the body rendered as a
262
+ single text node `lag · {sec}s` with the parent's font-
263
+ medium (fw=500) applied uniformly. R410 splits the digit
264
+ and unit into separate spans so the chip's internal
265
+ typography mirrors the family pattern R333-R341/R362/R369/
266
+ R389 established for the chip row:
267
+ digit (fw=600) data tier
268
+ unit (fw=500 + opacity=0.7) label tier
269
+ The `lag` prefix stays at the chip's baseline (fw=500
270
+ from parent font-medium) — it labels the state, not a
271
+ data value. data-freshness-chip-digit / -unit attrs
272
+ surface the spans for tests. tabular-nums + transition-
273
+ colors + R275 stale-only gate all preserved. */}
274
+ {stale ? 'lag' : 'live'} · <span className="font-semibold" data-freshness-chip-digit>{sec}</span><span className="opacity-70" data-freshness-chip-unit>s</span>
261
275
  </span>
262
276
  );
263
277
  }
@@ -6770,14 +6784,45 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
6770
6784
  220ms cadence the existing filter/stroke
6771
6785
  pair uses — coordinated 4-property easing
6772
6786
  across the card. */}
6787
+ {/* Round 411 / Loop: node label card rx 6 → 8.
6788
+ Pre-R411 the per-node label card painted at
6789
+ rx=6, sitting one tier BELOW the R332/R375/
6790
+ R376 compact-chrome tier (rx=8). Inside the
6791
+ corner-radius cascade family the cards used
6792
+ to be the only "smaller" tier — but the
6793
+ label card is a content-bearing surface
6794
+ (alias + sub text + ring), not a sub-
6795
+ element decoration. R411 lifts rx=6 → 8
6796
+ to align with the compact-chrome / segmented-
6797
+ control tier so all "compact card" surfaces
6798
+ read with the same corner radius.
6799
+ Corner-radius cascade (8 anchors now):
6800
+ R330 canvas rx 12 (root)
6801
+ R331 panels rx 10 (recent-signal, legend)
6802
+ R332 minimap container rx 8 (compact chrome)
6803
+ R375 Layout-toggle rx 8 (segmented control)
6804
+ R376 nodeSize/zoom rx 8 (segmented control)
6805
+ R390 hover-detail rx 10 (panel)
6806
+ R393 minimap viewport rx 2 (sub-element)
6807
+ R411 node label card rx 6 → 8 (compact card, this round)
6808
+ Pure paint — rx grows the corner curve
6809
+ inward without changing the card's outer
6810
+ cardW × cardH bbox (cardW=92/cardH=22 for
6811
+ standard nodes per R23 / R187 sizing). R217
6812
+ hover-stroke cyan tint + R142 drop-shadow
6813
+ + R246 fill+opacity 220ms transition list
6814
+ + R211 alias/sub text-fill ease all
6815
+ preserved. data-node-label-card-rx attr
6816
+ exposes the value for tests. */}
6773
6817
  <rect
6774
- x={-cardW / 2} y={cardTopY} width={cardW} height={cardH} rx="6"
6818
+ x={-cardW / 2} y={cardTopY} width={cardW} height={cardH} rx="8"
6775
6819
  fill={pal.labelBox.fill}
6776
6820
  stroke={!reducedMotion && hoveredAlias === session.alias
6777
6821
  ? pal.legendAccent
6778
6822
  : pal.labelBox.stroke}
6779
6823
  opacity={isLight ? 1 : 0.94}
6780
6824
  data-node-label-card={session.alias}
6825
+ data-node-label-card-rx="8"
6781
6826
  data-node-label-card-elevation={
6782
6827
  !reducedMotion && hoveredAlias === session.alias ? 'hover' : 'idle'
6783
6828
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleep2agi/agent-network-dashboard",
3
- "version": "0.5.1-preview.70",
3
+ "version": "0.5.1-preview.72",
4
4
  "description": "Agent Network Dashboard — Web UI for managing AI Agent networks",
5
5
  "scripts": {
6
6
  "dev": "next dev",
@@ -0,0 +1,93 @@
1
+ /* Round 410 verification: FreshnessChip body picks up chip-internal-
2
+ * hierarchy — `{sec}` digit (fw=600 data tier) + `s` unit (opacity
3
+ * 0.7 label tier). Extends R333-R341/R362/R369/R389 arc to 10th
4
+ * anchor.
5
+ *
6
+ * Contract:
7
+ * - R275 stale-only render: chip only mounts when sec > 10. Test
8
+ * waits for /api/hub/status to settle, then advances lastSyncRef
9
+ * by 11+ seconds via a long wait (the FreshnessChip ticks every
10
+ * 1s via setInterval). To avoid the slow wait we instead probe
11
+ * the SOURCE-FILE for the wire change, then probe the chip if
12
+ * it happens to mount during the test window.
13
+ * - Source-file:
14
+ * * <span className="font-semibold" data-freshness-chip-digit>
15
+ * * <span className="opacity-70" data-freshness-chip-unit>
16
+ * - If chip mounts at runtime: data-freshness-chip-digit + -unit
17
+ * spans both present
18
+ */
19
+ import { chromium } from 'playwright';
20
+ import { readFileSync } from 'node:fs';
21
+
22
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
23
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
24
+
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
+ await ctx.route('**/api/hub/status*', async (route) => {
32
+ const r = await route.fetch();
33
+ const b = await r.json();
34
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
35
+ const mk = (alias) => ({
36
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
37
+ network_id: nid, project_dir: null,
38
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
39
+ });
40
+ await route.fulfill({ response: r, json: { ...b, sessions: [ mk('alpha') ] } });
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-topo-hub]', { timeout: 15000 });
48
+
49
+ // Wait for FreshnessChip to mount (R275 stale-only: sec > 10). The
50
+ // chip ticks every 1s. Wait 12s so lastSyncRef-based sec crosses 10.
51
+ await page.waitForTimeout(12000);
52
+
53
+ const probe = await page.evaluate(() => {
54
+ const chip = document.querySelector('[data-freshness-chip]');
55
+ if (!chip) return { mounted: false };
56
+ const digit = chip.querySelector('[data-freshness-chip-digit]');
57
+ const unit = chip.querySelector('[data-freshness-chip-unit]');
58
+ return {
59
+ mounted: true,
60
+ chipStale: chip.getAttribute('data-freshness-chip-stale'),
61
+ digitClass: digit?.className ?? null,
62
+ digitText: digit?.textContent ?? null,
63
+ unitClass: unit?.className ?? null,
64
+ unitText: unit?.textContent ?? null,
65
+ };
66
+ });
67
+
68
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
69
+ const sourceHasDigitSpan = /<span className="font-semibold" data-freshness-chip-digit>/.test(fileText);
70
+ const sourceHasUnitSpan = /<span className="opacity-70" data-freshness-chip-unit>s<\/span>/.test(fileText);
71
+
72
+ await browser.close();
73
+
74
+ // Either runtime probe catches the chip OR source-file verification
75
+ // alone establishes the wire change.
76
+ const results = {
77
+ // Source-file canonical wire (deterministic)
78
+ source_digit_span: sourceHasDigitSpan,
79
+ source_unit_span: sourceHasUnitSpan,
80
+ // Runtime: chip might be present if test wait crossed 10s; assertions
81
+ // are conditional on mounted (only enforce structure when chip exists).
82
+ runtime_check_passed: !probe.mounted || (
83
+ probe.chipStale === 'true' &&
84
+ probe.digitClass?.includes('font-semibold') === true &&
85
+ /^\d+$/.test(probe.digitText || '') &&
86
+ probe.unitClass?.includes('opacity-70') === true &&
87
+ probe.unitText === 's'
88
+ ),
89
+ };
90
+ const ok = Object.values(results).every(Boolean);
91
+ console.log(`${ok ? '✅' : '❌'} FreshnessChip chip-internal-hierarchy:`, JSON.stringify(results),
92
+ '\n probe:', probe);
93
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,85 @@
1
+ /* Round 411 verification: node label card rx 6 → 8. Corner-radius
2
+ * cascade family 8th anchor — compact card tier aligns with the
3
+ * R332/R375/R376 compact-chrome rx=8 tier.
4
+ *
5
+ * Contract:
6
+ * - Each node's label-card <rect> rx attr === '8'
7
+ * - data-node-label-card-rx === '8'
8
+ * - Pre-R411 invariants on the rect:
9
+ * * cardW (width) > 0
10
+ * * cardH (height) > 0
11
+ * * fill, stroke present
12
+ */
13
+ import { chromium } from 'playwright';
14
+ import { readFileSync } from 'node:fs';
15
+
16
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
17
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
18
+
19
+ const browser = await chromium.launch({ headless: true });
20
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
21
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
22
+ await ctx.addInitScript(() => {
23
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
24
+ });
25
+ await ctx.route('**/api/hub/status*', async (route) => {
26
+ const r = await route.fetch();
27
+ const b = await r.json();
28
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
29
+ const mk = (alias) => ({
30
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
31
+ network_id: nid, project_dir: null,
32
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
33
+ });
34
+ await route.fulfill({ response: r, json: { ...b, sessions: [ mk('alpha'), mk('beta'), mk('gamma') ] } });
35
+ });
36
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
37
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
38
+
39
+ const page = await ctx.newPage();
40
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
41
+ await page.waitForSelector('[data-node-label-card]', { timeout: 15000 });
42
+ await page.waitForTimeout(400);
43
+
44
+ const probe = await page.evaluate(() => {
45
+ const cards = Array.from(document.querySelectorAll('[data-node-label-card]'));
46
+ if (cards.length === 0) return { count: 0 };
47
+ const sample = cards[0];
48
+ return {
49
+ count: cards.length,
50
+ rxAttr: sample.getAttribute('rx'),
51
+ rxData: sample.getAttribute('data-node-label-card-rx'),
52
+ widthAttr: sample.getAttribute('width'),
53
+ heightAttr: sample.getAttribute('height'),
54
+ fillPresent: !!sample.getAttribute('fill'),
55
+ strokePresent: !!sample.getAttribute('stroke'),
56
+ allRx8: cards.every((c) => c.getAttribute('rx') === '8'),
57
+ };
58
+ });
59
+
60
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
61
+ const sourceRx8 = /rx="8"\s*\n\s+fill=\{pal\.labelBox\.fill\}/.test(fileText) || /width=\{cardW\} height=\{cardH\} rx="8"/.test(fileText);
62
+ const sourceDataRx8 = /data-node-label-card-rx="8"/.test(fileText);
63
+
64
+ await browser.close();
65
+
66
+ const results = {
67
+ // At least one label card mounted
68
+ cards_count_ge_1: (probe.count || 0) >= 1,
69
+ // R411 wire
70
+ rx_attr_8: probe.rxAttr === '8',
71
+ rx_data_8: probe.rxData === '8',
72
+ all_cards_rx_8: probe.allRx8 === true,
73
+ // Pre-R411 invariants
74
+ width_present: Number(probe.widthAttr) > 0,
75
+ height_present: Number(probe.heightAttr) > 0,
76
+ fill_present: probe.fillPresent === true,
77
+ stroke_present: probe.strokePresent === true,
78
+ // Source-file canonical wire
79
+ source_rx_8: sourceRx8,
80
+ source_data_rx_8: sourceDataRx8,
81
+ };
82
+ const ok = Object.values(results).every(Boolean);
83
+ console.log(`${ok ? '✅' : '❌'} node label card rx 6 → 8:`, JSON.stringify(results),
84
+ '\n probe:', probe);
85
+ process.exit(ok ? 0 : 1);