agentxchain 2.69.0 → 2.71.0
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/multi.js +56 -2
- package/src/lib/coordinator-state.js +4 -0
- package/src/lib/report.js +177 -0
package/package.json
CHANGED
package/src/commands/multi.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* multi resync — detect divergence and rebuild coordinator state from repo authority
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import chalk from 'chalk';
|
|
13
14
|
import { loadCoordinatorConfig } from '../lib/coordinator-config.js';
|
|
14
15
|
import {
|
|
15
16
|
initializeCoordinatorRun,
|
|
@@ -105,11 +106,44 @@ export async function multiStatusCommand(options) {
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
console.log(`Super Run: ${status.super_run_id}`);
|
|
108
|
-
console.log(`Status: ${status.status}`);
|
|
109
|
+
console.log(`Status: ${formatCoordinatorStatus(status.status)}`);
|
|
109
110
|
console.log(`Phase: ${status.phase}`);
|
|
110
111
|
|
|
112
|
+
// Elapsed time for active runs
|
|
113
|
+
if (status.created_at && status.status === 'active') {
|
|
114
|
+
const elapsedMs = Date.now() - new Date(status.created_at).getTime();
|
|
115
|
+
if (elapsedMs >= 0) {
|
|
116
|
+
const secs = Math.floor(elapsedMs / 1000);
|
|
117
|
+
const mins = Math.floor(secs / 60);
|
|
118
|
+
const remainSecs = secs % 60;
|
|
119
|
+
const elapsed = mins > 0 ? `${mins}m ${remainSecs}s` : `${remainSecs}s`;
|
|
120
|
+
console.log(`Elapsed: ${elapsed}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Blocked state with reason
|
|
125
|
+
if (status.status === 'blocked') {
|
|
126
|
+
const reason = (typeof status.blocked_reason === 'string' && status.blocked_reason.trim())
|
|
127
|
+
? status.blocked_reason.trim()
|
|
128
|
+
: 'unknown reason';
|
|
129
|
+
console.log(`Blocked: ${chalk.red.bold('BLOCKED')} — ${reason}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Pending gate with phase transition direction
|
|
111
133
|
if (status.pending_gate) {
|
|
112
|
-
|
|
134
|
+
const pg = status.pending_gate;
|
|
135
|
+
const fromTo = pg.from && pg.to ? ` ${pg.from} → ${pg.to}` : '';
|
|
136
|
+
console.log(`Pending Gate: ${pg.gate} (${pg.gate_type})${fromTo}`);
|
|
137
|
+
console.log(`Action: Run ${chalk.cyan('agentxchain multi approve-gate')} to advance`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Completed state
|
|
141
|
+
if (status.status === 'completed') {
|
|
142
|
+
console.log('');
|
|
143
|
+
console.log(` ${chalk.green.bold('✓ Coordinator run completed')}`);
|
|
144
|
+
if (status.updated_at) {
|
|
145
|
+
console.log(` ${chalk.dim('Completed:')} ${status.updated_at}`);
|
|
146
|
+
}
|
|
113
147
|
}
|
|
114
148
|
|
|
115
149
|
console.log('');
|
|
@@ -119,6 +153,17 @@ export async function multiStatusCommand(options) {
|
|
|
119
153
|
console.log(` ${repoId}: ${info.status || 'unknown'}${phase} (run: ${info.run_id})`);
|
|
120
154
|
}
|
|
121
155
|
|
|
156
|
+
// Phase gate status
|
|
157
|
+
const gateEntries = Object.entries(status.phase_gate_status || {});
|
|
158
|
+
if (gateEntries.length > 0) {
|
|
159
|
+
console.log('');
|
|
160
|
+
console.log('Gates:');
|
|
161
|
+
for (const [gate, gateStatus] of gateEntries) {
|
|
162
|
+
const icon = gateStatus === 'passed' ? chalk.green('✓') : chalk.dim('○');
|
|
163
|
+
console.log(` ${icon} ${gate}: ${gateStatus}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
122
167
|
const barrierEntries = Object.entries(barriers || {});
|
|
123
168
|
if (barrierEntries.length > 0) {
|
|
124
169
|
console.log('');
|
|
@@ -129,6 +174,15 @@ export async function multiStatusCommand(options) {
|
|
|
129
174
|
}
|
|
130
175
|
}
|
|
131
176
|
|
|
177
|
+
function formatCoordinatorStatus(status) {
|
|
178
|
+
switch (status) {
|
|
179
|
+
case 'active': return chalk.green(status);
|
|
180
|
+
case 'blocked': return chalk.red.bold(status);
|
|
181
|
+
case 'completed': return chalk.cyan(status);
|
|
182
|
+
default: return status;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
132
186
|
// ── multi step ─────────────────────────────────────────────────────────────
|
|
133
187
|
|
|
134
188
|
export async function multiStepCommand(options) {
|
|
@@ -401,6 +401,10 @@ export function getCoordinatorStatus(workspacePath) {
|
|
|
401
401
|
repo_runs: state.repo_runs,
|
|
402
402
|
pending_barriers: pendingBarriers,
|
|
403
403
|
pending_gate: state.pending_gate ?? null,
|
|
404
|
+
blocked_reason: state.blocked_reason ?? null,
|
|
405
|
+
created_at: state.created_at ?? null,
|
|
406
|
+
updated_at: state.updated_at ?? null,
|
|
407
|
+
phase_gate_status: state.phase_gate_status ?? null,
|
|
404
408
|
};
|
|
405
409
|
}
|
|
406
410
|
|
package/src/lib/report.js
CHANGED
|
@@ -20,6 +20,76 @@ function summarizeBlockedOn(blockedOn) {
|
|
|
20
20
|
return 'present';
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
function renderGovernanceEventDetailText(lines, evt, indent) {
|
|
24
|
+
switch (evt.type) {
|
|
25
|
+
case 'policy_escalation':
|
|
26
|
+
for (const v of evt.violations || []) {
|
|
27
|
+
lines.push(`${indent}violation: ${v.policy_id || '?'} / ${v.rule || '?'} — ${v.message || 'n/a'}`);
|
|
28
|
+
}
|
|
29
|
+
break;
|
|
30
|
+
case 'conflict_detected':
|
|
31
|
+
if (evt.conflicting_files?.length > 0) {
|
|
32
|
+
lines.push(`${indent}files: ${evt.conflicting_files.join(', ')}`);
|
|
33
|
+
}
|
|
34
|
+
if (evt.overlap_ratio != null) {
|
|
35
|
+
lines.push(`${indent}overlap: ${(evt.overlap_ratio * 100).toFixed(0)}%`);
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
case 'conflict_rejected':
|
|
39
|
+
if (evt.conflicting_files?.length > 0) {
|
|
40
|
+
lines.push(`${indent}files: ${evt.conflicting_files.join(', ')}`);
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
case 'conflict_resolution_selected':
|
|
44
|
+
if (evt.resolution_method) {
|
|
45
|
+
lines.push(`${indent}resolution: ${evt.resolution_method}`);
|
|
46
|
+
}
|
|
47
|
+
break;
|
|
48
|
+
case 'operator_escalated':
|
|
49
|
+
if (evt.reason) lines.push(`${indent}reason: ${evt.reason}`);
|
|
50
|
+
if (evt.blocked_on) lines.push(`${indent}blocked_on: ${evt.blocked_on}`);
|
|
51
|
+
break;
|
|
52
|
+
case 'escalation_resolved':
|
|
53
|
+
if (evt.resolved_via) lines.push(`${indent}resolved via: ${evt.resolved_via}`);
|
|
54
|
+
if (evt.previous_blocked_on) lines.push(`${indent}was blocked on: ${evt.previous_blocked_on}`);
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function renderGovernanceEventDetailMarkdown(lines, evt) {
|
|
60
|
+
switch (evt.type) {
|
|
61
|
+
case 'policy_escalation':
|
|
62
|
+
for (const v of evt.violations || []) {
|
|
63
|
+
lines.push(` - Violation: \`${v.policy_id || '?'}\` / \`${v.rule || '?'}\` — ${v.message || 'n/a'}`);
|
|
64
|
+
}
|
|
65
|
+
break;
|
|
66
|
+
case 'conflict_detected':
|
|
67
|
+
if (evt.conflicting_files?.length > 0) {
|
|
68
|
+
lines.push(` - Files: ${evt.conflicting_files.map((f) => `\`${f}\``).join(', ')}`);
|
|
69
|
+
}
|
|
70
|
+
if (evt.overlap_ratio != null) {
|
|
71
|
+
lines.push(` - Overlap: ${(evt.overlap_ratio * 100).toFixed(0)}%`);
|
|
72
|
+
}
|
|
73
|
+
break;
|
|
74
|
+
case 'conflict_rejected':
|
|
75
|
+
if (evt.conflicting_files?.length > 0) {
|
|
76
|
+
lines.push(` - Files: ${evt.conflicting_files.map((f) => `\`${f}\``).join(', ')}`);
|
|
77
|
+
}
|
|
78
|
+
break;
|
|
79
|
+
case 'conflict_resolution_selected':
|
|
80
|
+
if (evt.resolution_method) lines.push(` - Resolution: \`${evt.resolution_method}\``);
|
|
81
|
+
break;
|
|
82
|
+
case 'operator_escalated':
|
|
83
|
+
if (evt.reason) lines.push(` - Reason: ${evt.reason}`);
|
|
84
|
+
if (evt.blocked_on) lines.push(` - Blocked on: \`${evt.blocked_on}\``);
|
|
85
|
+
break;
|
|
86
|
+
case 'escalation_resolved':
|
|
87
|
+
if (evt.resolved_via) lines.push(` - Resolved via: \`${evt.resolved_via}\``);
|
|
88
|
+
if (evt.previous_blocked_on) lines.push(` - Was blocked on: \`${evt.previous_blocked_on}\``);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
23
93
|
function summarizeBlockedState(run) {
|
|
24
94
|
const blockedReason = run?.blocked_reason;
|
|
25
95
|
if (blockedReason && typeof blockedReason === 'object' && !Array.isArray(blockedReason)) {
|
|
@@ -175,6 +245,60 @@ function extractGateFailureDigest(artifact) {
|
|
|
175
245
|
}));
|
|
176
246
|
}
|
|
177
247
|
|
|
248
|
+
const GOVERNANCE_EVENT_TYPES = new Set([
|
|
249
|
+
'policy_escalation',
|
|
250
|
+
'conflict_detected',
|
|
251
|
+
'conflict_rejected',
|
|
252
|
+
'conflict_resolution_selected',
|
|
253
|
+
'operator_escalated',
|
|
254
|
+
'escalation_resolved',
|
|
255
|
+
]);
|
|
256
|
+
|
|
257
|
+
function extractGovernanceEventDigest(artifact, relPath = '.agentxchain/decision-ledger.jsonl') {
|
|
258
|
+
const data = extractFileData(artifact, relPath);
|
|
259
|
+
if (!Array.isArray(data) || data.length === 0) return [];
|
|
260
|
+
return data
|
|
261
|
+
.filter((d) => typeof d?.decision === 'string' && GOVERNANCE_EVENT_TYPES.has(d.decision))
|
|
262
|
+
.map((d) => {
|
|
263
|
+
const base = {
|
|
264
|
+
type: d.decision,
|
|
265
|
+
timestamp: d.timestamp || null,
|
|
266
|
+
turn_id: d.turn_id || null,
|
|
267
|
+
role: d.role || null,
|
|
268
|
+
phase: d.phase || null,
|
|
269
|
+
};
|
|
270
|
+
switch (d.decision) {
|
|
271
|
+
case 'policy_escalation':
|
|
272
|
+
base.violations = Array.isArray(d.violations) ? d.violations.map((v) => ({
|
|
273
|
+
policy_id: v.policy_id || null,
|
|
274
|
+
rule: v.rule || null,
|
|
275
|
+
message: v.message || null,
|
|
276
|
+
})) : [];
|
|
277
|
+
break;
|
|
278
|
+
case 'conflict_detected':
|
|
279
|
+
base.conflicting_files = Array.isArray(d.conflict?.files) ? d.conflict.files : [];
|
|
280
|
+
base.overlap_ratio = typeof d.conflict?.overlap_ratio === 'number' ? d.conflict.overlap_ratio : null;
|
|
281
|
+
break;
|
|
282
|
+
case 'conflict_rejected':
|
|
283
|
+
base.conflicting_files = Array.isArray(d.conflict?.files) ? d.conflict.files : [];
|
|
284
|
+
break;
|
|
285
|
+
case 'conflict_resolution_selected':
|
|
286
|
+
base.resolution_method = d.conflict?.resolution || null;
|
|
287
|
+
break;
|
|
288
|
+
case 'operator_escalated':
|
|
289
|
+
base.blocked_on = d.blocked_on || null;
|
|
290
|
+
base.reason = d.escalation?.reason || null;
|
|
291
|
+
break;
|
|
292
|
+
case 'escalation_resolved':
|
|
293
|
+
base.resolved_via = d.resolved_via || null;
|
|
294
|
+
base.previous_blocked_on = d.blocked_on || null;
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
return base;
|
|
298
|
+
})
|
|
299
|
+
.sort((a, b) => (a.timestamp || '').localeCompare(b.timestamp || ''));
|
|
300
|
+
}
|
|
301
|
+
|
|
178
302
|
function extractTimeoutEventDigest(artifact, relPath = '.agentxchain/decision-ledger.jsonl') {
|
|
179
303
|
const data = extractFileData(artifact, relPath);
|
|
180
304
|
if (!Array.isArray(data) || data.length === 0) return [];
|
|
@@ -658,6 +782,7 @@ function buildRunSubject(artifact) {
|
|
|
658
782
|
const intakeLinks = extractIntakeLinks(artifact);
|
|
659
783
|
const recoverySummary = extractRecoverySummary(artifact);
|
|
660
784
|
const continuity = extractContinuityMetadata(artifact);
|
|
785
|
+
const governanceEvents = extractGovernanceEventDigest(artifact);
|
|
661
786
|
|
|
662
787
|
return {
|
|
663
788
|
kind: 'governed_run',
|
|
@@ -689,6 +814,7 @@ function buildRunSubject(artifact) {
|
|
|
689
814
|
turns,
|
|
690
815
|
decisions,
|
|
691
816
|
approval_policy_events: approvalPolicyEvents,
|
|
817
|
+
governance_events: governanceEvents,
|
|
692
818
|
gate_failures: gateFailures,
|
|
693
819
|
timeout_events: timeoutEvents,
|
|
694
820
|
hook_summary: hookSummary,
|
|
@@ -764,6 +890,7 @@ function buildCoordinatorSubject(artifact) {
|
|
|
764
890
|
base.turns = extractHistoryTimeline(childExport);
|
|
765
891
|
base.decisions = extractDecisionDigest(childExport);
|
|
766
892
|
base.approval_policy_events = extractApprovalPolicyDigest(childExport);
|
|
893
|
+
base.governance_events = extractGovernanceEventDigest(childExport);
|
|
767
894
|
base.gate_failures = extractGateFailureDigest(childExport);
|
|
768
895
|
base.timeout_events = extractTimeoutEventDigest(childExport);
|
|
769
896
|
base.hook_summary = extractHookSummary(childExport);
|
|
@@ -780,6 +907,7 @@ function buildCoordinatorSubject(artifact) {
|
|
|
780
907
|
const barrierLedgerTimeline = extractBarrierLedgerTimeline(artifact);
|
|
781
908
|
const decisionDigest = extractCoordinatorDecisionDigest(artifact);
|
|
782
909
|
const coordinatorApprovalPolicyEvents = extractCoordinatorApprovalPolicyDigest(artifact);
|
|
910
|
+
const coordinatorGovernanceEvents = extractGovernanceEventDigest(artifact, '.agentxchain/multirepo/decision-ledger.jsonl');
|
|
783
911
|
const coordinatorTimeoutEvents = extractTimeoutEventDigest(artifact, '.agentxchain/multirepo/decision-ledger.jsonl');
|
|
784
912
|
const timing = computeCoordinatorTiming(artifact, coordinatorTimeline);
|
|
785
913
|
const blockedReason = normalizeCoordinatorBlockedReason(coordinatorState.blocked_reason);
|
|
@@ -824,6 +952,7 @@ function buildCoordinatorSubject(artifact) {
|
|
|
824
952
|
barrier_ledger_timeline: barrierLedgerTimeline,
|
|
825
953
|
decision_digest: decisionDigest,
|
|
826
954
|
approval_policy_events: coordinatorApprovalPolicyEvents,
|
|
955
|
+
governance_events: coordinatorGovernanceEvents,
|
|
827
956
|
timeout_events: coordinatorTimeoutEvents,
|
|
828
957
|
recovery_report: extractRecoveryReportSummary(artifact),
|
|
829
958
|
repos,
|
|
@@ -1008,6 +1137,14 @@ export function formatGovernanceReportText(report) {
|
|
|
1008
1137
|
}
|
|
1009
1138
|
}
|
|
1010
1139
|
|
|
1140
|
+
if (run.governance_events && run.governance_events.length > 0) {
|
|
1141
|
+
lines.push('', 'Governance Events:');
|
|
1142
|
+
for (const evt of run.governance_events) {
|
|
1143
|
+
lines.push(` - ${evt.type} | ${evt.role || '?'} | ${evt.phase || '?'} | at: ${evt.timestamp || 'n/a'}`);
|
|
1144
|
+
renderGovernanceEventDetailText(lines, evt, ' ');
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1011
1148
|
if (run.timeout_events && run.timeout_events.length > 0) {
|
|
1012
1149
|
lines.push('', 'Timeout Events:');
|
|
1013
1150
|
for (const evt of run.timeout_events) {
|
|
@@ -1082,6 +1219,7 @@ export function formatGovernanceReportText(report) {
|
|
|
1082
1219
|
barrier_ledger_timeline,
|
|
1083
1220
|
decision_digest,
|
|
1084
1221
|
approval_policy_events,
|
|
1222
|
+
governance_events,
|
|
1085
1223
|
timeout_events,
|
|
1086
1224
|
recovery_report,
|
|
1087
1225
|
} = report.subject;
|
|
@@ -1179,6 +1317,14 @@ export function formatGovernanceReportText(report) {
|
|
|
1179
1317
|
}
|
|
1180
1318
|
}
|
|
1181
1319
|
|
|
1320
|
+
if (governance_events && governance_events.length > 0) {
|
|
1321
|
+
lines.push('', 'Governance Events:');
|
|
1322
|
+
for (const evt of governance_events) {
|
|
1323
|
+
lines.push(` - ${evt.type} | ${evt.role || '?'} | ${evt.phase || '?'} | at: ${evt.timestamp || 'n/a'}`);
|
|
1324
|
+
renderGovernanceEventDetailText(lines, evt, ' ');
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1182
1328
|
if (timeout_events && timeout_events.length > 0) {
|
|
1183
1329
|
lines.push('', 'Timeout Events:');
|
|
1184
1330
|
for (const evt of timeout_events) {
|
|
@@ -1254,6 +1400,13 @@ export function formatGovernanceReportText(report) {
|
|
|
1254
1400
|
repoLines.push(` - ${evt.action || 'unknown'}: ${evt.gate_type || 'unknown'} | ${transition} | ${evt.timestamp || 'n/a'}`);
|
|
1255
1401
|
}
|
|
1256
1402
|
}
|
|
1403
|
+
if (repo.governance_events && repo.governance_events.length > 0) {
|
|
1404
|
+
repoLines.push(' Governance Events:');
|
|
1405
|
+
for (const evt of repo.governance_events) {
|
|
1406
|
+
repoLines.push(` - ${evt.type} | ${evt.role || '?'} | ${evt.phase || '?'} | at: ${evt.timestamp || 'n/a'}`);
|
|
1407
|
+
renderGovernanceEventDetailText(repoLines, evt, ' ');
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1257
1410
|
if (repo.timeout_events && repo.timeout_events.length > 0) {
|
|
1258
1411
|
repoLines.push(' Timeout Events:');
|
|
1259
1412
|
for (const evt of repo.timeout_events) {
|
|
@@ -1417,6 +1570,14 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1417
1570
|
}
|
|
1418
1571
|
}
|
|
1419
1572
|
|
|
1573
|
+
if (run.governance_events && run.governance_events.length > 0) {
|
|
1574
|
+
lines.push('', '## Governance Events', '');
|
|
1575
|
+
for (const evt of run.governance_events) {
|
|
1576
|
+
lines.push(`- **${evt.type}** (\`${evt.role || '?'}\`, \`${evt.phase || '?'}\` phase) at \`${evt.timestamp || 'n/a'}\``);
|
|
1577
|
+
renderGovernanceEventDetailMarkdown(lines, evt);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1420
1581
|
if (run.timeout_events && run.timeout_events.length > 0) {
|
|
1421
1582
|
lines.push('', '## Timeout Events', '');
|
|
1422
1583
|
for (const evt of run.timeout_events) {
|
|
@@ -1494,6 +1655,7 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1494
1655
|
barrier_ledger_timeline,
|
|
1495
1656
|
decision_digest,
|
|
1496
1657
|
approval_policy_events,
|
|
1658
|
+
governance_events,
|
|
1497
1659
|
timeout_events,
|
|
1498
1660
|
recovery_report: coordRecoveryReport,
|
|
1499
1661
|
} = report.subject;
|
|
@@ -1592,6 +1754,14 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1592
1754
|
}
|
|
1593
1755
|
}
|
|
1594
1756
|
|
|
1757
|
+
if (governance_events && governance_events.length > 0) {
|
|
1758
|
+
mdLines.push('', '## Governance Events', '');
|
|
1759
|
+
for (const evt of governance_events) {
|
|
1760
|
+
mdLines.push(`- **${evt.type}** (\`${evt.role || '?'}\`, \`${evt.phase || '?'}\` phase) at \`${evt.timestamp || 'n/a'}\``);
|
|
1761
|
+
renderGovernanceEventDetailMarkdown(mdLines, evt);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1595
1765
|
if (timeout_events && timeout_events.length > 0) {
|
|
1596
1766
|
mdLines.push('', '## Timeout Events', '');
|
|
1597
1767
|
for (const evt of timeout_events) {
|
|
@@ -1670,6 +1840,13 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1670
1840
|
if (evt.reason) repoLines.push(` - ${evt.reason}`);
|
|
1671
1841
|
}
|
|
1672
1842
|
}
|
|
1843
|
+
if (repo.governance_events && repo.governance_events.length > 0) {
|
|
1844
|
+
repoLines.push('', '#### Governance Events', '');
|
|
1845
|
+
for (const evt of repo.governance_events) {
|
|
1846
|
+
repoLines.push(`- **${evt.type}** (\`${evt.role || '?'}\`, \`${evt.phase || '?'}\` phase) at \`${evt.timestamp || 'n/a'}\``);
|
|
1847
|
+
renderGovernanceEventDetailMarkdown(repoLines, evt);
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1673
1850
|
if (repo.timeout_events && repo.timeout_events.length > 0) {
|
|
1674
1851
|
repoLines.push('', '#### Timeout Events', '');
|
|
1675
1852
|
for (const evt of repo.timeout_events) {
|