agentxchain 2.155.34 → 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 +1 -1
- package/src/commands/run.js +18 -7
- package/src/lib/governed-state.js +1 -1
- package/src/lib/report.js +189 -110
package/package.json
CHANGED
package/src/commands/run.js
CHANGED
|
@@ -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
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
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
|
}
|
|
@@ -6021,7 +6021,7 @@ function _acceptGovernedTurnLocked(root, config, opts) {
|
|
|
6021
6021
|
category: 'non_progress',
|
|
6022
6022
|
recovery: {
|
|
6023
6023
|
typed_reason: `Non-progress detected: ${newCount} accepted turns have not reduced gate failure "${gateId}".`,
|
|
6024
|
-
recovery_action: 'agentxchain resume
|
|
6024
|
+
recovery_action: 'agentxchain resume',
|
|
6025
6025
|
detail: `Gate "${gateId}" has been failing on ${failingFiles.join(', ')} for ${newCount} consecutive turns. The gated file(s) were never modified.`,
|
|
6026
6026
|
},
|
|
6027
6027
|
turnId: currentTurn.turn_id,
|
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 <
|
|
1406
|
-
const t =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 <
|
|
1635
|
-
const ev =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 <
|
|
1977
|
-
const t =
|
|
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
|
|
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
|
|
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
|
|
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 ? `
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 || '?')} → ${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
|
-
|
|
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
|
-
|
|
2711
|
-
|
|
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 || '?')} → ${esc(failure.to_phase || '?')}`;
|
|
2713
|
-
|
|
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
|
-
|
|
2767
|
+
gfParts.push('<ul>' + failure.reasons.map((r) => `<li>${esc(r)}</li>`).join('') + '</ul>');
|
|
2716
2768
|
}
|
|
2717
|
-
|
|
2769
|
+
gfParts.push('</li>');
|
|
2718
2770
|
}
|
|
2719
|
-
|
|
2720
|
-
|
|
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
|
-
|
|
2725
|
-
|
|
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
|
|
2729
|
-
|
|
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
|
-
|
|
2785
|
+
gaParts.push(`<br><code>${esc(action.stderr_tail)}</code>`);
|
|
2732
2786
|
}
|
|
2733
|
-
|
|
2787
|
+
gaParts.push('</li>');
|
|
2734
2788
|
}
|
|
2735
|
-
|
|
2736
|
-
|
|
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
|
-
|
|
2742
|
-
|
|
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 || '?')} → ${esc(evt.to_phase || '?')}`;
|
|
2744
|
-
|
|
2745
|
-
if (evt.reason)
|
|
2746
|
-
|
|
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
|
-
|
|
2749
|
-
|
|
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
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
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
|
-
|
|
2759
|
-
|
|
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
|
-
|
|
2765
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2773
|
-
|
|
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
|
-
|
|
2976
|
-
|
|
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 || '?')} → ${esc(evt.to_phase || '?')}`;
|
|
2978
|
-
|
|
2979
|
-
if (evt.reason)
|
|
2980
|
-
|
|
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
|
-
|
|
2983
|
-
|
|
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
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
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
|
-
|
|
2993
|
-
|
|
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
|
-
|
|
2999
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3006
|
-
|
|
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
|
-
|
|
3089
|
+
const repoParts = [];
|
|
3023
3090
|
for (const repo of repos) {
|
|
3024
3091
|
if (!repo.ok) {
|
|
3025
|
-
|
|
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
|
-
|
|
3102
|
+
repoParts.push(`<h3>${esc(repo.repo_id)}</h3>${htmlDl(repoPairs)}`);
|
|
3036
3103
|
|
|
3037
3104
|
if (repo.turns?.length > 0) {
|
|
3038
|
-
const
|
|
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 || '?')} → ${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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3053
|
-
|
|
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 || '?')} → ${esc(failure.to_phase || '?')}`;
|
|
3057
|
-
|
|
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
|
-
|
|
3131
|
+
gfParts.push('<ul>' + failure.reasons.map((reason) => `<li>${esc(reason)}</li>`).join('') + '</ul>');
|
|
3060
3132
|
}
|
|
3061
|
-
|
|
3133
|
+
gfParts.push('</li>');
|
|
3062
3134
|
}
|
|
3063
|
-
|
|
3064
|
-
|
|
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
|
-
|
|
3068
|
-
|
|
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 || '?')} → ${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
|
-
|
|
3076
|
-
if (evt.reason)
|
|
3077
|
-
|
|
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
|
-
|
|
3080
|
-
|
|
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
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
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
|
-
|
|
3088
|
-
|
|
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
|
-
|
|
3092
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3103
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3224
|
+
repoParts.push(htmlSection('Continuity', htmlDl(continuityPairs), 4));
|
|
3146
3225
|
}
|
|
3147
3226
|
}
|
|
3148
|
-
sections.push(`<div class="section">${htmlSection('Repo Details',
|
|
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'));
|