agentxchain 2.155.35 → 2.155.36

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentxchain",
3
- "version": "2.155.35",
3
+ "version": "2.155.36",
4
4
  "description": "CLI for AgentXchain — governed multi-agent software delivery",
5
5
  "type": "module",
6
6
  "bin": {
@@ -677,14 +677,25 @@ export async function executeGovernedRun(context, opts = {}) {
677
677
  if (exportResult.ok) {
678
678
  const runId = result.state.run_id || 'unknown';
679
679
  const exportPath = join(reportsDir, `export-${runId}.json`);
680
- writeFileSync(exportPath, JSON.stringify(exportResult.export, null, 2));
681
680
 
682
- const reportResult = buildGovernanceReport(exportResult.export, { input: exportPath });
683
- const reportPath = join(reportsDir, `report-${runId}.md`);
684
- writeFileSync(reportPath, formatGovernanceReportMarkdown(reportResult.report));
685
-
686
- log('');
687
- log(chalk.dim(` Governance report: .agentxchain/reports/report-${runId}.md`));
681
+ // Write export JSON compact format to avoid string-length overflow (BUG-84)
682
+ try {
683
+ writeFileSync(exportPath, JSON.stringify(exportResult.export));
684
+ } catch (exportWriteErr) {
685
+ log(chalk.dim(` Governance export write failed: ${exportWriteErr.message}`));
686
+ }
687
+
688
+ // Generate markdown report separately so export-write failure doesn't block it
689
+ try {
690
+ const reportResult = buildGovernanceReport(exportResult.export, { input: exportPath });
691
+ const reportPath = join(reportsDir, `report-${runId}.md`);
692
+ writeFileSync(reportPath, formatGovernanceReportMarkdown(reportResult.report));
693
+
694
+ log('');
695
+ log(chalk.dim(` Governance report: .agentxchain/reports/report-${runId}.md`));
696
+ } catch (reportErr) {
697
+ log(chalk.dim(` Governance report failed: ${reportErr.message}`));
698
+ }
688
699
  } else {
689
700
  log(chalk.dim(` Governance report skipped: ${exportResult.error}`));
690
701
  }
package/src/lib/report.js CHANGED
@@ -15,6 +15,15 @@ import { extractGateActionDigest } from './gate-actions.js';
15
15
 
16
16
  export const GOVERNANCE_REPORT_VERSION = '0.1';
17
17
 
18
+ // BUG-84: bounded report sections to prevent Invalid string length on large sessions
19
+ export const MAX_REPORT_SECTION_ITEMS = 500;
20
+
21
+ export function boundedSlice(arr, max = MAX_REPORT_SECTION_ITEMS) {
22
+ if (!Array.isArray(arr)) return { items: [], omitted: 0 };
23
+ if (arr.length <= max) return { items: arr, omitted: 0 };
24
+ return { items: arr.slice(0, max), omitted: arr.length - max };
25
+ }
26
+
18
27
  const VALID_DELEGATION_OUTCOMES = new Set(['completed', 'failed', 'mixed', 'pending']);
19
28
  const VALID_DASHBOARD_SESSION_STATUSES = new Set(['running', 'pid_only', 'stale', 'not_running']);
20
29
 
@@ -1401,21 +1410,25 @@ export function formatGovernanceReportText(report) {
1401
1410
  }
1402
1411
 
1403
1412
  if (run.turns && run.turns.length > 0) {
1413
+ const { items: boundedTurns, omitted: turnsOmitted } = boundedSlice(run.turns);
1404
1414
  lines.push('', 'Turn Timeline:');
1405
- for (let i = 0; i < run.turns.length; i++) {
1406
- const t = run.turns[i];
1415
+ for (let i = 0; i < boundedTurns.length; i++) {
1416
+ const t = boundedTurns[i];
1407
1417
  const cost = t.cost_usd != null ? formatUsd(t.cost_usd) : 'n/a';
1408
1418
  const phase = t.phase_transition ? `${t.phase || '?'} -> ${t.phase_transition}` : (t.phase || '?');
1409
1419
  const siblingNote = Array.isArray(t.sibling_attributed_files) ? ` (${t.sibling_attributed_files.length} sibling-attributed)` : '';
1410
1420
  lines.push(` ${i + 1}. [${t.role}] ${t.summary || '(no summary)'} | phase: ${phase} | files: ${t.files_changed_count}${siblingNote} | cost: ${cost} | ${formatTurnTimelineTime(t)}`);
1411
1421
  }
1422
+ if (turnsOmitted > 0) lines.push(` (${turnsOmitted} more turns omitted)`);
1412
1423
  }
1413
1424
 
1414
1425
  if (run.decisions && run.decisions.length > 0) {
1426
+ const { items: boundedDecs, omitted: decsOmitted } = boundedSlice(run.decisions);
1415
1427
  lines.push('', 'Decisions:');
1416
- for (const d of run.decisions) {
1428
+ for (const d of boundedDecs) {
1417
1429
  lines.push(` - ${d.id} (${d.role || '?'}, ${d.phase || '?'}): ${d.statement}`);
1418
1430
  }
1431
+ if (decsOmitted > 0) lines.push(` (${decsOmitted} more decisions omitted)`);
1419
1432
  }
1420
1433
 
1421
1434
  if (run.gate_summary && run.gate_summary.length > 0) {
@@ -1426,8 +1439,9 @@ export function formatGovernanceReportText(report) {
1426
1439
  }
1427
1440
 
1428
1441
  if (run.gate_failures && run.gate_failures.length > 0) {
1442
+ const { items: boundedGF, omitted: gfOmitted } = boundedSlice(run.gate_failures);
1429
1443
  lines.push('', 'Gate Failures:');
1430
- for (const failure of run.gate_failures) {
1444
+ for (const failure of boundedGF) {
1431
1445
  const request = failure.gate_type === 'run_completion'
1432
1446
  ? 'run completion'
1433
1447
  : `${failure.from_phase || failure.phase || '?'} -> ${failure.to_phase || '?'}`;
@@ -1437,11 +1451,13 @@ export function formatGovernanceReportText(report) {
1437
1451
  lines.push(` reason: ${reason}`);
1438
1452
  }
1439
1453
  }
1454
+ if (gfOmitted > 0) lines.push(` (${gfOmitted} more gate failures omitted)`);
1440
1455
  }
1441
1456
 
1442
1457
  if (run.gate_actions && run.gate_actions.length > 0) {
1458
+ const { items: boundedGA, omitted: gaOmitted } = boundedSlice(run.gate_actions);
1443
1459
  lines.push('', 'Gate Actions:');
1444
- for (const action of run.gate_actions) {
1460
+ for (const action of boundedGA) {
1445
1461
  const label = action.action_label || action.command || `action ${action.action_index || '?'}`;
1446
1462
  const exit = action.exit_code == null ? 'n/a' : String(action.exit_code);
1447
1463
  const timeoutTag = action.timed_out ? ` | timed_out after ${action.timeout_ms}ms` : '';
@@ -1450,11 +1466,13 @@ export function formatGovernanceReportText(report) {
1450
1466
  lines.push(` stderr: ${action.stderr_tail}`);
1451
1467
  }
1452
1468
  }
1469
+ if (gaOmitted > 0) lines.push(` (${gaOmitted} more gate actions omitted)`);
1453
1470
  }
1454
1471
 
1455
1472
  if (run.approval_policy_events && run.approval_policy_events.length > 0) {
1473
+ const { items: boundedAP, omitted: apOmitted } = boundedSlice(run.approval_policy_events);
1456
1474
  lines.push('', 'Approval Policy:');
1457
- for (const evt of run.approval_policy_events) {
1475
+ for (const evt of boundedAP) {
1458
1476
  const transition = evt.gate_type === 'run_completion'
1459
1477
  ? 'run completion'
1460
1478
  : `${evt.from_phase || '?'} -> ${evt.to_phase || '?'}`;
@@ -1462,19 +1480,23 @@ export function formatGovernanceReportText(report) {
1462
1480
  lines.push(` - ${evt.action || 'unknown'} | ${evt.gate_type || 'unknown'} | ${transition}${rule} | at: ${evt.timestamp || 'n/a'}`);
1463
1481
  if (evt.reason) lines.push(` reason: ${evt.reason}`);
1464
1482
  }
1483
+ if (apOmitted > 0) lines.push(` (${apOmitted} more approval policy events omitted)`);
1465
1484
  }
1466
1485
 
1467
1486
  if (run.governance_events && run.governance_events.length > 0) {
1487
+ const { items: boundedGE, omitted: geOmitted } = boundedSlice(run.governance_events);
1468
1488
  lines.push('', 'Governance Events:');
1469
- for (const evt of run.governance_events) {
1489
+ for (const evt of boundedGE) {
1470
1490
  lines.push(` - ${evt.type} | ${evt.role || '?'} | ${evt.phase || '?'} | at: ${evt.timestamp || 'n/a'}`);
1471
1491
  renderGovernanceEventDetailText(lines, evt, ' ');
1472
1492
  }
1493
+ if (geOmitted > 0) lines.push(` (${geOmitted} more governance events omitted)`);
1473
1494
  }
1474
1495
 
1475
1496
  if (run.timeout_events && run.timeout_events.length > 0) {
1497
+ const { items: boundedTE, omitted: teOmitted } = boundedSlice(run.timeout_events);
1476
1498
  lines.push('', 'Timeout Events:');
1477
- for (const evt of run.timeout_events) {
1499
+ for (const evt of boundedTE) {
1478
1500
  const label = evt.type === 'timeout_warning' ? 'warning'
1479
1501
  : evt.type === 'timeout_skip' ? 'skip'
1480
1502
  : evt.type === 'timeout_skip_failed' ? 'skip failed'
@@ -1484,6 +1506,7 @@ export function formatGovernanceReportText(report) {
1484
1506
  const exceeded = evt.exceeded_by_minutes != null ? `+${evt.exceeded_by_minutes}m` : '';
1485
1507
  lines.push(` - ${label} | ${evt.scope || '?'} scope | ${elapsed}/${limit}${exceeded ? ` (${exceeded})` : ''} | action: ${evt.action || 'n/a'} | phase: ${evt.phase || 'n/a'} | at: ${evt.timestamp || 'n/a'}`);
1486
1508
  }
1509
+ if (teOmitted > 0) lines.push(` (${teOmitted} more timeout events omitted)`);
1487
1510
  }
1488
1511
 
1489
1512
  if (run.intake_links && run.intake_links.length > 0) {
@@ -1630,21 +1653,25 @@ export function formatGovernanceReportText(report) {
1630
1653
  }
1631
1654
 
1632
1655
  if (coordinator_timeline && coordinator_timeline.length > 0) {
1656
+ const { items: boundedCT, omitted: ctOmitted } = boundedSlice(coordinator_timeline);
1633
1657
  lines.push('', 'Coordinator Timeline:');
1634
- for (let i = 0; i < coordinator_timeline.length; i++) {
1635
- const ev = coordinator_timeline[i];
1658
+ for (let i = 0; i < boundedCT.length; i++) {
1659
+ const ev = boundedCT[i];
1636
1660
  const ts = ev.timestamp ? ` [${ev.timestamp}]` : '';
1637
1661
  lines.push(` ${i + 1}. [${ev.type}]${ts} ${ev.summary}`);
1638
1662
  }
1663
+ if (ctOmitted > 0) lines.push(` (${ctOmitted} more coordinator events omitted)`);
1639
1664
  }
1640
1665
 
1641
1666
  const aggregated_event_timeline = report.subject.aggregated_event_timeline;
1642
1667
  if (aggregated_event_timeline && aggregated_event_timeline.length > 0) {
1668
+ const { items: boundedAET, omitted: aetOmitted } = boundedSlice(aggregated_event_timeline);
1643
1669
  lines.push('', 'Aggregated Child Repo Events:');
1644
- for (const evt of aggregated_event_timeline) {
1670
+ for (const evt of boundedAET) {
1645
1671
  const ts = evt.timestamp ? ` [${evt.timestamp}]` : '';
1646
1672
  lines.push(` [${evt.repo_id || '?'}] ${evt.type}${ts}`);
1647
1673
  }
1674
+ if (aetOmitted > 0) lines.push(` (${aetOmitted} more child repo events omitted)`);
1648
1675
  } else {
1649
1676
  lines.push('', 'Aggregated Child Repo Events:', ' No child repo events.');
1650
1677
  }
@@ -1679,15 +1706,18 @@ export function formatGovernanceReportText(report) {
1679
1706
  }
1680
1707
 
1681
1708
  if (decision_digest && decision_digest.length > 0) {
1709
+ const { items: boundedDD, omitted: ddOmitted } = boundedSlice(decision_digest);
1682
1710
  lines.push('', 'Coordinator Decisions:');
1683
- for (const d of decision_digest) {
1711
+ for (const d of boundedDD) {
1684
1712
  lines.push(` - ${d.id} (${d.role || '?'}, ${d.phase || '?'}): ${d.statement}`);
1685
1713
  }
1714
+ if (ddOmitted > 0) lines.push(` (${ddOmitted} more decisions omitted)`);
1686
1715
  }
1687
1716
 
1688
1717
  if (approval_policy_events && approval_policy_events.length > 0) {
1718
+ const { items: boundedAP, omitted: apOmitted } = boundedSlice(approval_policy_events);
1689
1719
  lines.push('', 'Approval Policy:');
1690
- for (const evt of approval_policy_events) {
1720
+ for (const evt of boundedAP) {
1691
1721
  const transition = evt.gate_type === 'run_completion'
1692
1722
  ? 'run completion'
1693
1723
  : `${evt.from_phase || '?'} -> ${evt.to_phase || '?'}`;
@@ -1695,19 +1725,23 @@ export function formatGovernanceReportText(report) {
1695
1725
  lines.push(` - ${evt.action || 'unknown'} | ${evt.gate_type || 'unknown'} | ${transition}${rule} | at: ${evt.timestamp || 'n/a'}`);
1696
1726
  if (evt.reason) lines.push(` reason: ${evt.reason}`);
1697
1727
  }
1728
+ if (apOmitted > 0) lines.push(` (${apOmitted} more approval policy events omitted)`);
1698
1729
  }
1699
1730
 
1700
1731
  if (governance_events && governance_events.length > 0) {
1732
+ const { items: boundedGE, omitted: geOmitted } = boundedSlice(governance_events);
1701
1733
  lines.push('', 'Governance Events:');
1702
- for (const evt of governance_events) {
1734
+ for (const evt of boundedGE) {
1703
1735
  lines.push(` - ${evt.type} | ${evt.role || '?'} | ${evt.phase || '?'} | at: ${evt.timestamp || 'n/a'}`);
1704
1736
  renderGovernanceEventDetailText(lines, evt, ' ');
1705
1737
  }
1738
+ if (geOmitted > 0) lines.push(` (${geOmitted} more governance events omitted)`);
1706
1739
  }
1707
1740
 
1708
1741
  if (timeout_events && timeout_events.length > 0) {
1742
+ const { items: boundedTE, omitted: teOmitted } = boundedSlice(timeout_events);
1709
1743
  lines.push('', 'Timeout Events:');
1710
- for (const evt of timeout_events) {
1744
+ for (const evt of boundedTE) {
1711
1745
  const label = evt.type === 'timeout_warning' ? 'warning'
1712
1746
  : evt.type === 'timeout_skip' ? 'skip'
1713
1747
  : evt.type === 'timeout_skip_failed' ? 'skip failed'
@@ -1717,6 +1751,7 @@ export function formatGovernanceReportText(report) {
1717
1751
  const exceeded = evt.exceeded_by_minutes != null ? `+${evt.exceeded_by_minutes}m` : '';
1718
1752
  lines.push(` - ${label} | ${evt.scope || '?'} scope | ${elapsed}/${limit}${exceeded ? ` (${exceeded})` : ''} | action: ${evt.action || 'n/a'} | phase: ${evt.phase || 'n/a'} | at: ${evt.timestamp || 'n/a'}`);
1719
1753
  }
1754
+ if (teOmitted > 0) lines.push(` (${teOmitted} more timeout events omitted)`);
1720
1755
  }
1721
1756
 
1722
1757
  if (recovery_report) {
@@ -1972,22 +2007,26 @@ export function formatGovernanceReportMarkdown(report) {
1972
2007
  }
1973
2008
 
1974
2009
  if (run.turns && run.turns.length > 0) {
2010
+ const { items: boundedTurns, omitted: turnsOmitted } = boundedSlice(run.turns);
1975
2011
  lines.push('', '## Turn Timeline', '', '| # | Role | Phase | Summary | Files | Cost | Time |', '|---|------|-------|---------|-------|------|------|');
1976
- for (let i = 0; i < run.turns.length; i++) {
1977
- const t = run.turns[i];
2012
+ for (let i = 0; i < boundedTurns.length; i++) {
2013
+ const t = boundedTurns[i];
1978
2014
  const cost = t.cost_usd != null ? formatUsd(t.cost_usd) : 'n/a';
1979
2015
  const phase = t.phase_transition ? `${t.phase || '?'} → ${t.phase_transition}` : (t.phase || '?');
1980
2016
  const summary = (t.summary || '(no summary)').replace(/\|/g, '\\|');
1981
2017
  const siblingNote = Array.isArray(t.sibling_attributed_files) ? ` (${t.sibling_attributed_files.length} sibling)` : '';
1982
2018
  lines.push(`| ${i + 1} | ${t.role} | ${phase} | ${summary} | ${t.files_changed_count}${siblingNote} | ${cost} | ${formatTurnTimelineTime(t).replace(/\|/g, '\\|')} |`);
1983
2019
  }
2020
+ if (turnsOmitted > 0) lines.push('', `*(${turnsOmitted} more turns omitted)*`);
1984
2021
  }
1985
2022
 
1986
2023
  if (run.decisions && run.decisions.length > 0) {
2024
+ const { items: boundedDecs, omitted: decsOmitted } = boundedSlice(run.decisions);
1987
2025
  lines.push('', '## Decisions', '');
1988
- for (const d of run.decisions) {
2026
+ for (const d of boundedDecs) {
1989
2027
  lines.push(`- **${d.id}** (${d.role || '?'}, ${d.phase || '?'} phase): ${d.statement}`);
1990
2028
  }
2029
+ if (decsOmitted > 0) lines.push('', `*(${decsOmitted} more decisions omitted)*`);
1991
2030
  }
1992
2031
 
1993
2032
  if (run.gate_summary && run.gate_summary.length > 0) {
@@ -1998,8 +2037,9 @@ export function formatGovernanceReportMarkdown(report) {
1998
2037
  }
1999
2038
 
2000
2039
  if (run.gate_failures && run.gate_failures.length > 0) {
2040
+ const { items: boundedGF, omitted: gfOmitted } = boundedSlice(run.gate_failures);
2001
2041
  lines.push('', '## Gate Failures', '');
2002
- for (const failure of run.gate_failures) {
2042
+ for (const failure of boundedGF) {
2003
2043
  const request = failure.gate_type === 'run_completion'
2004
2044
  ? 'run completion'
2005
2045
  : `${failure.from_phase || failure.phase || '?'} → ${failure.to_phase || '?'}`;
@@ -2008,24 +2048,28 @@ export function formatGovernanceReportMarkdown(report) {
2008
2048
  lines.push(` - ${reason}`);
2009
2049
  }
2010
2050
  }
2051
+ if (gfOmitted > 0) lines.push('', `*(${gfOmitted} more gate failures omitted)*`);
2011
2052
  }
2012
2053
 
2013
2054
  if (run.gate_actions && run.gate_actions.length > 0) {
2055
+ const { items: boundedGA, omitted: gaOmitted } = boundedSlice(run.gate_actions);
2014
2056
  lines.push('', '## Gate Actions', '');
2015
- for (const action of run.gate_actions) {
2057
+ for (const action of boundedGA) {
2016
2058
  const label = action.action_label || action.command || `action ${action.action_index || '?'}`;
2017
2059
  const exit = action.exit_code == null ? 'n/a' : String(action.exit_code);
2018
- const mdTimeout = action.timed_out ? ` timed out after ${action.timeout_ms}ms` : '';
2060
+ const mdTimeout = action.timed_out ? ` timed out after ${action.timeout_ms}ms` : '';
2019
2061
  lines.push(`- \`${action.gate_id || 'unknown'}\` (${action.gate_type || 'unknown'}) action ${action.action_index || '?'} — **${action.status}** at \`${action.timestamp || 'n/a'}\`: ${label} (exit \`${exit}\`)${mdTimeout}`);
2020
2062
  if (action.stderr_tail) {
2021
2063
  lines.push(` - stderr: ${action.stderr_tail}`);
2022
2064
  }
2023
2065
  }
2066
+ if (gaOmitted > 0) lines.push('', `*(${gaOmitted} more gate actions omitted)*`);
2024
2067
  }
2025
2068
 
2026
2069
  if (run.approval_policy_events && run.approval_policy_events.length > 0) {
2070
+ const { items: boundedAP, omitted: apOmitted } = boundedSlice(run.approval_policy_events);
2027
2071
  lines.push('', '## Approval Policy', '');
2028
- for (const evt of run.approval_policy_events) {
2072
+ for (const evt of boundedAP) {
2029
2073
  const transition = evt.gate_type === 'run_completion'
2030
2074
  ? 'run completion'
2031
2075
  : `${evt.from_phase || '?'} → ${evt.to_phase || '?'}`;
@@ -2033,19 +2077,23 @@ export function formatGovernanceReportMarkdown(report) {
2033
2077
  lines.push(`- **${evt.action || 'unknown'}** (${evt.gate_type || 'unknown'}) ${transition}${rule} at \`${evt.timestamp || 'n/a'}\``);
2034
2078
  if (evt.reason) lines.push(` - ${evt.reason}`);
2035
2079
  }
2080
+ if (apOmitted > 0) lines.push('', `*(${apOmitted} more approval policy events omitted)*`);
2036
2081
  }
2037
2082
 
2038
2083
  if (run.governance_events && run.governance_events.length > 0) {
2084
+ const { items: boundedGE, omitted: geOmitted } = boundedSlice(run.governance_events);
2039
2085
  lines.push('', '## Governance Events', '');
2040
- for (const evt of run.governance_events) {
2086
+ for (const evt of boundedGE) {
2041
2087
  lines.push(`- **${evt.type}** (\`${evt.role || '?'}\`, \`${evt.phase || '?'}\` phase) at \`${evt.timestamp || 'n/a'}\``);
2042
2088
  renderGovernanceEventDetailMarkdown(lines, evt);
2043
2089
  }
2090
+ if (geOmitted > 0) lines.push('', `*(${geOmitted} more governance events omitted)*`);
2044
2091
  }
2045
2092
 
2046
2093
  if (run.timeout_events && run.timeout_events.length > 0) {
2094
+ const { items: boundedTE, omitted: teOmitted } = boundedSlice(run.timeout_events);
2047
2095
  lines.push('', '## Timeout Events', '');
2048
- for (const evt of run.timeout_events) {
2096
+ for (const evt of boundedTE) {
2049
2097
  const label = evt.type === 'timeout_warning' ? 'Warning'
2050
2098
  : evt.type === 'timeout_skip' ? 'Skip'
2051
2099
  : evt.type === 'timeout_skip_failed' ? 'Skip Failed'
@@ -2055,6 +2103,7 @@ export function formatGovernanceReportMarkdown(report) {
2055
2103
  const exceeded = evt.exceeded_by_minutes != null ? ` (+${evt.exceeded_by_minutes}m)` : '';
2056
2104
  lines.push(`- **${label}** (\`${evt.scope || '?'}\` scope) — ${elapsed}/${limit}${exceeded}, action: \`${evt.action || 'n/a'}\`, phase: \`${evt.phase || 'n/a'}\` at \`${evt.timestamp || 'n/a'}\``);
2057
2105
  }
2106
+ if (teOmitted > 0) lines.push('', `*(${teOmitted} more timeout events omitted)*`);
2058
2107
  }
2059
2108
 
2060
2109
  if (run.intake_links && run.intake_links.length > 0) {
@@ -2684,13 +2733,15 @@ function renderRunHtml(report) {
2684
2733
 
2685
2734
  // Turn Timeline
2686
2735
  if (run.turns && run.turns.length > 0) {
2687
- const turnRows = run.turns.map((t, i) => {
2736
+ const { items: boundedTurns, omitted: turnsOmitted } = boundedSlice(run.turns);
2737
+ const turnRows = boundedTurns.map((t, i) => {
2688
2738
  const cost = t.cost_usd != null ? formatUsd(t.cost_usd) : 'n/a';
2689
2739
  const phase = t.phase_transition ? `${esc(t.phase || '?')} &rarr; ${esc(t.phase_transition)}` : esc(t.phase || '?');
2690
2740
  const sibNote = Array.isArray(t.sibling_attributed_files) ? ` (${t.sibling_attributed_files.length} sibling)` : '';
2691
2741
  return [String(i + 1), esc(t.role), phase, esc(t.summary || '(no summary)'), `${t.files_changed_count}${sibNote}`, cost, esc(formatTurnTimelineTime(t))];
2692
2742
  });
2693
- sections.push(`<div class="section">${htmlSection('Turn Timeline', htmlTable(['#', 'Role', 'Phase', 'Summary', 'Files', 'Cost', 'Time'], turnRows))}</div>`);
2743
+ const omitNote = turnsOmitted > 0 ? `<p><em>(${turnsOmitted} more turns omitted)</em></p>` : '';
2744
+ sections.push(`<div class="section">${htmlSection('Turn Timeline', htmlTable(['#', 'Role', 'Phase', 'Summary', 'Files', 'Cost', 'Time'], turnRows) + omitNote)}</div>`);
2694
2745
  }
2695
2746
 
2696
2747
  // Decisions
@@ -2707,70 +2758,80 @@ function renderRunHtml(report) {
2707
2758
 
2708
2759
  // Gate Failures
2709
2760
  if (run.gate_failures && run.gate_failures.length > 0) {
2710
- let gfHtml = '<ul>';
2711
- for (const failure of run.gate_failures) {
2761
+ const { items: boundedGF, omitted: gfOmitted } = boundedSlice(run.gate_failures);
2762
+ const gfParts = ['<ul>'];
2763
+ for (const failure of boundedGF) {
2712
2764
  const request = failure.gate_type === 'run_completion' ? 'run completion' : `${esc(failure.from_phase || failure.phase || '?')} &rarr; ${esc(failure.to_phase || '?')}`;
2713
- gfHtml += `<li><code>${esc(failure.gate_id || 'unknown')}</code> (${esc(failure.gate_type || 'unknown')}) at <code>${esc(failure.failed_at || 'n/a')}</code>: ${request}`;
2765
+ gfParts.push(`<li><code>${esc(failure.gate_id || 'unknown')}</code> (${esc(failure.gate_type || 'unknown')}) at <code>${esc(failure.failed_at || 'n/a')}</code>: ${request}`);
2714
2766
  if (failure.reasons?.length) {
2715
- gfHtml += '<ul>' + failure.reasons.map((r) => `<li>${esc(r)}</li>`).join('') + '</ul>';
2767
+ gfParts.push('<ul>' + failure.reasons.map((r) => `<li>${esc(r)}</li>`).join('') + '</ul>');
2716
2768
  }
2717
- gfHtml += '</li>';
2769
+ gfParts.push('</li>');
2718
2770
  }
2719
- gfHtml += '</ul>';
2720
- sections.push(`<div class="section">${htmlSection('Gate Failures', gfHtml)}</div>`);
2771
+ gfParts.push('</ul>');
2772
+ if (gfOmitted > 0) gfParts.push(`<p><em>(${gfOmitted} more gate failures omitted)</em></p>`);
2773
+ sections.push(`<div class="section">${htmlSection('Gate Failures', gfParts.join(''))}</div>`);
2721
2774
  }
2722
2775
 
2723
2776
  if (run.gate_actions && run.gate_actions.length > 0) {
2724
- let gaHtml = '<ul>';
2725
- for (const action of run.gate_actions) {
2777
+ const { items: boundedGA, omitted: gaOmitted } = boundedSlice(run.gate_actions);
2778
+ const gaParts = ['<ul>'];
2779
+ for (const action of boundedGA) {
2726
2780
  const label = action.action_label || action.command || `action ${action.action_index || '?'}`;
2727
2781
  const exit = action.exit_code == null ? 'n/a' : String(action.exit_code);
2728
- const htmlTimeout = action.timed_out ? ` <em>⏱ timed out after ${esc(String(action.timeout_ms))}ms</em>` : '';
2729
- gaHtml += `<li><code>${esc(action.gate_id || 'unknown')}</code> (${esc(action.gate_type || 'unknown')}) action ${esc(String(action.action_index || '?'))} — <strong>${esc(action.status)}</strong> at <code>${esc(action.timestamp || 'n/a')}</code>: ${esc(label)} (exit <code>${esc(exit)}</code>)${htmlTimeout}`;
2782
+ const htmlTimeout = action.timed_out ? ` <em>timed out after ${esc(String(action.timeout_ms))}ms</em>` : '';
2783
+ gaParts.push(`<li><code>${esc(action.gate_id || 'unknown')}</code> (${esc(action.gate_type || 'unknown')}) action ${esc(String(action.action_index || '?'))} — <strong>${esc(action.status)}</strong> at <code>${esc(action.timestamp || 'n/a')}</code>: ${esc(label)} (exit <code>${esc(exit)}</code>)${htmlTimeout}`);
2730
2784
  if (action.stderr_tail) {
2731
- gaHtml += `<br><code>${esc(action.stderr_tail)}</code>`;
2785
+ gaParts.push(`<br><code>${esc(action.stderr_tail)}</code>`);
2732
2786
  }
2733
- gaHtml += '</li>';
2787
+ gaParts.push('</li>');
2734
2788
  }
2735
- gaHtml += '</ul>';
2736
- sections.push(`<div class="section">${htmlSection('Gate Actions', gaHtml)}</div>`);
2789
+ gaParts.push('</ul>');
2790
+ if (gaOmitted > 0) gaParts.push(`<p><em>(${gaOmitted} more gate actions omitted)</em></p>`);
2791
+ sections.push(`<div class="section">${htmlSection('Gate Actions', gaParts.join(''))}</div>`);
2737
2792
  }
2738
2793
 
2739
2794
  // Approval Policy
2740
2795
  if (run.approval_policy_events && run.approval_policy_events.length > 0) {
2741
- let apHtml = '<ul>';
2742
- for (const evt of run.approval_policy_events) {
2796
+ const { items: boundedAP, omitted: apOmitted } = boundedSlice(run.approval_policy_events);
2797
+ const apParts = ['<ul>'];
2798
+ for (const evt of boundedAP) {
2743
2799
  const transition = evt.gate_type === 'run_completion' ? 'run completion' : `${esc(evt.from_phase || '?')} &rarr; ${esc(evt.to_phase || '?')}`;
2744
- apHtml += `<li><strong>${esc(evt.action || 'unknown')}</strong> (${esc(evt.gate_type || 'unknown')}) ${transition} at <code>${esc(evt.timestamp || 'n/a')}</code>`;
2745
- if (evt.reason) apHtml += `<br>${esc(evt.reason)}`;
2746
- apHtml += '</li>';
2800
+ apParts.push(`<li><strong>${esc(evt.action || 'unknown')}</strong> (${esc(evt.gate_type || 'unknown')}) ${transition} at <code>${esc(evt.timestamp || 'n/a')}</code>`);
2801
+ if (evt.reason) apParts.push(`<br>${esc(evt.reason)}`);
2802
+ apParts.push('</li>');
2747
2803
  }
2748
- apHtml += '</ul>';
2749
- sections.push(`<div class="section">${htmlSection('Approval Policy', apHtml)}</div>`);
2804
+ apParts.push('</ul>');
2805
+ if (apOmitted > 0) apParts.push(`<p><em>(${apOmitted} more approval policy events omitted)</em></p>`);
2806
+ sections.push(`<div class="section">${htmlSection('Approval Policy', apParts.join(''))}</div>`);
2750
2807
  }
2751
2808
 
2752
2809
  // Governance Events
2753
2810
  if (run.governance_events && run.governance_events.length > 0) {
2754
- let geHtml = '<ul>';
2755
- for (const evt of run.governance_events) {
2756
- geHtml += `<li><strong>${esc(evt.type)}</strong> (<code>${esc(evt.role || '?')}</code>, <code>${esc(evt.phase || '?')}</code> phase) at <code>${esc(evt.timestamp || 'n/a')}</code>${renderHtmlGovEventDetail(evt)}</li>`;
2811
+ const { items: boundedGE, omitted: geOmitted } = boundedSlice(run.governance_events);
2812
+ const geParts = ['<ul>'];
2813
+ for (const evt of boundedGE) {
2814
+ geParts.push(`<li><strong>${esc(evt.type)}</strong> (<code>${esc(evt.role || '?')}</code>, <code>${esc(evt.phase || '?')}</code> phase) at <code>${esc(evt.timestamp || 'n/a')}</code>${renderHtmlGovEventDetail(evt)}</li>`);
2757
2815
  }
2758
- geHtml += '</ul>';
2759
- sections.push(`<div class="section">${htmlSection('Governance Events', geHtml)}</div>`);
2816
+ geParts.push('</ul>');
2817
+ if (geOmitted > 0) geParts.push(`<p><em>(${geOmitted} more governance events omitted)</em></p>`);
2818
+ sections.push(`<div class="section">${htmlSection('Governance Events', geParts.join(''))}</div>`);
2760
2819
  }
2761
2820
 
2762
2821
  // Timeout Events
2763
2822
  if (run.timeout_events && run.timeout_events.length > 0) {
2764
- let teHtml = '<ul>';
2765
- for (const evt of run.timeout_events) {
2823
+ const { items: boundedTE, omitted: teOmitted } = boundedSlice(run.timeout_events);
2824
+ const teParts = ['<ul>'];
2825
+ for (const evt of boundedTE) {
2766
2826
  const label = evt.type === 'timeout_warning' ? 'Warning' : evt.type === 'timeout_skip' ? 'Skip' : evt.type === 'timeout_skip_failed' ? 'Skip Failed' : 'Escalation';
2767
2827
  const elapsed = evt.elapsed_minutes != null ? `${evt.elapsed_minutes}m` : '?';
2768
2828
  const limit = evt.limit_minutes != null ? `${evt.limit_minutes}m` : '?';
2769
2829
  const exceeded = evt.exceeded_by_minutes != null ? ` (+${evt.exceeded_by_minutes}m)` : '';
2770
- teHtml += `<li><strong>${label}</strong> (<code>${esc(evt.scope || '?')}</code> scope) \u2014 ${elapsed}/${limit}${exceeded}, action: <code>${esc(evt.action || 'n/a')}</code>, phase: <code>${esc(evt.phase || 'n/a')}</code> at <code>${esc(evt.timestamp || 'n/a')}</code></li>`;
2830
+ teParts.push(`<li><strong>${label}</strong> (<code>${esc(evt.scope || '?')}</code> scope) \u2014 ${elapsed}/${limit}${exceeded}, action: <code>${esc(evt.action || 'n/a')}</code>, phase: <code>${esc(evt.phase || 'n/a')}</code> at <code>${esc(evt.timestamp || 'n/a')}</code></li>`);
2771
2831
  }
2772
- teHtml += '</ul>';
2773
- sections.push(`<div class="section">${htmlSection('Timeout Events', teHtml)}</div>`);
2832
+ teParts.push('</ul>');
2833
+ if (teOmitted > 0) teParts.push(`<p><em>(${teOmitted} more timeout events omitted)</em></p>`);
2834
+ sections.push(`<div class="section">${htmlSection('Timeout Events', teParts.join(''))}</div>`);
2774
2835
  }
2775
2836
 
2776
2837
  // Intake Linkage
@@ -2972,38 +3033,44 @@ function renderCoordinatorHtml(report) {
2972
3033
 
2973
3034
  // Approval Policy
2974
3035
  if (approval_policy_events?.length > 0) {
2975
- let apHtml = '<ul>';
2976
- for (const evt of approval_policy_events) {
3036
+ const { items: boundedAP, omitted: apOmitted } = boundedSlice(approval_policy_events);
3037
+ const apParts = ['<ul>'];
3038
+ for (const evt of boundedAP) {
2977
3039
  const transition = evt.gate_type === 'run_completion' ? 'run completion' : `${esc(evt.from_phase || '?')} &rarr; ${esc(evt.to_phase || '?')}`;
2978
- apHtml += `<li><strong>${esc(evt.action || 'unknown')}</strong> (${esc(evt.gate_type || 'unknown')}) ${transition} at <code>${esc(evt.timestamp || 'n/a')}</code>`;
2979
- if (evt.reason) apHtml += `<br>${esc(evt.reason)}`;
2980
- apHtml += '</li>';
3040
+ apParts.push(`<li><strong>${esc(evt.action || 'unknown')}</strong> (${esc(evt.gate_type || 'unknown')}) ${transition} at <code>${esc(evt.timestamp || 'n/a')}</code>`);
3041
+ if (evt.reason) apParts.push(`<br>${esc(evt.reason)}`);
3042
+ apParts.push('</li>');
2981
3043
  }
2982
- apHtml += '</ul>';
2983
- sections.push(`<div class="section">${htmlSection('Approval Policy', apHtml)}</div>`);
3044
+ apParts.push('</ul>');
3045
+ if (apOmitted > 0) apParts.push(`<p><em>(${apOmitted} more approval policy events omitted)</em></p>`);
3046
+ sections.push(`<div class="section">${htmlSection('Approval Policy', apParts.join(''))}</div>`);
2984
3047
  }
2985
3048
 
2986
3049
  // Governance Events
2987
3050
  if (governance_events?.length > 0) {
2988
- let geHtml = '<ul>';
2989
- for (const evt of governance_events) {
2990
- geHtml += `<li><strong>${esc(evt.type)}</strong> (<code>${esc(evt.role || '?')}</code>, <code>${esc(evt.phase || '?')}</code> phase) at <code>${esc(evt.timestamp || 'n/a')}</code>${renderHtmlGovEventDetail(evt)}</li>`;
3051
+ const { items: boundedGE, omitted: geOmitted } = boundedSlice(governance_events);
3052
+ const geParts = ['<ul>'];
3053
+ for (const evt of boundedGE) {
3054
+ geParts.push(`<li><strong>${esc(evt.type)}</strong> (<code>${esc(evt.role || '?')}</code>, <code>${esc(evt.phase || '?')}</code> phase) at <code>${esc(evt.timestamp || 'n/a')}</code>${renderHtmlGovEventDetail(evt)}</li>`);
2991
3055
  }
2992
- geHtml += '</ul>';
2993
- sections.push(`<div class="section">${htmlSection('Governance Events', geHtml)}</div>`);
3056
+ geParts.push('</ul>');
3057
+ if (geOmitted > 0) geParts.push(`<p><em>(${geOmitted} more governance events omitted)</em></p>`);
3058
+ sections.push(`<div class="section">${htmlSection('Governance Events', geParts.join(''))}</div>`);
2994
3059
  }
2995
3060
 
2996
3061
  // Timeout Events
2997
3062
  if (timeout_events?.length > 0) {
2998
- let teHtml = '<ul>';
2999
- for (const evt of timeout_events) {
3063
+ const { items: boundedTE, omitted: teOmitted } = boundedSlice(timeout_events);
3064
+ const teParts = ['<ul>'];
3065
+ for (const evt of boundedTE) {
3000
3066
  const label = evt.type === 'timeout_warning' ? 'Warning' : evt.type === 'timeout_skip' ? 'Skip' : 'Escalation';
3001
3067
  const elapsed = evt.elapsed_minutes != null ? `${evt.elapsed_minutes}m` : '?';
3002
3068
  const limit = evt.limit_minutes != null ? `${evt.limit_minutes}m` : '?';
3003
- teHtml += `<li><strong>${label}</strong> (<code>${esc(evt.scope || '?')}</code>) \u2014 ${elapsed}/${limit}, action: <code>${esc(evt.action || 'n/a')}</code> at <code>${esc(evt.timestamp || 'n/a')}</code></li>`;
3069
+ teParts.push(`<li><strong>${label}</strong> (<code>${esc(evt.scope || '?')}</code>) \u2014 ${elapsed}/${limit}, action: <code>${esc(evt.action || 'n/a')}</code> at <code>${esc(evt.timestamp || 'n/a')}</code></li>`);
3004
3070
  }
3005
- teHtml += '</ul>';
3006
- sections.push(`<div class="section">${htmlSection('Timeout Events', teHtml)}</div>`);
3071
+ teParts.push('</ul>');
3072
+ if (teOmitted > 0) teParts.push(`<p><em>(${teOmitted} more timeout events omitted)</em></p>`);
3073
+ sections.push(`<div class="section">${htmlSection('Timeout Events', teParts.join(''))}</div>`);
3007
3074
  }
3008
3075
 
3009
3076
  // Recovery Report
@@ -3019,10 +3086,10 @@ function renderCoordinatorHtml(report) {
3019
3086
 
3020
3087
  // Repo Details
3021
3088
  if (repos?.length > 0) {
3022
- let repoHtml = '';
3089
+ const repoParts = [];
3023
3090
  for (const repo of repos) {
3024
3091
  if (!repo.ok) {
3025
- repoHtml += `<h3>${esc(repo.repo_id)}</h3><p>Failed export: ${esc(repo.error || 'unknown error')}, path <code>${esc(repo.path || 'unknown')}</code></p>`;
3092
+ repoParts.push(`<h3>${esc(repo.repo_id)}</h3><p>Failed export: ${esc(repo.error || 'unknown error')}, path <code>${esc(repo.path || 'unknown')}</code></p>`);
3026
3093
  continue;
3027
3094
  }
3028
3095
  const repoPairs = [
@@ -3032,64 +3099,75 @@ function renderCoordinatorHtml(report) {
3032
3099
  ['Path', `<code>${esc(repo.path || 'unknown')}</code>`],
3033
3100
  ];
3034
3101
  if (repo.blocked_on) repoPairs.push(['Blocked on', `<code>${esc(summarizeBlockedOn(repo.blocked_on))}</code>`]);
3035
- repoHtml += `<h3>${esc(repo.repo_id)}</h3>${htmlDl(repoPairs)}`;
3102
+ repoParts.push(`<h3>${esc(repo.repo_id)}</h3>${htmlDl(repoPairs)}`);
3036
3103
 
3037
3104
  if (repo.turns?.length > 0) {
3038
- const turnRows = repo.turns.map((t, i) => {
3105
+ const { items: boundedRepoTurns, omitted: repoTurnsOmitted } = boundedSlice(repo.turns);
3106
+ const turnRows = boundedRepoTurns.map((t, i) => {
3039
3107
  const cost = t.cost_usd != null ? formatUsd(t.cost_usd) : 'n/a';
3040
3108
  const phase = t.phase_transition ? `${esc(t.phase || '?')} &rarr; ${esc(t.phase_transition)}` : esc(t.phase || '?');
3041
3109
  return [String(i + 1), esc(t.role), phase, esc(t.summary || '(no summary)'), String(t.files_changed_count), cost, esc(formatTurnTimelineTime(t))];
3042
3110
  });
3043
- repoHtml += htmlSection('Turn Timeline', htmlTable(['#', 'Role', 'Phase', 'Summary', 'Files', 'Cost', 'Time'], turnRows), 4);
3111
+ const omitNote = repoTurnsOmitted > 0 ? `<p><em>(${repoTurnsOmitted} more turns omitted)</em></p>` : '';
3112
+ repoParts.push(htmlSection('Turn Timeline', htmlTable(['#', 'Role', 'Phase', 'Summary', 'Files', 'Cost', 'Time'], turnRows) + omitNote, 4));
3044
3113
  }
3045
3114
  if (repo.decisions?.length > 0) {
3046
- repoHtml += htmlSection('Decisions', '<ul>' + repo.decisions.map((d) => `<li><strong>${esc(d.id)}</strong> (${esc(d.role || '?')}, ${esc(d.phase || '?')} phase): ${esc(d.statement)}</li>`).join('') + '</ul>', 4);
3115
+ const { items: bDecs, omitted: decsOm } = boundedSlice(repo.decisions);
3116
+ const omitNote = decsOm > 0 ? `<p><em>(${decsOm} more decisions omitted)</em></p>` : '';
3117
+ repoParts.push(htmlSection('Decisions', '<ul>' + bDecs.map((d) => `<li><strong>${esc(d.id)}</strong> (${esc(d.role || '?')}, ${esc(d.phase || '?')} phase): ${esc(d.statement)}</li>`).join('') + '</ul>' + omitNote, 4));
3047
3118
  }
3048
3119
  if (repo.gate_summary?.length > 0) {
3049
- repoHtml += htmlSection('Gate Outcomes', '<ul>' + repo.gate_summary.map((g) => `<li><code>${esc(g.gate_id)}</code>: ${badge(g.status)}</li>`).join('') + '</ul>', 4);
3120
+ repoParts.push(htmlSection('Gate Outcomes', '<ul>' + repo.gate_summary.map((g) => `<li><code>${esc(g.gate_id)}</code>: ${badge(g.status)}</li>`).join('') + '</ul>', 4));
3050
3121
  }
3051
3122
  if (repo.gate_failures?.length > 0) {
3052
- let gateFailureHtml = '<ul>';
3053
- for (const failure of repo.gate_failures) {
3123
+ const { items: bGF, omitted: gfOm } = boundedSlice(repo.gate_failures);
3124
+ const gfParts = ['<ul>'];
3125
+ for (const failure of bGF) {
3054
3126
  const request = failure.gate_type === 'run_completion'
3055
3127
  ? 'run completion'
3056
3128
  : `${esc(failure.from_phase || '?')} &rarr; ${esc(failure.to_phase || '?')}`;
3057
- gateFailureHtml += `<li><code>${esc(failure.gate_id || 'unknown')}</code> (<code>${esc(failure.gate_type || 'unknown')}</code>) at <code>${esc(failure.failed_at || 'n/a')}</code> via ${failure.queued_request ? 'queued drain' : 'direct'} request: ${request}`;
3129
+ gfParts.push(`<li><code>${esc(failure.gate_id || 'unknown')}</code> (<code>${esc(failure.gate_type || 'unknown')}</code>) at <code>${esc(failure.failed_at || 'n/a')}</code> via ${failure.queued_request ? 'queued drain' : 'direct'} request: ${request}`);
3058
3130
  if (failure.reasons?.length > 0) {
3059
- gateFailureHtml += '<ul>' + failure.reasons.map((reason) => `<li>${esc(reason)}</li>`).join('') + '</ul>';
3131
+ gfParts.push('<ul>' + failure.reasons.map((reason) => `<li>${esc(reason)}</li>`).join('') + '</ul>');
3060
3132
  }
3061
- gateFailureHtml += '</li>';
3133
+ gfParts.push('</li>');
3062
3134
  }
3063
- gateFailureHtml += '</ul>';
3064
- repoHtml += htmlSection('Gate Failures', gateFailureHtml, 4);
3135
+ gfParts.push('</ul>');
3136
+ if (gfOm > 0) gfParts.push(`<p><em>(${gfOm} more gate failures omitted)</em></p>`);
3137
+ repoParts.push(htmlSection('Gate Failures', gfParts.join(''), 4));
3065
3138
  }
3066
3139
  if (repo.approval_policy_events?.length > 0) {
3067
- let approvalHtml = '<ul>';
3068
- for (const evt of repo.approval_policy_events) {
3140
+ const { items: bAP, omitted: apOm } = boundedSlice(repo.approval_policy_events);
3141
+ const apParts = ['<ul>'];
3142
+ for (const evt of bAP) {
3069
3143
  const transition = evt.gate_type === 'run_completion'
3070
3144
  ? 'run completion'
3071
3145
  : `${esc(evt.from_phase || '?')} &rarr; ${esc(evt.to_phase || '?')}`;
3072
3146
  const rule = evt.matched_rule
3073
3147
  ? ` — rule: <code>${esc(typeof evt.matched_rule === 'object' ? JSON.stringify(evt.matched_rule) : evt.matched_rule)}</code>`
3074
3148
  : '';
3075
- approvalHtml += `<li><strong>${esc(evt.action || 'unknown')}</strong> (${esc(evt.gate_type || 'unknown')}) ${transition}${rule} at <code>${esc(evt.timestamp || 'n/a')}</code>`;
3076
- if (evt.reason) approvalHtml += `<br>${esc(evt.reason)}`;
3077
- approvalHtml += '</li>';
3149
+ apParts.push(`<li><strong>${esc(evt.action || 'unknown')}</strong> (${esc(evt.gate_type || 'unknown')}) ${transition}${rule} at <code>${esc(evt.timestamp || 'n/a')}</code>`);
3150
+ if (evt.reason) apParts.push(`<br>${esc(evt.reason)}`);
3151
+ apParts.push('</li>');
3078
3152
  }
3079
- approvalHtml += '</ul>';
3080
- repoHtml += htmlSection('Approval Policy', approvalHtml, 4);
3153
+ apParts.push('</ul>');
3154
+ if (apOm > 0) apParts.push(`<p><em>(${apOm} more approval policy events omitted)</em></p>`);
3155
+ repoParts.push(htmlSection('Approval Policy', apParts.join(''), 4));
3081
3156
  }
3082
3157
  if (repo.governance_events?.length > 0) {
3083
- let governanceHtml = '<ul>';
3084
- for (const evt of repo.governance_events) {
3085
- governanceHtml += `<li><strong>${esc(evt.type)}</strong> (<code>${esc(evt.role || '?')}</code>, <code>${esc(evt.phase || '?')}</code> phase) at <code>${esc(evt.timestamp || 'n/a')}</code>${renderHtmlGovEventDetail(evt)}</li>`;
3158
+ const { items: bGE, omitted: geOm } = boundedSlice(repo.governance_events);
3159
+ const geParts = ['<ul>'];
3160
+ for (const evt of bGE) {
3161
+ geParts.push(`<li><strong>${esc(evt.type)}</strong> (<code>${esc(evt.role || '?')}</code>, <code>${esc(evt.phase || '?')}</code> phase) at <code>${esc(evt.timestamp || 'n/a')}</code>${renderHtmlGovEventDetail(evt)}</li>`);
3086
3162
  }
3087
- governanceHtml += '</ul>';
3088
- repoHtml += htmlSection('Governance Events', governanceHtml, 4);
3163
+ geParts.push('</ul>');
3164
+ if (geOm > 0) geParts.push(`<p><em>(${geOm} more governance events omitted)</em></p>`);
3165
+ repoParts.push(htmlSection('Governance Events', geParts.join(''), 4));
3089
3166
  }
3090
3167
  if (repo.timeout_events?.length > 0) {
3091
- let timeoutHtml = '<ul>';
3092
- for (const evt of repo.timeout_events) {
3168
+ const { items: bTE, omitted: teOm } = boundedSlice(repo.timeout_events);
3169
+ const teParts = ['<ul>'];
3170
+ for (const evt of bTE) {
3093
3171
  const label = evt.type === 'timeout_warning' ? 'Warning'
3094
3172
  : evt.type === 'timeout_skip' ? 'Skip'
3095
3173
  : evt.type === 'timeout_skip_failed' ? 'Skip Failed'
@@ -3097,10 +3175,11 @@ function renderCoordinatorHtml(report) {
3097
3175
  const elapsed = evt.elapsed_minutes != null ? `${evt.elapsed_minutes}m` : '?';
3098
3176
  const limit = evt.limit_minutes != null ? `${evt.limit_minutes}m` : '?';
3099
3177
  const exceeded = evt.exceeded_by_minutes != null ? ` (+${evt.exceeded_by_minutes}m)` : '';
3100
- timeoutHtml += `<li><strong>${label}</strong> (<code>${esc(evt.scope || '?')}</code> scope) — ${elapsed}/${limit}${exceeded}, action: <code>${esc(evt.action || 'n/a')}</code>, phase: <code>${esc(evt.phase || 'n/a')}</code> at <code>${esc(evt.timestamp || 'n/a')}</code></li>`;
3178
+ teParts.push(`<li><strong>${label}</strong> (<code>${esc(evt.scope || '?')}</code> scope) — ${elapsed}/${limit}${exceeded}, action: <code>${esc(evt.action || 'n/a')}</code>, phase: <code>${esc(evt.phase || 'n/a')}</code> at <code>${esc(evt.timestamp || 'n/a')}</code></li>`);
3101
3179
  }
3102
- timeoutHtml += '</ul>';
3103
- repoHtml += htmlSection('Timeout Events', timeoutHtml, 4);
3180
+ teParts.push('</ul>');
3181
+ if (teOm > 0) teParts.push(`<p><em>(${teOm} more timeout events omitted)</em></p>`);
3182
+ repoParts.push(htmlSection('Timeout Events', teParts.join(''), 4));
3104
3183
  }
3105
3184
  if (repo.hook_summary) {
3106
3185
  const eventList = Object.entries(repo.hook_summary.events)
@@ -3112,7 +3191,7 @@ function renderCoordinatorHtml(report) {
3112
3191
  ['Blocked', String(repo.hook_summary.blocked)],
3113
3192
  ...(eventList ? [['Events', eventList]] : []),
3114
3193
  ]);
3115
- repoHtml += htmlSection('Hook Activity', hookHtml, 4);
3194
+ repoParts.push(htmlSection('Hook Activity', hookHtml, 4));
3116
3195
  }
3117
3196
  if (repo.recovery_summary) {
3118
3197
  const recovery = repo.recovery_summary;
@@ -3129,7 +3208,7 @@ function renderCoordinatorHtml(report) {
3129
3208
  `<li><code>${esc(entry.code)}</code> — <code>${esc(entry.command)}</code>: ${esc(entry.reason)}</li>`
3130
3209
  ).join('') + '</ul>', 5);
3131
3210
  }
3132
- repoHtml += htmlSection('Recovery', recoveryHtml, 4);
3211
+ repoParts.push(htmlSection('Recovery', recoveryHtml, 4));
3133
3212
  }
3134
3213
  if (repo.continuity) {
3135
3214
  const continuityPairs = [
@@ -3142,10 +3221,10 @@ function renderCoordinatorHtml(report) {
3142
3221
  if (repo.continuity.stale_checkpoint) {
3143
3222
  continuityPairs.push(['Warning', `<span class="warn">checkpoint tracks run <code>${esc(repo.continuity.run_id)}</code>, but repo export tracks <code>${esc(repo.run_id)}</code></span>`]);
3144
3223
  }
3145
- repoHtml += htmlSection('Continuity', htmlDl(continuityPairs), 4);
3224
+ repoParts.push(htmlSection('Continuity', htmlDl(continuityPairs), 4));
3146
3225
  }
3147
3226
  }
3148
- sections.push(`<div class="section">${htmlSection('Repo Details', repoHtml)}</div>`);
3227
+ sections.push(`<div class="section">${htmlSection('Repo Details', repoParts.join(''))}</div>`);
3149
3228
  }
3150
3229
 
3151
3230
  return wrapHtml('AgentXchain Governance Report — Coordinator', sections.join('\n'));