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

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/{071f-verpvy.l.js → 0-.th5fmz_k0s.js} +3 -3
  132. package/.next/static/chunks/{0u-kktt2jl42v.js → 00y5qffq1-qmu.js} +1 -1
  133. package/.next/static/chunks/{0scyt82phn6ko.js → 0cw_nwi91obqy.js} +1 -1
  134. package/.next/static/chunks/{115od_bzi_gud.js → 0kajnj3~yayxh.js} +1 -1
  135. package/.next/trace +2 -2
  136. package/.next/trace-build +1 -1
  137. package/app/components/TopoGraph.tsx +63 -1
  138. package/package.json +1 -1
  139. package/scripts/topo-a11y-titles-catalog-test.mjs +2 -2
  140. package/scripts/topo-canvas-scan-beam-diagonal-test.mjs +148 -0
  141. package/scripts/topo-respiratory-axis-count-stats-test.mjs +24 -1
  142. package/scripts/topo-respiratory-patterns-catalog-test.mjs +5 -5
  143. /package/.next/static/{2ivD7P_Y9t_aydinQpBz5 → ziizAcC6hi7lQU1brTS_G}/_buildManifest.js +0 -0
  144. /package/.next/static/{2ivD7P_Y9t_aydinQpBz5 → ziizAcC6hi7lQU1brTS_G}/_clientMiddlewareManifest.js +0 -0
  145. /package/.next/static/{2ivD7P_Y9t_aydinQpBz5 → ziizAcC6hi7lQU1brTS_G}/_ssgManifest.js +0 -0
@@ -0,0 +1,148 @@
1
+ /* Round 739 — diagonal scan beam (3rd member of ambient scan beam
2
+ * family). Completes the trio from pair:
3
+ * R735 horizontal beam 30 s (top → bottom)
4
+ * R736 vertical beam 23 s (left → right)
5
+ * R739 diagonal beam 19 s (NW → SE) ← this round
6
+ *
7
+ * Coprime invariants:
8
+ * gcd(19, 23) = 1
9
+ * gcd(19, 30) = 1
10
+ * gcd(23, 30) = 1
11
+ * → 3 cadences pairwise coprime → never phase-lock as a trio.
12
+ *
13
+ * Assertions:
14
+ * - <line data-topo-canvas-scan-beam-diagonal> exists with 4 SMIL
15
+ * <animate> children on x1/y1/x2/y2 (all 19s, all repeatCount=indefinite)
16
+ * - opacity animate 0 → 0.06 → 0 at keyTimes 0/0.05/0.95/1, dur 19s
17
+ * - SVG <title>: "canvas scan beam diagonal · ambient sweep · 19s cycle"
18
+ * - data-topo-canvas-scan-beam-diagonal-active="true"
19
+ * - diagonal beam in background layer (rendered before nodes)
20
+ * - diagonal beam rendered AFTER vertical beam (trio document order)
21
+ * - all 3 beams alive (R735 + R736 regressions)
22
+ * - all 3 cadences pairwise coprime (Euclid check)
23
+ * - R717 patterns has 10 entries (was 9)
24
+ * - R717 includes new `scan-beam-trio` with shape "coprime-trio"
25
+ * and cadences [19, 23, 30]
26
+ * - R729 stats: patterns_count 10, ambient_patterns 2, ambient_cadences 3
27
+ * - R732 a11y catalog: 9 entries incl. diagonal beam
28
+ */
29
+ import { chromium } from 'playwright';
30
+ import { readFileSync } from 'node:fs';
31
+
32
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
33
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
34
+
35
+ const browser = await chromium.launch({ headless: true });
36
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
37
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
38
+ await ctx.addInitScript(() => {
39
+ try {
40
+ localStorage.setItem('anet-theme', 'cyber');
41
+ localStorage.setItem('anet-topo-layout', 'ring');
42
+ sessionStorage.setItem('anet_v3_auth', '1');
43
+ } catch {}
44
+ });
45
+ await ctx.route('**/api/hub/status*', async (route) => {
46
+ const r = await route.fetch();
47
+ const b = await r.json();
48
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
49
+ const mk = (alias) => ({
50
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
51
+ network_id: nid, project_dir: null,
52
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
53
+ });
54
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('a·1'), mk('a·2')] } });
55
+ });
56
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
57
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
58
+ const page = await ctx.newPage();
59
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
60
+ await page.waitForSelector('[data-topo-canvas-scan-beam-diagonal]', { timeout: 15000, state: 'attached' });
61
+ await page.waitForTimeout(300);
62
+
63
+ const state = await page.evaluate(() => {
64
+ const dbeam = document.querySelector('[data-topo-canvas-scan-beam-diagonal]');
65
+ const vbeam = document.querySelector('[data-topo-canvas-scan-beam-vertical]');
66
+ const hbeam = document.querySelector('[data-topo-canvas-scan-beam]');
67
+ if (!dbeam) return null;
68
+ const getAnim = (el, attr) => el?.querySelector(`animate[attributeName="${attr}"]`);
69
+ const x1A = getAnim(dbeam, 'x1');
70
+ const y1A = getAnim(dbeam, 'y1');
71
+ const x2A = getAnim(dbeam, 'x2');
72
+ const y2A = getAnim(dbeam, 'y2');
73
+ const opA = getAnim(dbeam, 'opacity');
74
+ const dtitle = dbeam.querySelector(':scope > title');
75
+ const svg = document.querySelector('[data-topo-canvas-aria]');
76
+ const firstNode = svg?.querySelector('g[data-node]');
77
+ const dbeamBeforeNodes = dbeam && firstNode
78
+ ? (dbeam.compareDocumentPosition(firstNode) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0
79
+ : null;
80
+ const dbeamAfterVbeam = vbeam && dbeam
81
+ ? (vbeam.compareDocumentPosition(dbeam) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0
82
+ : null;
83
+ return {
84
+ dbeam_present: !!dbeam,
85
+ dbeam_active_attr: dbeam?.getAttribute('data-topo-canvas-scan-beam-diagonal-active') ?? null,
86
+ x1_dur: x1A?.getAttribute('dur'), x1_vals: x1A?.getAttribute('values'),
87
+ y1_dur: y1A?.getAttribute('dur'), y1_vals: y1A?.getAttribute('values'),
88
+ x2_dur: x2A?.getAttribute('dur'), x2_vals: x2A?.getAttribute('values'),
89
+ y2_dur: y2A?.getAttribute('dur'), y2_vals: y2A?.getAttribute('values'),
90
+ op_dur: opA?.getAttribute('dur'), op_vals: opA?.getAttribute('values'), op_kt: opA?.getAttribute('keyTimes'),
91
+ dtitle_text: dtitle?.textContent ?? null,
92
+ dbeam_before_nodes: dbeamBeforeNodes,
93
+ dbeam_after_vbeam: dbeamAfterVbeam,
94
+ hbeam_present: !!hbeam,
95
+ vbeam_present: !!vbeam,
96
+ patterns: svg?.getAttribute('data-topo-respiratory-patterns') ?? null,
97
+ stats: svg?.getAttribute('data-topo-respiratory-axis-count-stats') ?? null,
98
+ a11y: svg?.getAttribute('data-topo-a11y-titles') ?? null,
99
+ };
100
+ });
101
+
102
+ await browser.close();
103
+
104
+ const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b));
105
+ const coprime_trio = gcd(19, 23) === 1 && gcd(19, 30) === 1 && gcd(23, 30) === 1;
106
+
107
+ let patterns = null;
108
+ let stats = null;
109
+ let a11y = null;
110
+ try {
111
+ patterns = JSON.parse(state?.patterns ?? '');
112
+ stats = JSON.parse(state?.stats ?? '');
113
+ a11y = JSON.parse(state?.a11y ?? '');
114
+ } catch {}
115
+
116
+ const trioEntry = Array.isArray(patterns) ? patterns.find(p => p.name === 'scan-beam-trio') : null;
117
+ const trioCorrect = !!trioEntry
118
+ && JSON.stringify(trioEntry.cadences) === JSON.stringify([19, 23, 30])
119
+ && trioEntry.shape === 'coprime-trio'
120
+ && Array.isArray(trioEntry.anchors) && trioEntry.anchors.length === 3
121
+ && trioEntry.anchors.includes('scan beam diagonal');
122
+
123
+ const results = {
124
+ dbeam_line_present: state?.dbeam_present === true,
125
+ dbeam_active_true: state?.dbeam_active_attr === 'true',
126
+ x1_anim_full: state?.x1_dur === '19s' && state?.x1_vals === '-100;1100;-100',
127
+ y1_anim_full: state?.y1_dur === '19s' && state?.y1_vals === '-100;780;-100',
128
+ x2_anim_full: state?.x2_dur === '19s' && state?.x2_vals === '-30;1170;-30',
129
+ y2_anim_full: state?.y2_dur === '19s' && state?.y2_vals === '-30;850;-30',
130
+ op_anim_ramped: state?.op_dur === '19s' && state?.op_vals === '0;0.06;0.06;0' && state?.op_kt === '0;0.05;0.95;1',
131
+ dbeam_a11y_title: state?.dtitle_text === 'canvas scan beam diagonal · ambient sweep · 19s cycle',
132
+ dbeam_background_layer: state?.dbeam_before_nodes === true,
133
+ dbeam_after_vertical: state?.dbeam_after_vbeam === true,
134
+ all_three_beams_present: state?.dbeam_present && state?.vbeam_present && state?.hbeam_present,
135
+ coprime_trio_pairwise: coprime_trio,
136
+ r717_has_10_entries: Array.isArray(patterns) && patterns.length === 10,
137
+ r717_trio_entry_correct: trioCorrect,
138
+ r729_patterns_count_10: stats?.patterns_count === 10,
139
+ r729_ambient_patterns_2: stats?.ambient_patterns === 2,
140
+ r729_ambient_cadences_3: stats?.ambient_cadences === 3,
141
+ r732_a11y_has_9_entries: Array.isArray(a11y) && a11y.length === 9,
142
+ };
143
+ const ok = Object.values(results).every(Boolean);
144
+ console.log(`${ok ? '✅' : '❌'} R739 diagonal scan beam (3rd ambient member, completes scan-beam-trio with pairwise-coprime cadences):`,
145
+ JSON.stringify(results, null, 2),
146
+ `\n trio entry: ${JSON.stringify(trioEntry)}`,
147
+ `\n gcd: 19-23=${gcd(19,23)} 19-30=${gcd(19,30)} 23-30=${gcd(23,30)}`);
148
+ process.exit(ok ? 0 : 1);
@@ -89,7 +89,9 @@ try {
89
89
  parseError = String(e);
90
90
  }
91
91
 
92
- const expectedKeys = ['total_anchors', 'cadences', 'cadence_range_s', 'cadence_arc_s', 'axis_counts', 'patterns_count', 'tiers_count', 'triple_axis_pairs', 'triple_axis_solos'];
92
+ const expectedKeys = ['total_anchors', 'cadences', 'cadence_range_s', 'cadence_arc_s', 'axis_counts', 'patterns_count', 'tiers_count', 'triple_axis_pairs', 'triple_axis_solos',
93
+ /* R738 cross-family awareness fields */
94
+ 'breath_patterns', 'ambient_patterns', 'pattern_families', 'scan_beam_pairs', 'ambient_cadences'];
93
95
  const hasAllKeys = stats && expectedKeys.every(k => k in stats);
94
96
 
95
97
  const rolodexAnchorCount = rolodex && typeof rolodex === 'object'
@@ -103,6 +105,20 @@ const r717TriplePairCount = Array.isArray(patterns)
103
105
  ? patterns.filter(p => typeof p?.name === 'string' && p.name.startsWith('triple-axis-pair')).length
104
106
  : -1;
105
107
 
108
+ /* R738 — cross-family aware derivations from R717 patterns. The
109
+ * scan-beam-pair entry is the only ambient-family pattern today;
110
+ * everything else is breath. */
111
+ const ambientPatternNames = new Set(['scan-beam-pair']);
112
+ const breathPatternCount = Array.isArray(patterns)
113
+ ? patterns.filter(p => !ambientPatternNames.has(p?.name)).length : -1;
114
+ const ambientPatternCount = Array.isArray(patterns)
115
+ ? patterns.filter(p => ambientPatternNames.has(p?.name)).length : -1;
116
+ const scanBeamPairCount = Array.isArray(patterns)
117
+ ? patterns.filter(p => typeof p?.name === 'string' && p.name.startsWith('scan-beam-pair')).length : -1;
118
+ const ambientPatterns = Array.isArray(patterns)
119
+ ? patterns.filter(p => ambientPatternNames.has(p?.name)) : [];
120
+ const distinctAmbientCadences = new Set(ambientPatterns.flatMap(p => Array.isArray(p?.cadences) ? p.cadences : []));
121
+
106
122
  const axisCountSum = stats?.axis_counts
107
123
  ? (stats.axis_counts.single ?? 0) + (stats.axis_counts.dual ?? 0) + (stats.axis_counts.triple ?? 0)
108
124
  : -1;
@@ -122,6 +138,13 @@ const results = {
122
138
  tiers_count_matches_r720: stats?.tiers_count === (Array.isArray(tiers) ? tiers.length : -1),
123
139
  triple_axis_pairs_matches: stats?.triple_axis_pairs === r717TriplePairCount,
124
140
  dual_axis_tier_empty: stats?.axis_counts?.dual === 0,
141
+ /* R738 cross-family invariants */
142
+ breath_patterns_match_r717: stats?.breath_patterns === breathPatternCount,
143
+ ambient_patterns_match_r717: stats?.ambient_patterns === ambientPatternCount,
144
+ breath_plus_ambient_eq_total: (stats?.breath_patterns ?? -1) + (stats?.ambient_patterns ?? -2) === stats?.patterns_count,
145
+ pattern_families_eq_2: stats?.pattern_families === 2,
146
+ scan_beam_pairs_match_r717: stats?.scan_beam_pairs === scanBeamPairCount,
147
+ ambient_cadences_match_r717: stats?.ambient_cadences === distinctAmbientCadences.size,
125
148
  };
126
149
  const ok = Object.values(results).every(Boolean);
127
150
  console.log(`${ok ? '✅' : '❌'} R729 axis-count stats catalog (6th meta-doc — breath family hexagon):`,
@@ -71,7 +71,7 @@ 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', 'coprime-crosshair'].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', 'coprime-trio'].includes(p.shape))
75
75
  : false;
76
76
 
77
77
  const totalAnchorCount = Array.isArray(patterns)
@@ -84,7 +84,7 @@ const totalAnchorCount = Array.isArray(patterns)
84
84
  * 23/30); the breath rolodex doesn't cover 30 (no breath anchor at
85
85
  * 30s — rolodex max is 25 for R719 fullscreen). Exclude cross-family
86
86
  * entries from the rolodex cross-check via name allowlist. */
87
- const ambientFamilyPatternNames = new Set(['scan-beam-pair']);
87
+ const ambientFamilyPatternNames = new Set(['scan-beam-pair', 'scan-beam-trio']);
88
88
  const rolodexCadences = rolodex ? new Set(Object.keys(rolodex).map(Number)) : new Set();
89
89
  const breathPatternCadences = Array.isArray(patterns)
90
90
  ? new Set(patterns.filter(p => !ambientFamilyPatternNames.has(p.name)).flatMap(p => p.cadences))
@@ -92,16 +92,16 @@ const breathPatternCadences = Array.isArray(patterns)
92
92
  const allPatternCadencesInRolodex = [...breathPatternCadences].every(c => rolodexCadences.has(c));
93
93
 
94
94
  const patternNames = Array.isArray(patterns) ? patterns.map(p => p.name).sort() : [];
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'];
95
+ const expectedNames = ['background', 'canvas-brand-pair', 'chrome-strip', 'panel-pair', 'scan-beam-pair', 'scan-beam-trio', 'title-block', 'triple-axis-pair', 'triple-axis-pair-8s', 'triple-axis-tier'];
96
96
 
97
97
  const results = {
98
98
  attr_present: !!runtimeAttrs.patterns,
99
99
  json_parses: patterns !== null && parseError === null,
100
100
  is_array: Array.isArray(patterns),
101
- has_9_entries: Array.isArray(patterns) && patterns.length === 9,
101
+ has_10_entries: Array.isArray(patterns) && patterns.length === 10,
102
102
  pattern_names_match: JSON.stringify(patternNames) === JSON.stringify(expectedNames),
103
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
104
+ total_anchors_count: totalAnchorCount >= 14 && totalAnchorCount <= 38, // R739 +3 anchors (scan-beam-trio adds 3 anchors)
105
105
  breath_cadences_in_rolodex: allPatternCadencesInRolodex,
106
106
  };
107
107
  const ok = Object.values(results).every(Boolean);