@sleep2agi/agent-network-dashboard 0.5.3-preview.249 → 0.5.3-preview.251

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 (145) 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 +5 -5
  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/[root-of-the-server]__0sv~g.o._.js +1 -1
  121. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -1
  122. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +1 -1
  123. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  124. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  125. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  126. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  127. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  128. package/.next/server/middleware-build-manifest.js +3 -3
  129. package/.next/server/pages/404.html +1 -1
  130. package/.next/server/pages/500.html +1 -1
  131. package/.next/static/chunks/{06wiclye-apdf.js → 071f-verpvy.l.js} +1 -1
  132. package/.next/static/chunks/{0smee.q5ag8f0.js → 0scyt82phn6ko.js} +1 -1
  133. package/.next/static/chunks/{099eai9qkfdz~.js → 0u-kktt2jl42v.js} +1 -1
  134. package/.next/static/chunks/{0.w20n414dt1w.js → 115od_bzi_gud.js} +1 -1
  135. package/.next/trace +2 -2
  136. package/.next/trace-build +1 -1
  137. package/app/components/TopoGraph.tsx +68 -2
  138. package/package.json +1 -1
  139. package/scripts/topo-a11y-titles-catalog-test.mjs +2 -2
  140. package/scripts/topo-canvas-scan-beam-vertical-test.mjs +123 -0
  141. package/scripts/topo-r717-scan-beam-pair-pattern-test.mjs +100 -0
  142. package/scripts/topo-respiratory-patterns-catalog-test.mjs +20 -14
  143. /package/.next/static/{zXhc40uO3T0MFGWUMXf_f → 2ivD7P_Y9t_aydinQpBz5}/_buildManifest.js +0 -0
  144. /package/.next/static/{zXhc40uO3T0MFGWUMXf_f → 2ivD7P_Y9t_aydinQpBz5}/_clientMiddlewareManifest.js +0 -0
  145. /package/.next/static/{zXhc40uO3T0MFGWUMXf_f → 2ivD7P_Y9t_aydinQpBz5}/_ssgManifest.js +0 -0
@@ -0,0 +1,123 @@
1
+ /* Round 736 — vertical scan beam companion to R735's horizontal beam.
2
+ * Forms the "scan beam" ambient family — coprime cadences mean the
3
+ * beams never phase-lock; over time the visual rhythm reads as
4
+ * "ambient crosshair drift".
5
+ *
6
+ * R735 horizontal beam 30 s (top → bottom)
7
+ * R736 vertical beam 23 s (left → right) ← this round
8
+ * gcd(23, 30) = 1
9
+ *
10
+ * Assertions:
11
+ * - <rect data-topo-canvas-scan-beam-vertical> exists with 1×680 box
12
+ * - x animates -2 → 1000 → -2 over 23s repeatCount=indefinite
13
+ * - opacity animates 0 → 0.08 → 0.08 → 0 at keyTimes 0/0.05/0.95/1, dur 23s
14
+ * - SVG <title> child = "canvas scan beam vertical · ambient sweep · 23s cycle"
15
+ * - data-topo-canvas-scan-beam-vertical-active="true" (reducedMotion off)
16
+ * - vertical beam in background layer (rendered before nodes)
17
+ * - vertical beam rendered AFTER horizontal beam (pair grouping)
18
+ * - R735 horizontal beam still intact (regression — y, dur, title preserved)
19
+ * - coprime cadences: gcd(23, 30) === 1 — verified by checking the
20
+ * two beams' durs are 23s/30s and Math.gcd-equivalent reduces to 1
21
+ * - R732 a11y-titles catalog has 8 entries incl. vertical beam
22
+ */
23
+ import { chromium } from 'playwright';
24
+ import { readFileSync } from 'node:fs';
25
+
26
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
27
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
28
+
29
+ const browser = await chromium.launch({ headless: true });
30
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
31
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
32
+ await ctx.addInitScript(() => {
33
+ try {
34
+ localStorage.setItem('anet-theme', 'cyber');
35
+ localStorage.setItem('anet-topo-layout', 'ring');
36
+ sessionStorage.setItem('anet_v3_auth', '1');
37
+ } catch {}
38
+ });
39
+ await ctx.route('**/api/hub/status*', async (route) => {
40
+ const r = await route.fetch();
41
+ const b = await r.json();
42
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
43
+ const mk = (alias) => ({
44
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
45
+ network_id: nid, project_dir: null,
46
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
47
+ });
48
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('a·1'), mk('a·2')] } });
49
+ });
50
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
51
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
52
+ const page = await ctx.newPage();
53
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
54
+ await page.waitForSelector('[data-topo-canvas-scan-beam-vertical]', { timeout: 15000, state: 'attached' });
55
+ await page.waitForTimeout(300);
56
+
57
+ const state = await page.evaluate(() => {
58
+ const vbeam = document.querySelector('[data-topo-canvas-scan-beam-vertical]');
59
+ const hbeam = document.querySelector('[data-topo-canvas-scan-beam]');
60
+ if (!vbeam || !hbeam) return null;
61
+ const xAnim = vbeam.querySelector('animate[attributeName="x"]');
62
+ const opAnim = vbeam.querySelector('animate[attributeName="opacity"]');
63
+ const vtitle = vbeam.querySelector(':scope > title');
64
+ const yAnim = hbeam.querySelector('animate[attributeName="y"]');
65
+ const htitle = hbeam.querySelector(':scope > title');
66
+ const svg = document.querySelector('[data-topo-canvas-aria]');
67
+ const firstNode = svg?.querySelector('g[data-node]');
68
+ const vbeamBeforeNodes = vbeam && firstNode
69
+ ? (vbeam.compareDocumentPosition(firstNode) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0
70
+ : null;
71
+ const vbeamAfterHbeam = hbeam && vbeam
72
+ ? (hbeam.compareDocumentPosition(vbeam) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0
73
+ : null;
74
+ const catalogAttr = svg?.getAttribute('data-topo-a11y-titles') ?? null;
75
+ return {
76
+ vbeam_present: !!vbeam,
77
+ vbeam_active_attr: vbeam?.getAttribute('data-topo-canvas-scan-beam-vertical-active') ?? null,
78
+ vbeam_width: vbeam?.getAttribute('width') ?? null,
79
+ vbeam_height: vbeam?.getAttribute('height') ?? null,
80
+ x_anim_present: !!xAnim,
81
+ x_anim_values: xAnim?.getAttribute('values') ?? null,
82
+ x_anim_dur: xAnim?.getAttribute('dur') ?? null,
83
+ x_anim_repeat: xAnim?.getAttribute('repeatCount') ?? null,
84
+ op_anim_present: !!opAnim,
85
+ op_anim_values: opAnim?.getAttribute('values') ?? null,
86
+ op_anim_dur: opAnim?.getAttribute('dur') ?? null,
87
+ op_anim_keytimes: opAnim?.getAttribute('keyTimes') ?? null,
88
+ vtitle_text: vtitle?.textContent ?? null,
89
+ hbeam_present: !!hbeam,
90
+ hbeam_y_dur: yAnim?.getAttribute('dur') ?? null,
91
+ hbeam_y_values: yAnim?.getAttribute('values') ?? null,
92
+ htitle_text: htitle?.textContent ?? null,
93
+ vbeam_before_nodes: vbeamBeforeNodes,
94
+ vbeam_after_hbeam: vbeamAfterHbeam,
95
+ catalog_includes_vbeam: typeof catalogAttr === 'string' && catalogAttr.includes('canvas scan beam vertical'),
96
+ };
97
+ });
98
+
99
+ await browser.close();
100
+
101
+ const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b));
102
+ const coprime_23_30 = gcd(23, 30) === 1;
103
+
104
+ const results = {
105
+ vbeam_rect_present: state?.vbeam_present === true,
106
+ vbeam_active_true: state?.vbeam_active_attr === 'true',
107
+ vbeam_1px_full_height: state?.vbeam_width === '1' && state?.vbeam_height === '680',
108
+ x_anim_full_sweep: state?.x_anim_present === true && state?.x_anim_values === '-2;1000;-2' && state?.x_anim_dur === '23s' && state?.x_anim_repeat === 'indefinite',
109
+ vbeam_opacity_anim_ramped: state?.op_anim_present === true && state?.op_anim_values === '0;0.08;0.08;0' && state?.op_anim_dur === '23s' && state?.op_anim_keytimes === '0;0.05;0.95;1',
110
+ vbeam_a11y_title_present: state?.vtitle_text === 'canvas scan beam vertical · ambient sweep · 23s cycle',
111
+ vbeam_in_background_layer: state?.vbeam_before_nodes === true,
112
+ vbeam_after_horizontal_beam: state?.vbeam_after_hbeam === true,
113
+ hbeam_y_30s_preserved: state?.hbeam_y_dur === '30s' && state?.hbeam_y_values === '-2;680;-2',
114
+ hbeam_title_preserved: state?.htitle_text === 'canvas scan beam · ambient sweep · 30s cycle',
115
+ coprime_cadences_23_30: coprime_23_30,
116
+ r732_catalog_includes_vbeam: state?.catalog_includes_vbeam === true,
117
+ };
118
+ const ok = Object.values(results).every(Boolean);
119
+ console.log(`${ok ? '✅' : '❌'} R736 vertical scan beam (companion to R735, coprime cadences form ambient crosshair pair):`,
120
+ JSON.stringify(results, null, 2),
121
+ `\n state: ${JSON.stringify(state)}`,
122
+ `\n gcd(23, 30) = ${gcd(23, 30)}`);
123
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,100 @@
1
+ /* Round 737 — formalize the R735/R736 scan beam ambient pair as the
2
+ * 9TH entry in R717 patterns catalog. CROSS-FAMILY pattern entry:
3
+ * prior 8 patterns are all in the respiratory (breath) family;
4
+ * scan-beam-pair lives in the AMBIENT family but uses the same
5
+ * R717 "pattern axis" introspection vocabulary.
6
+ *
7
+ * Mirrors R724's pattern-formalization round (which added the
8
+ * triple-axis-pair entry after R721/R722 established the surfaces).
9
+ *
10
+ * Assertions:
11
+ * - R717 patterns has exactly 9 entries
12
+ * - `scan-beam-pair` entry exists with:
13
+ * - cadences [23, 30]
14
+ * - anchors ["scan beam horizontal", "scan beam vertical"]
15
+ * - shape "coprime-crosshair"
16
+ * - Coprime invariant: gcd(23, 30) === 1
17
+ * - Pattern cadences match the actual SMIL animate durs on the
18
+ * two beams (regression: R735 y-anim dur === 30s, R736 x-anim
19
+ * dur === 23s)
20
+ * - Pattern anchors map to actual data-attr selectors that resolve
21
+ * to live DOM elements (semantic anchor-to-element correspondence)
22
+ * - All other 8 R717 entries preserved (regression)
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 fresh = new Date(Date.now() - 60 * 1000).toISOString();
29
+
30
+ const browser = await chromium.launch({ headless: true });
31
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
32
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
33
+ await ctx.addInitScript(() => {
34
+ try {
35
+ localStorage.setItem('anet-theme', 'cyber');
36
+ localStorage.setItem('anet-topo-layout', 'ring');
37
+ sessionStorage.setItem('anet_v3_auth', '1');
38
+ } catch {}
39
+ });
40
+ await ctx.route('**/api/hub/status*', async (route) => {
41
+ const r = await route.fetch();
42
+ const b = await r.json();
43
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
44
+ const mk = (alias) => ({
45
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
46
+ network_id: nid, project_dir: null,
47
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
48
+ });
49
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('a·1'), mk('a·2')] } });
50
+ });
51
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
52
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
53
+ const page = await ctx.newPage();
54
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
55
+ await page.waitForSelector('[data-topo-canvas-aria]', { timeout: 15000, state: 'attached' });
56
+ await page.waitForTimeout(300);
57
+
58
+ const state = await page.evaluate(() => {
59
+ const svg = document.querySelector('[data-topo-canvas-aria]');
60
+ const patternsAttr = svg?.getAttribute('data-topo-respiratory-patterns') ?? null;
61
+ const hbeam = document.querySelector('[data-topo-canvas-scan-beam]');
62
+ const vbeam = document.querySelector('[data-topo-canvas-scan-beam-vertical]');
63
+ return {
64
+ patterns_attr: patternsAttr,
65
+ hbeam_y_dur: hbeam?.querySelector('animate[attributeName="y"]')?.getAttribute('dur') ?? null,
66
+ vbeam_x_dur: vbeam?.querySelector('animate[attributeName="x"]')?.getAttribute('dur') ?? null,
67
+ hbeam_present: !!hbeam,
68
+ vbeam_present: !!vbeam,
69
+ };
70
+ });
71
+
72
+ await browser.close();
73
+
74
+ let patterns = null;
75
+ try { patterns = JSON.parse(state.patterns_attr ?? ''); } catch {}
76
+
77
+ const sbpEntry = Array.isArray(patterns) ? patterns.find(p => p.name === 'scan-beam-pair') : null;
78
+ const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b));
79
+
80
+ const expectedNames = ['background', 'canvas-brand-pair', 'chrome-strip', 'panel-pair', 'scan-beam-pair', 'title-block', 'triple-axis-pair', 'triple-axis-pair-8s', 'triple-axis-tier'];
81
+ const actualNames = Array.isArray(patterns) ? patterns.map(p => p.name).sort() : [];
82
+
83
+ const results = {
84
+ patterns_has_9_entries: Array.isArray(patterns) && patterns.length === 9,
85
+ pattern_names_complete: JSON.stringify(actualNames) === JSON.stringify(expectedNames),
86
+ scan_beam_pair_entry_present: !!sbpEntry,
87
+ cadences_23_30: !!sbpEntry && JSON.stringify(sbpEntry.cadences) === JSON.stringify([23, 30]),
88
+ anchors_horizontal_vertical: !!sbpEntry && JSON.stringify(sbpEntry.anchors) === JSON.stringify(['scan beam horizontal', 'scan beam vertical']),
89
+ shape_coprime_crosshair: sbpEntry?.shape === 'coprime-crosshair',
90
+ coprime_invariant_holds: gcd(23, 30) === 1,
91
+ pattern_matches_hbeam_dur: state.hbeam_y_dur === '30s' && Array.isArray(sbpEntry?.cadences) && sbpEntry.cadences.includes(30),
92
+ pattern_matches_vbeam_dur: state.vbeam_x_dur === '23s' && Array.isArray(sbpEntry?.cadences) && sbpEntry.cadences.includes(23),
93
+ both_beams_in_dom: state.hbeam_present && state.vbeam_present,
94
+ };
95
+ const ok = Object.values(results).every(Boolean);
96
+ console.log(`${ok ? '✅' : '❌'} R737 R717 patterns +1: scan-beam-pair (9th entry, first cross-family pattern from the ambient layer):`,
97
+ JSON.stringify(results, null, 2),
98
+ `\n sbp entry: ${JSON.stringify(sbpEntry)}`,
99
+ `\n gcd(23, 30) = ${gcd(23, 30)}`);
100
+ process.exit(ok ? 0 : 1);
@@ -71,32 +71,38 @@ const validShape = Array.isArray(patterns)
71
71
  && typeof p.name === 'string' && p.name.length > 0
72
72
  && Array.isArray(p.cadences) && p.cadences.length > 0 && p.cadences.every(c => typeof c === 'number' && c > 0)
73
73
  && Array.isArray(p.anchors) && p.anchors.length > 0 && p.anchors.every(a => typeof a === 'string' && a.length > 0)
74
- && typeof p.shape === 'string' && ['trio-with-envelope', 'parity', 'tiered-with-trio', 'tiered-with-quartet', 'tiered-with-quintet', 'coprime-nested-pair', 'baseline-pair', '6s-triple-pair', '8s-triple-pair', 'tier-multi-cadence'].includes(p.shape))
74
+ && typeof p.shape === 'string' && ['trio-with-envelope', 'parity', 'tiered-with-trio', 'tiered-with-quartet', 'tiered-with-quintet', 'coprime-nested-pair', 'baseline-pair', '6s-triple-pair', '8s-triple-pair', 'tier-multi-cadence', 'coprime-crosshair'].includes(p.shape))
75
75
  : false;
76
76
 
77
77
  const totalAnchorCount = Array.isArray(patterns)
78
78
  ? patterns.reduce((acc, p) => acc + (Array.isArray(p.anchors) ? p.anchors.length : 0), 0)
79
79
  : 0;
80
80
 
81
- // Cross-check: every cadence in R717 patterns must appear in R710 rolodex
81
+ /* Cross-check: every cadence in BREATH-family patterns must appear
82
+ * in R710 rolodex. R737 introduced the first cross-family pattern
83
+ * entry (`scan-beam-pair` lives in the ambient family with cadences
84
+ * 23/30); the breath rolodex doesn't cover 30 (no breath anchor at
85
+ * 30s — rolodex max is 25 for R719 fullscreen). Exclude cross-family
86
+ * entries from the rolodex cross-check via name allowlist. */
87
+ const ambientFamilyPatternNames = new Set(['scan-beam-pair']);
82
88
  const rolodexCadences = rolodex ? new Set(Object.keys(rolodex).map(Number)) : new Set();
83
- const patternsCadences = Array.isArray(patterns)
84
- ? new Set(patterns.flatMap(p => p.cadences))
89
+ const breathPatternCadences = Array.isArray(patterns)
90
+ ? new Set(patterns.filter(p => !ambientFamilyPatternNames.has(p.name)).flatMap(p => p.cadences))
85
91
  : new Set();
86
- const allPatternCadencesInRolodex = [...patternsCadences].every(c => rolodexCadences.has(c));
92
+ const allPatternCadencesInRolodex = [...breathPatternCadences].every(c => rolodexCadences.has(c));
87
93
 
88
94
  const patternNames = Array.isArray(patterns) ? patterns.map(p => p.name).sort() : [];
89
- const expectedNames = ['background', 'canvas-brand-pair', 'chrome-strip', 'panel-pair', 'title-block', 'triple-axis-pair', 'triple-axis-pair-8s', 'triple-axis-tier'];
95
+ const expectedNames = ['background', 'canvas-brand-pair', 'chrome-strip', 'panel-pair', 'scan-beam-pair', 'title-block', 'triple-axis-pair', 'triple-axis-pair-8s', 'triple-axis-tier'];
90
96
 
91
97
  const results = {
92
- attr_present: !!runtimeAttrs.patterns,
93
- json_parses: patterns !== null && parseError === null,
94
- is_array: Array.isArray(patterns),
95
- has_8_entries: Array.isArray(patterns) && patterns.length === 8,
96
- pattern_names_match: JSON.stringify(patternNames) === JSON.stringify(expectedNames),
97
- shape_and_taxonomy_valid: validShape,
98
- total_anchors_count: totalAnchorCount >= 14 && totalAnchorCount <= 32, // R728 added 8s pair (+2) + extended tier (+2 entries 6 total) all of pair/tier members counted again
99
- all_cadences_in_rolodex: allPatternCadencesInRolodex,
98
+ attr_present: !!runtimeAttrs.patterns,
99
+ json_parses: patterns !== null && parseError === null,
100
+ is_array: Array.isArray(patterns),
101
+ has_9_entries: Array.isArray(patterns) && patterns.length === 9,
102
+ pattern_names_match: JSON.stringify(patternNames) === JSON.stringify(expectedNames),
103
+ shape_and_taxonomy_valid: validShape,
104
+ total_anchors_count: totalAnchorCount >= 14 && totalAnchorCount <= 34, // R737 +2 anchors (scan beam horizontal + vertical) added to the ambient family pattern entry
105
+ breath_cadences_in_rolodex: allPatternCadencesInRolodex,
100
106
  };
101
107
  const ok = Object.values(results).every(Boolean);
102
108
  console.log(`${ok ? '✅' : '❌'} R717 patterns catalog attr (3rd meta-doc — breath family triangle complete):`,