agentxchain 2.104.0 → 2.105.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/README.md +12 -6
- package/bin/agentxchain.js +5 -5
- package/dashboard/app.js +111 -7
- package/dashboard/components/blocked.js +95 -11
- package/dashboard/components/blockers.js +85 -86
- package/dashboard/components/coordinator-timeouts.js +13 -0
- package/dashboard/components/cross-repo.js +17 -12
- package/dashboard/components/gate.js +31 -11
- package/dashboard/components/initiative.js +173 -78
- package/dashboard/components/ledger.js +28 -0
- package/dashboard/components/live-status.js +39 -0
- package/dashboard/components/run-history.js +76 -1
- package/dashboard/components/timeline.js +5 -1
- package/dashboard/index.html +21 -0
- package/dashboard/live-observer.js +91 -0
- package/package.json +1 -1
- package/scripts/release-bump.sh +26 -3
- package/src/commands/accept-turn.js +3 -3
- package/src/commands/decisions.js +98 -29
- package/src/commands/diff.js +27 -4
- package/src/commands/doctor.js +48 -16
- package/src/commands/history.js +21 -3
- package/src/commands/multi.js +223 -54
- package/src/commands/phase.js +11 -13
- package/src/commands/reject-turn.js +1 -1
- package/src/commands/restart.js +28 -11
- package/src/commands/resume.js +6 -6
- package/src/commands/role.js +51 -14
- package/src/commands/run.js +5 -11
- package/src/commands/status.js +145 -13
- package/src/commands/step.js +36 -29
- package/src/lib/admission-control.js +14 -12
- package/src/lib/blocked-state.js +150 -0
- package/src/lib/conflict-actions.js +17 -0
- package/src/lib/context-section-parser.js +2 -0
- package/src/lib/continuity-status.js +1 -1
- package/src/lib/coordinator-blocker-presentation.js +127 -0
- package/src/lib/coordinator-event-narrative.js +43 -0
- package/src/lib/coordinator-gate-approval.js +98 -0
- package/src/lib/coordinator-gate-evaluation-presentation.js +57 -0
- package/src/lib/coordinator-next-actions.js +128 -0
- package/src/lib/coordinator-pending-gate-presentation.js +79 -0
- package/src/lib/coordinator-presentation-detail.js +11 -0
- package/src/lib/coordinator-repo-snapshots.js +53 -0
- package/src/lib/coordinator-repo-status-presentation.js +134 -0
- package/src/lib/dashboard/actions.js +105 -29
- package/src/lib/dashboard/bridge-server.js +7 -0
- package/src/lib/dashboard/coordinator-blockers.js +17 -0
- package/src/lib/dashboard/coordinator-repo-status.js +50 -0
- package/src/lib/dashboard/coordinator-timeout-status.js +34 -11
- package/src/lib/dashboard/state-reader.js +36 -1
- package/src/lib/dispatch-bundle.js +23 -0
- package/src/lib/export-diff.js +70 -38
- package/src/lib/export-verifier.js +3 -0
- package/src/lib/history-diff-summary.js +249 -0
- package/src/lib/manual-qa-fallback.js +18 -0
- package/src/lib/normalized-config.js +27 -22
- package/src/lib/recent-event-summary.js +132 -0
- package/src/lib/repo-decisions.js +69 -28
- package/src/lib/report.js +353 -145
- package/src/lib/run-diff.js +4 -0
- package/src/lib/runtime-capabilities.js +222 -0
package/src/lib/report.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { verifyExportArtifact } from './export-verifier.js';
|
|
2
2
|
import { buildDelegationSummary } from './export.js';
|
|
3
3
|
import { normalizeRunProvenance, summarizeRunProvenance } from './run-provenance.js';
|
|
4
|
+
import { deriveGovernedRunNextActions, deriveRuntimeBlockedGuidance } from './blocked-state.js';
|
|
5
|
+
import {
|
|
6
|
+
buildRecentEventSummary,
|
|
7
|
+
formatRecentEventSummaryLine,
|
|
8
|
+
} from './recent-event-summary.js';
|
|
9
|
+
import {
|
|
10
|
+
deriveCoordinatorNextActions,
|
|
11
|
+
} from './coordinator-next-actions.js';
|
|
12
|
+
import { buildCoordinatorRepoStatusEntries } from './coordinator-repo-status-presentation.js';
|
|
13
|
+
import { summarizeCoordinatorEvent } from './coordinator-event-narrative.js';
|
|
4
14
|
|
|
5
15
|
export const GOVERNANCE_REPORT_VERSION = '0.1';
|
|
6
16
|
|
|
@@ -77,6 +87,18 @@ function extractDashboardSessionSummary(artifact) {
|
|
|
77
87
|
return normalizeDashboardSessionSummary(artifact.summary?.dashboard_session);
|
|
78
88
|
}
|
|
79
89
|
|
|
90
|
+
function extractRunEventTimeline(artifact) {
|
|
91
|
+
const data = extractFileData(artifact, '.agentxchain/events.jsonl');
|
|
92
|
+
if (!Array.isArray(data)) return [];
|
|
93
|
+
return data.filter((entry) => entry && typeof entry === 'object' && !Array.isArray(entry));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function formatRecentEventDetail(summary) {
|
|
97
|
+
if (!summary?.latest_event) return 'No event recorded';
|
|
98
|
+
const latest = summary.latest_event;
|
|
99
|
+
return `${latest.summary || latest.event_type || 'unknown_event'} at ${latest.timestamp || 'unknown'}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
80
102
|
function formatDashboardSessionLine(session) {
|
|
81
103
|
if (!session) return null;
|
|
82
104
|
switch (session.status) {
|
|
@@ -244,6 +266,35 @@ function formatTokenCount(value) {
|
|
|
244
266
|
return value.toLocaleString('en-US');
|
|
245
267
|
}
|
|
246
268
|
|
|
269
|
+
function formatRepoDecisionAuthority(level, role, source) {
|
|
270
|
+
if (typeof level !== 'number') return null;
|
|
271
|
+
const roleLabel = role || 'unknown';
|
|
272
|
+
if (source === 'human_default') return `${level} (${roleLabel}, human default)`;
|
|
273
|
+
if (source === 'unknown_role') return `${level} (${roleLabel}, role missing from config)`;
|
|
274
|
+
return `${level} (${roleLabel})`;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function buildRepoDecisionSummaryLines(summary) {
|
|
278
|
+
if (!summary) return [];
|
|
279
|
+
const operatorSummary = summary.operator_summary || {};
|
|
280
|
+
const categories = Array.isArray(operatorSummary.active_categories) && operatorSummary.active_categories.length > 0
|
|
281
|
+
? operatorSummary.active_categories.join(', ')
|
|
282
|
+
: 'none active';
|
|
283
|
+
const authority = formatRepoDecisionAuthority(
|
|
284
|
+
operatorSummary.highest_active_authority_level,
|
|
285
|
+
operatorSummary.highest_active_authority_role,
|
|
286
|
+
operatorSummary.highest_active_authority_source,
|
|
287
|
+
) || '—';
|
|
288
|
+
const superseding = operatorSummary.superseding_active_count || 0;
|
|
289
|
+
const overridden = operatorSummary.overridden_with_successor_count || 0;
|
|
290
|
+
|
|
291
|
+
return [
|
|
292
|
+
`Categories: ${categories}`,
|
|
293
|
+
`Highest authority: ${authority}`,
|
|
294
|
+
`Lineage: ${superseding} active superseding earlier decision${superseding === 1 ? '' : 's'} | ${overridden} overridden with recorded successor${overridden === 1 ? '' : 's'}`,
|
|
295
|
+
];
|
|
296
|
+
}
|
|
297
|
+
|
|
247
298
|
export function computeCostSummary(turns) {
|
|
248
299
|
if (!Array.isArray(turns) || turns.length === 0) return null;
|
|
249
300
|
|
|
@@ -554,6 +605,7 @@ function extractRecoverySummary(artifact) {
|
|
|
554
605
|
if (!blockedReason || typeof blockedReason !== 'object' || Array.isArray(blockedReason)) return null;
|
|
555
606
|
const recovery = blockedReason.recovery;
|
|
556
607
|
if (!recovery || typeof recovery !== 'object' || Array.isArray(recovery)) return null;
|
|
608
|
+
const runtimeGuidance = deriveRuntimeBlockedGuidance(artifact.state, artifact.config);
|
|
557
609
|
return {
|
|
558
610
|
category: blockedReason.category || null,
|
|
559
611
|
typed_reason: recovery.typed_reason || null,
|
|
@@ -563,48 +615,10 @@ function extractRecoverySummary(artifact) {
|
|
|
563
615
|
turn_retained: typeof recovery.turn_retained === 'boolean' ? recovery.turn_retained : null,
|
|
564
616
|
blocked_at: blockedReason.blocked_at || null,
|
|
565
617
|
turn_id: blockedReason.turn_id || null,
|
|
618
|
+
runtime_guidance: runtimeGuidance,
|
|
566
619
|
};
|
|
567
620
|
}
|
|
568
621
|
|
|
569
|
-
function summarizeCoordinatorEvent(entry) {
|
|
570
|
-
const type = entry?.type || 'unknown';
|
|
571
|
-
const ts = entry?.timestamp || '';
|
|
572
|
-
switch (type) {
|
|
573
|
-
case 'run_initialized': {
|
|
574
|
-
const repoCount = entry.repo_runs ? Object.keys(entry.repo_runs).length : 0;
|
|
575
|
-
return `Coordinator run initialized with ${repoCount} repo${repoCount !== 1 ? 's' : ''}`;
|
|
576
|
-
}
|
|
577
|
-
case 'turn_dispatched':
|
|
578
|
-
return `Dispatched turn to ${entry.repo_id || 'unknown'} (${entry.role || '?'}) in workstream ${entry.workstream_id || 'unknown'}`;
|
|
579
|
-
case 'acceptance_projection': {
|
|
580
|
-
const turnRef = entry.repo_turn_id ? ` (turn ${entry.repo_turn_id})` : '';
|
|
581
|
-
const summaryText = entry.summary ? ` — ${entry.summary}` : '';
|
|
582
|
-
return `Projected acceptance from ${entry.repo_id || 'unknown'}${turnRef}${summaryText}`;
|
|
583
|
-
}
|
|
584
|
-
case 'context_generated': {
|
|
585
|
-
const upstreamCount = Array.isArray(entry.upstream_repo_ids) ? entry.upstream_repo_ids.length : 0;
|
|
586
|
-
return `Generated cross-repo context for ${entry.target_repo_id || 'unknown'} from ${upstreamCount} upstream repo${upstreamCount !== 1 ? 's' : ''}`;
|
|
587
|
-
}
|
|
588
|
-
case 'phase_transition_requested':
|
|
589
|
-
return `Requested phase transition: ${entry.from || '?'} → ${entry.to || '?'}`;
|
|
590
|
-
case 'phase_transition_approved':
|
|
591
|
-
return `Phase transition approved: ${entry.from || '?'} → ${entry.to || '?'}`;
|
|
592
|
-
case 'run_completion_requested':
|
|
593
|
-
return `Requested run completion (gate: ${entry.gate || 'unknown'})`;
|
|
594
|
-
case 'run_completed':
|
|
595
|
-
return 'Coordinator run completed';
|
|
596
|
-
case 'state_resynced': {
|
|
597
|
-
const resynced = Array.isArray(entry.resynced_repos) ? entry.resynced_repos.length : 0;
|
|
598
|
-
const barrierChanges = Array.isArray(entry.barrier_changes) ? entry.barrier_changes.length : 0;
|
|
599
|
-
return `Resynced state for ${resynced} repo${resynced !== 1 ? 's' : ''}, ${barrierChanges} barrier change${barrierChanges !== 1 ? 's' : ''}`;
|
|
600
|
-
}
|
|
601
|
-
case 'blocked_resolved':
|
|
602
|
-
return `Blocked state resolved: ${entry.from || '?'} → ${entry.to || '?'}`;
|
|
603
|
-
default:
|
|
604
|
-
return `${type} event${ts ? ` at ${ts}` : ''}`;
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
|
|
608
622
|
function extractCoordinatorTimeline(artifact) {
|
|
609
623
|
const data = extractFileData(artifact, '.agentxchain/multirepo/history.jsonl');
|
|
610
624
|
if (!Array.isArray(data) || data.length === 0) return [];
|
|
@@ -683,23 +697,6 @@ function normalizeCoordinatorBlockedReason(blockedReason) {
|
|
|
683
697
|
return null;
|
|
684
698
|
}
|
|
685
699
|
|
|
686
|
-
function detectRunIdMismatches(repos, coordinatorRepoRuns) {
|
|
687
|
-
const mismatches = [];
|
|
688
|
-
for (const repo of repos) {
|
|
689
|
-
if (!repo.ok || !repo.run_id) continue;
|
|
690
|
-
const expected = coordinatorRepoRuns[repo.repo_id]?.run_id;
|
|
691
|
-
if (!expected) continue;
|
|
692
|
-
if (expected !== repo.run_id) {
|
|
693
|
-
mismatches.push({
|
|
694
|
-
repo_id: repo.repo_id,
|
|
695
|
-
expected_run_id: expected,
|
|
696
|
-
actual_run_id: repo.run_id,
|
|
697
|
-
});
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
return mismatches;
|
|
701
|
-
}
|
|
702
|
-
|
|
703
700
|
function normalizePendingGate(pendingGate) {
|
|
704
701
|
if (!pendingGate || typeof pendingGate !== 'object' || Array.isArray(pendingGate)) return null;
|
|
705
702
|
if (typeof pendingGate.gate !== 'string' || pendingGate.gate.length === 0) return null;
|
|
@@ -718,65 +715,6 @@ function normalizePendingGate(pendingGate) {
|
|
|
718
715
|
return normalized;
|
|
719
716
|
}
|
|
720
717
|
|
|
721
|
-
function deriveCoordinatorNextActions({ status, blockedReason, pendingGate, repos, coordinatorRepoRuns, runIdMismatches }) {
|
|
722
|
-
const nextActions = [];
|
|
723
|
-
|
|
724
|
-
if (status === 'blocked') {
|
|
725
|
-
nextActions.push({
|
|
726
|
-
command: 'agentxchain multi resume',
|
|
727
|
-
reason: `Coordinator is blocked${blockedReason ? `: ${blockedReason}` : ''}. Resume after fixing the underlying issue.`,
|
|
728
|
-
});
|
|
729
|
-
if (runIdMismatches && runIdMismatches.length > 0) {
|
|
730
|
-
for (const m of runIdMismatches) {
|
|
731
|
-
nextActions.push({
|
|
732
|
-
command: `# repo_run_id_mismatch: ${m.repo_id}`,
|
|
733
|
-
reason: `Repo "${m.repo_id}" run identity drifted: coordinator expects "${m.expected_run_id}" but repo has "${m.actual_run_id}". Re-initialize the child repo with the correct run or use multi resume after investigation.`,
|
|
734
|
-
});
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
if (pendingGate) {
|
|
738
|
-
nextActions.push({
|
|
739
|
-
command: 'agentxchain multi approve-gate',
|
|
740
|
-
reason: `After resume, approve pending gate "${pendingGate.gate}" (${pendingGate.gate_type}).`,
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
|
-
return nextActions;
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
const driftedRepos = repos
|
|
747
|
-
.filter((repo) => repo.ok)
|
|
748
|
-
.filter((repo) => {
|
|
749
|
-
const coordinatorStatus = coordinatorRepoRuns?.[repo.repo_id]?.status || null;
|
|
750
|
-
return coordinatorStatus && repo.status && coordinatorStatus !== repo.status;
|
|
751
|
-
})
|
|
752
|
-
.map((repo) => repo.repo_id);
|
|
753
|
-
|
|
754
|
-
if (driftedRepos.length > 0) {
|
|
755
|
-
nextActions.push({
|
|
756
|
-
command: 'agentxchain multi resync',
|
|
757
|
-
reason: `Coordinator state disagrees with child repo status for: ${driftedRepos.join(', ')}.`,
|
|
758
|
-
});
|
|
759
|
-
return nextActions;
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
if (pendingGate) {
|
|
763
|
-
nextActions.push({
|
|
764
|
-
command: 'agentxchain multi approve-gate',
|
|
765
|
-
reason: `Coordinator is waiting on pending gate "${pendingGate.gate}" (${pendingGate.gate_type}).`,
|
|
766
|
-
});
|
|
767
|
-
return nextActions;
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
if (status === 'active' || status === 'paused') {
|
|
771
|
-
nextActions.push({
|
|
772
|
-
command: 'agentxchain multi step',
|
|
773
|
-
reason: 'Coordinator has no blocked state or pending gate and can continue.',
|
|
774
|
-
});
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
return nextActions;
|
|
778
|
-
}
|
|
779
|
-
|
|
780
718
|
function extractCoordinatorDecisionDigest(artifact) {
|
|
781
719
|
const data = extractFileData(artifact, '.agentxchain/multirepo/decision-ledger.jsonl');
|
|
782
720
|
if (!Array.isArray(data) || data.length === 0) return [];
|
|
@@ -879,13 +817,37 @@ function extractBarrierLedgerTimeline(artifact) {
|
|
|
879
817
|
|
|
880
818
|
function deriveRepoStatusCounts(repoStatuses) {
|
|
881
819
|
const counts = {};
|
|
882
|
-
|
|
820
|
+
const statuses = Array.isArray(repoStatuses)
|
|
821
|
+
? repoStatuses
|
|
822
|
+
: Object.values(repoStatuses || {});
|
|
823
|
+
for (const status of statuses) {
|
|
883
824
|
const key = status || 'unknown';
|
|
884
825
|
counts[key] = (counts[key] || 0) + 1;
|
|
885
826
|
}
|
|
886
827
|
return counts;
|
|
887
828
|
}
|
|
888
829
|
|
|
830
|
+
function deriveCoordinatorStatusDrifts(repoStatusEntries) {
|
|
831
|
+
return repoStatusEntries
|
|
832
|
+
.filter((entry) => entry?.status_drift)
|
|
833
|
+
.map((entry) => entry.status_drift);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
function deriveCoordinatorTerminalObservabilityNote(status, runIdMismatches, repoStatusDrifts) {
|
|
837
|
+
if (status !== 'completed') return null;
|
|
838
|
+
const driftKinds = [];
|
|
839
|
+
if (Array.isArray(runIdMismatches) && runIdMismatches.length > 0) {
|
|
840
|
+
driftKinds.push('run-id drift');
|
|
841
|
+
}
|
|
842
|
+
if (Array.isArray(repoStatusDrifts) && repoStatusDrifts.length > 0) {
|
|
843
|
+
driftKinds.push('status drift');
|
|
844
|
+
}
|
|
845
|
+
if (driftKinds.length === 0) return null;
|
|
846
|
+
|
|
847
|
+
const verb = driftKinds.length > 1 ? 'remain' : 'remains';
|
|
848
|
+
return `Child repo ${driftKinds.join(' and ')} ${verb} visible for audit, but this coordinator is already completed, so no recovery command is emitted.`;
|
|
849
|
+
}
|
|
850
|
+
|
|
889
851
|
export function extractWorkflowKitArtifacts(artifact) {
|
|
890
852
|
const config = artifact.config;
|
|
891
853
|
if (!config || typeof config !== 'object' || !config.workflow_kit) return null;
|
|
@@ -961,10 +923,12 @@ function buildRunSubject(artifact) {
|
|
|
961
923
|
const gateSummary = extractGateSummary(artifact);
|
|
962
924
|
const intakeLinks = extractIntakeLinks(artifact);
|
|
963
925
|
const recoverySummary = extractRecoverySummary(artifact);
|
|
926
|
+
const nextActions = deriveGovernedRunNextActions(artifact.state, artifact.config);
|
|
964
927
|
const continuity = extractContinuityMetadata(artifact);
|
|
965
928
|
const governanceEvents = extractGovernanceEventDigest(artifact);
|
|
966
929
|
const delegationSummary = extractDelegationSummary(artifact);
|
|
967
930
|
const dashboardSession = extractDashboardSessionSummary(artifact);
|
|
931
|
+
const recentEventSummary = buildRecentEventSummary(extractRunEventTimeline(artifact));
|
|
968
932
|
|
|
969
933
|
return {
|
|
970
934
|
kind: 'governed_run',
|
|
@@ -992,6 +956,7 @@ function buildRunSubject(artifact) {
|
|
|
992
956
|
budget_status: normalizeBudgetStatus(artifact.state?.budget_status),
|
|
993
957
|
cost_summary: computeCostSummary(turns),
|
|
994
958
|
dashboard_session: dashboardSession,
|
|
959
|
+
recent_event_summary: recentEventSummary,
|
|
995
960
|
created_at: timing.created_at,
|
|
996
961
|
completed_at: timing.completed_at,
|
|
997
962
|
duration_seconds: timing.duration_seconds,
|
|
@@ -1006,6 +971,7 @@ function buildRunSubject(artifact) {
|
|
|
1006
971
|
gate_summary: gateSummary,
|
|
1007
972
|
intake_links: intakeLinks,
|
|
1008
973
|
recovery_summary: recoverySummary,
|
|
974
|
+
next_actions: nextActions,
|
|
1009
975
|
continuity,
|
|
1010
976
|
workflow_kit_artifacts: extractWorkflowKitArtifacts(artifact),
|
|
1011
977
|
repo_decisions: artifact.summary?.repo_decisions || null,
|
|
@@ -1055,8 +1021,8 @@ function extractRecoveryReportSummary(artifact) {
|
|
|
1055
1021
|
|
|
1056
1022
|
function buildCoordinatorSubject(artifact) {
|
|
1057
1023
|
const coordinatorState = extractFileData(artifact, '.agentxchain/multirepo/state.json') || {};
|
|
1058
|
-
const
|
|
1059
|
-
const
|
|
1024
|
+
const coordinatorStatus = coordinatorState?.status || artifact.summary?.status || null;
|
|
1025
|
+
const coordinatorPhase = coordinatorState?.phase || artifact.summary?.phase || null;
|
|
1060
1026
|
const repos = Object.entries(artifact.repos || {})
|
|
1061
1027
|
.sort(([left], [right]) => left.localeCompare(right, 'en'))
|
|
1062
1028
|
.map(([repoId, repoEntry]) => {
|
|
@@ -1098,15 +1064,31 @@ function buildCoordinatorSubject(artifact) {
|
|
|
1098
1064
|
const timing = computeCoordinatorTiming(artifact, coordinatorTimeline);
|
|
1099
1065
|
const blockedReason = normalizeCoordinatorBlockedReason(coordinatorState.blocked_reason);
|
|
1100
1066
|
const pendingGate = normalizePendingGate(coordinatorState.pending_gate);
|
|
1101
|
-
const
|
|
1067
|
+
const repoStatusEntries = buildCoordinatorRepoStatusEntries({
|
|
1068
|
+
config: artifact.config,
|
|
1069
|
+
coordinatorRepoRuns: coordinatorState.repo_runs || {},
|
|
1070
|
+
repoSnapshots: repos,
|
|
1071
|
+
});
|
|
1072
|
+
const repoStatusCounts = deriveRepoStatusCounts(repoStatusEntries.map((entry) => entry.status));
|
|
1073
|
+
const runIdMismatches = repoStatusEntries
|
|
1074
|
+
.filter((entry) => entry?.run_id_mismatch)
|
|
1075
|
+
.map((entry) => entry.run_id_mismatch);
|
|
1076
|
+
const repoStatusDrifts = deriveCoordinatorStatusDrifts(repoStatusEntries);
|
|
1077
|
+
const recentCoordinatorEvents = buildRecentEventSummary(coordinatorTimeline);
|
|
1078
|
+
const recentChildRepoEvents = buildRecentEventSummary(extractAggregatedEventTimeline(artifact));
|
|
1102
1079
|
const nextActions = deriveCoordinatorNextActions({
|
|
1103
|
-
status:
|
|
1080
|
+
status: coordinatorStatus,
|
|
1104
1081
|
blockedReason,
|
|
1105
1082
|
pendingGate,
|
|
1106
1083
|
repos,
|
|
1107
1084
|
coordinatorRepoRuns: coordinatorState.repo_runs || {},
|
|
1108
1085
|
runIdMismatches,
|
|
1109
1086
|
});
|
|
1087
|
+
const terminalObservabilityNote = deriveCoordinatorTerminalObservabilityNote(
|
|
1088
|
+
coordinatorStatus,
|
|
1089
|
+
runIdMismatches,
|
|
1090
|
+
repoStatusDrifts,
|
|
1091
|
+
);
|
|
1110
1092
|
|
|
1111
1093
|
return {
|
|
1112
1094
|
kind: 'coordinator_workspace',
|
|
@@ -1118,12 +1100,16 @@ function buildCoordinatorSubject(artifact) {
|
|
|
1118
1100
|
workstream_count: artifact.coordinator?.workstream_count || 0,
|
|
1119
1101
|
},
|
|
1120
1102
|
run: {
|
|
1121
|
-
super_run_id: artifact.summary?.super_run_id || null,
|
|
1122
|
-
status:
|
|
1123
|
-
phase:
|
|
1103
|
+
super_run_id: coordinatorState?.super_run_id || artifact.summary?.super_run_id || null,
|
|
1104
|
+
status: coordinatorStatus,
|
|
1105
|
+
phase: coordinatorPhase,
|
|
1124
1106
|
blocked_reason: blockedReason,
|
|
1125
1107
|
pending_gate: pendingGate,
|
|
1126
1108
|
run_id_mismatches: runIdMismatches,
|
|
1109
|
+
repo_status_drifts: repoStatusDrifts,
|
|
1110
|
+
terminal_observability_note: terminalObservabilityNote,
|
|
1111
|
+
recent_coordinator_events: recentCoordinatorEvents,
|
|
1112
|
+
recent_child_repo_events: recentChildRepoEvents,
|
|
1127
1113
|
next_actions: nextActions,
|
|
1128
1114
|
created_at: timing.created_at,
|
|
1129
1115
|
completed_at: timing.completed_at,
|
|
@@ -1264,6 +1250,10 @@ export function formatGovernanceReportText(report) {
|
|
|
1264
1250
|
if (run.dashboard_session) {
|
|
1265
1251
|
lines.push(`Dashboard session: ${formatDashboardSessionLine(run.dashboard_session)}`);
|
|
1266
1252
|
}
|
|
1253
|
+
if (run.recent_event_summary) {
|
|
1254
|
+
lines.push(`Recent events: ${formatRecentEventSummaryLine(run.recent_event_summary)}`);
|
|
1255
|
+
lines.push(`Latest event: ${formatRecentEventDetail(run.recent_event_summary)}`);
|
|
1256
|
+
}
|
|
1267
1257
|
|
|
1268
1258
|
lines.push(
|
|
1269
1259
|
`History entries: ${artifacts.history_entries}`,
|
|
@@ -1316,9 +1306,12 @@ export function formatGovernanceReportText(report) {
|
|
|
1316
1306
|
}
|
|
1317
1307
|
}
|
|
1318
1308
|
|
|
1319
|
-
if (run.repo_decisions
|
|
1309
|
+
if (run.repo_decisions) {
|
|
1320
1310
|
lines.push('', 'Repo Decisions:');
|
|
1321
1311
|
lines.push(` Active: ${run.repo_decisions.active_count} Overridden: ${run.repo_decisions.overridden_count}`);
|
|
1312
|
+
for (const summaryLine of buildRepoDecisionSummaryLines(run.repo_decisions)) {
|
|
1313
|
+
lines.push(` ${summaryLine}`);
|
|
1314
|
+
}
|
|
1322
1315
|
for (const d of run.repo_decisions.active) {
|
|
1323
1316
|
const supersedes = d.overrides ? ` | supersedes ${d.overrides}` : '';
|
|
1324
1317
|
const authority = d.authority_level == null ? '' : ` | authority ${d.authority_level}${d.authority_source === 'human_default' ? ' (human default)' : ''}`;
|
|
@@ -1425,6 +1418,20 @@ export function formatGovernanceReportText(report) {
|
|
|
1425
1418
|
lines.push(` Action: ${run.recovery_summary.recovery_action || 'n/a'}`);
|
|
1426
1419
|
lines.push(` Detail: ${run.recovery_summary.detail || 'n/a'}`);
|
|
1427
1420
|
lines.push(` Turn retained: ${run.recovery_summary.turn_retained == null ? 'n/a' : yesNo(run.recovery_summary.turn_retained)}`);
|
|
1421
|
+
if (Array.isArray(run.recovery_summary.runtime_guidance) && run.recovery_summary.runtime_guidance.length > 0) {
|
|
1422
|
+
lines.push(' Runtime guidance:');
|
|
1423
|
+
for (const entry of run.recovery_summary.runtime_guidance) {
|
|
1424
|
+
lines.push(` - ${entry.code} | ${entry.command} | ${entry.reason}`);
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
if (run.next_actions && run.next_actions.length > 0) {
|
|
1430
|
+
lines.push('', 'Next Actions:');
|
|
1431
|
+
for (let i = 0; i < run.next_actions.length; i++) {
|
|
1432
|
+
const action = run.next_actions[i];
|
|
1433
|
+
lines.push(` ${i + 1}. ${action.command} | ${action.reason}`);
|
|
1434
|
+
}
|
|
1428
1435
|
}
|
|
1429
1436
|
|
|
1430
1437
|
if (run.continuity) {
|
|
@@ -1486,6 +1493,12 @@ export function formatGovernanceReportText(report) {
|
|
|
1486
1493
|
lines.push(` - ${m.repo_id}: expected ${m.expected_run_id}, actual ${m.actual_run_id}`);
|
|
1487
1494
|
}
|
|
1488
1495
|
}
|
|
1496
|
+
if (run.repo_status_drifts && run.repo_status_drifts.length > 0) {
|
|
1497
|
+
lines.push(`Repo status drift: ${run.repo_status_drifts.length}`);
|
|
1498
|
+
for (const drift of run.repo_status_drifts) {
|
|
1499
|
+
lines.push(` - ${drift.repo_id}: coordinator ${drift.coordinator_status || 'unknown'}, repo ${drift.repo_status || 'unknown'}`);
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1489
1502
|
|
|
1490
1503
|
lines.push(
|
|
1491
1504
|
`Started: ${run.created_at || 'n/a'}`,
|
|
@@ -1506,6 +1519,17 @@ export function formatGovernanceReportText(report) {
|
|
|
1506
1519
|
if (run.pending_gate) {
|
|
1507
1520
|
lines.push(`Pending gate: ${run.pending_gate.gate} (${run.pending_gate.gate_type})`);
|
|
1508
1521
|
}
|
|
1522
|
+
if (run.recent_coordinator_events) {
|
|
1523
|
+
lines.push(`Recent coordinator events: ${formatRecentEventSummaryLine(run.recent_coordinator_events)}`);
|
|
1524
|
+
lines.push(`Latest coordinator event: ${formatRecentEventDetail(run.recent_coordinator_events)}`);
|
|
1525
|
+
}
|
|
1526
|
+
if (run.recent_child_repo_events) {
|
|
1527
|
+
lines.push(`Recent child repo events: ${formatRecentEventSummaryLine(run.recent_child_repo_events)}`);
|
|
1528
|
+
lines.push(`Latest child repo event: ${formatRecentEventDetail(run.recent_child_repo_events)}`);
|
|
1529
|
+
}
|
|
1530
|
+
if (run.terminal_observability_note) {
|
|
1531
|
+
lines.push(`Terminal drift note: ${run.terminal_observability_note}`);
|
|
1532
|
+
}
|
|
1509
1533
|
|
|
1510
1534
|
if (run.next_actions && run.next_actions.length > 0) {
|
|
1511
1535
|
lines.push('', 'Next Actions:');
|
|
@@ -1776,6 +1800,10 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1776
1800
|
if (run.dashboard_session) {
|
|
1777
1801
|
lines.push(`- Dashboard session: \`${formatDashboardSessionLine(run.dashboard_session)}\``);
|
|
1778
1802
|
}
|
|
1803
|
+
if (run.recent_event_summary) {
|
|
1804
|
+
lines.push(`- Recent events: \`${formatRecentEventSummaryLine(run.recent_event_summary)}\``);
|
|
1805
|
+
lines.push(`- Latest event: ${formatRecentEventDetail(run.recent_event_summary)}`);
|
|
1806
|
+
}
|
|
1779
1807
|
|
|
1780
1808
|
lines.push(
|
|
1781
1809
|
`- History entries: ${artifacts.history_entries}`,
|
|
@@ -1828,14 +1856,19 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1828
1856
|
}
|
|
1829
1857
|
}
|
|
1830
1858
|
|
|
1831
|
-
if (run.repo_decisions
|
|
1859
|
+
if (run.repo_decisions) {
|
|
1832
1860
|
lines.push('', '## Repo Decisions', '');
|
|
1833
1861
|
lines.push(`Active: ${run.repo_decisions.active_count} | Overridden: ${run.repo_decisions.overridden_count}`, '');
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
lines.push(
|
|
1862
|
+
for (const summaryLine of buildRepoDecisionSummaryLines(run.repo_decisions)) {
|
|
1863
|
+
lines.push(`${summaryLine}`, '');
|
|
1864
|
+
}
|
|
1865
|
+
if (run.repo_decisions.active.length > 0) {
|
|
1866
|
+
lines.push('| ID | Category | Statement | Role | Authority | Run | Supersedes |', '|----|----------|-----------|------|-----------|-----|------------|');
|
|
1867
|
+
for (const d of run.repo_decisions.active) {
|
|
1868
|
+
const stmt = (d.statement || '').replace(/\|/g, '\\|');
|
|
1869
|
+
const authority = d.authority_level == null ? '—' : `${d.authority_level}${d.authority_source === 'human_default' ? ' (human default)' : ''}`;
|
|
1870
|
+
lines.push(`| ${d.id} | ${d.category} | ${stmt} | ${d.role || '—'} | ${authority} | \`${(d.run_id || '').slice(0, 12)}\` | ${d.overrides || '—'} |`);
|
|
1871
|
+
}
|
|
1839
1872
|
}
|
|
1840
1873
|
if (run.repo_decisions.overridden?.length > 0) {
|
|
1841
1874
|
lines.push('', 'Overridden decisions:', '');
|
|
@@ -1944,6 +1977,20 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
1944
1977
|
lines.push(`- Action: \`${run.recovery_summary.recovery_action || 'n/a'}\``);
|
|
1945
1978
|
lines.push(`- Detail: ${run.recovery_summary.detail || 'n/a'}`);
|
|
1946
1979
|
lines.push(`- Turn retained: \`${run.recovery_summary.turn_retained == null ? 'n/a' : yesNo(run.recovery_summary.turn_retained)}\``);
|
|
1980
|
+
if (Array.isArray(run.recovery_summary.runtime_guidance) && run.recovery_summary.runtime_guidance.length > 0) {
|
|
1981
|
+
lines.push('- Runtime guidance:');
|
|
1982
|
+
for (const entry of run.recovery_summary.runtime_guidance) {
|
|
1983
|
+
lines.push(` - \`${entry.code}\` — \`${entry.command}\`: ${entry.reason}`);
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
if (run.next_actions && run.next_actions.length > 0) {
|
|
1989
|
+
lines.push('', '## Next Actions', '');
|
|
1990
|
+
for (let i = 0; i < run.next_actions.length; i++) {
|
|
1991
|
+
const action = run.next_actions[i];
|
|
1992
|
+
lines.push(`${i + 1}. \`${action.command}\`: ${action.reason}`);
|
|
1993
|
+
}
|
|
1947
1994
|
}
|
|
1948
1995
|
|
|
1949
1996
|
if (run.continuity) {
|
|
@@ -2008,6 +2055,12 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
2008
2055
|
mdLines.push(` - \`${m.repo_id}\`: expected \`${m.expected_run_id}\`, actual \`${m.actual_run_id}\``);
|
|
2009
2056
|
}
|
|
2010
2057
|
}
|
|
2058
|
+
if (run.repo_status_drifts && run.repo_status_drifts.length > 0) {
|
|
2059
|
+
mdLines.push(`- **Repo status drift: ${run.repo_status_drifts.length}**`);
|
|
2060
|
+
for (const drift of run.repo_status_drifts) {
|
|
2061
|
+
mdLines.push(` - \`${drift.repo_id}\`: coordinator \`${drift.coordinator_status || 'unknown'}\`, repo \`${drift.repo_status || 'unknown'}\``);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2011
2064
|
|
|
2012
2065
|
mdLines.push(
|
|
2013
2066
|
`- Started: \`${run.created_at || 'n/a'}\``,
|
|
@@ -2028,6 +2081,17 @@ export function formatGovernanceReportMarkdown(report) {
|
|
|
2028
2081
|
if (run.pending_gate) {
|
|
2029
2082
|
mdLines.push(`- Pending gate: \`${run.pending_gate.gate}\` (\`${run.pending_gate.gate_type}\`)`);
|
|
2030
2083
|
}
|
|
2084
|
+
if (run.recent_coordinator_events) {
|
|
2085
|
+
mdLines.push(`- Recent coordinator events: \`${formatRecentEventSummaryLine(run.recent_coordinator_events)}\``);
|
|
2086
|
+
mdLines.push(`- Latest coordinator event: ${formatRecentEventDetail(run.recent_coordinator_events)}`);
|
|
2087
|
+
}
|
|
2088
|
+
if (run.recent_child_repo_events) {
|
|
2089
|
+
mdLines.push(`- Recent child repo events: \`${formatRecentEventSummaryLine(run.recent_child_repo_events)}\``);
|
|
2090
|
+
mdLines.push(`- Latest child repo event: ${formatRecentEventDetail(run.recent_child_repo_events)}`);
|
|
2091
|
+
}
|
|
2092
|
+
if (run.terminal_observability_note) {
|
|
2093
|
+
mdLines.push(`- Terminal drift note: ${run.terminal_observability_note}`);
|
|
2094
|
+
}
|
|
2031
2095
|
|
|
2032
2096
|
if (run.next_actions && run.next_actions.length > 0) {
|
|
2033
2097
|
mdLines.push('', '## Next Actions', '');
|
|
@@ -2403,6 +2467,10 @@ function renderRunHtml(report) {
|
|
|
2403
2467
|
if (summarizeRunProvenance(run.provenance)) metaPairs.push(['Provenance', `<code>${esc(summarizeRunProvenance(run.provenance))}</code>`]);
|
|
2404
2468
|
if (run.inherited_context?.parent_run_id) metaPairs.push(['Inherited from', `<code>${esc(run.inherited_context.parent_run_id)}</code> (${esc(run.inherited_context.parent_status || 'unknown')})`]);
|
|
2405
2469
|
if (run.dashboard_session) metaPairs.push(['Dashboard', `<code>${esc(formatDashboardSessionLine(run.dashboard_session))}</code>`]);
|
|
2470
|
+
if (run.recent_event_summary) {
|
|
2471
|
+
metaPairs.push(['Recent events', esc(formatRecentEventSummaryLine(run.recent_event_summary))]);
|
|
2472
|
+
metaPairs.push(['Latest event', esc(formatRecentEventDetail(run.recent_event_summary))]);
|
|
2473
|
+
}
|
|
2406
2474
|
|
|
2407
2475
|
metaPairs.push(
|
|
2408
2476
|
['History entries', String(artifacts.history_entries)],
|
|
@@ -2466,20 +2534,25 @@ function renderRunHtml(report) {
|
|
|
2466
2534
|
}
|
|
2467
2535
|
|
|
2468
2536
|
// Repo Decisions
|
|
2469
|
-
if (run.repo_decisions
|
|
2537
|
+
if (run.repo_decisions) {
|
|
2470
2538
|
let rdHtml = `<p>Active: ${run.repo_decisions.active_count} | Overridden: ${run.repo_decisions.overridden_count}</p>`;
|
|
2471
|
-
rdHtml +=
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2539
|
+
rdHtml += buildRepoDecisionSummaryLines(run.repo_decisions)
|
|
2540
|
+
.map((line) => `<p>${esc(line)}</p>`)
|
|
2541
|
+
.join('');
|
|
2542
|
+
if (run.repo_decisions.active.length > 0) {
|
|
2543
|
+
rdHtml += htmlTable(
|
|
2544
|
+
['ID', 'Category', 'Statement', 'Role', 'Authority', 'Run', 'Supersedes'],
|
|
2545
|
+
run.repo_decisions.active.map((d) => [
|
|
2546
|
+
esc(d.id),
|
|
2547
|
+
esc(d.category),
|
|
2548
|
+
esc(d.statement || ''),
|
|
2549
|
+
esc(d.role || '\u2014'),
|
|
2550
|
+
esc(d.authority_level == null ? '\u2014' : `${d.authority_level}${d.authority_source === 'human_default' ? ' (human default)' : ''}`),
|
|
2551
|
+
`<code>${esc((d.run_id || '').slice(0, 12))}</code>`,
|
|
2552
|
+
esc(d.overrides || '\u2014'),
|
|
2553
|
+
]),
|
|
2554
|
+
);
|
|
2555
|
+
}
|
|
2483
2556
|
if (run.repo_decisions.overridden?.length > 0) {
|
|
2484
2557
|
rdHtml += htmlTable(
|
|
2485
2558
|
['ID', 'Statement', 'Authority', 'Overridden By'],
|
|
@@ -2595,14 +2668,28 @@ function renderRunHtml(report) {
|
|
|
2595
2668
|
// Recovery
|
|
2596
2669
|
if (run.recovery_summary) {
|
|
2597
2670
|
const rs = run.recovery_summary;
|
|
2598
|
-
|
|
2671
|
+
let recoveryHtml = htmlDl([
|
|
2599
2672
|
['Category', `<code>${esc(rs.category || 'unknown')}</code>`],
|
|
2600
2673
|
['Typed reason', `<code>${esc(rs.typed_reason || 'unknown')}</code>`],
|
|
2601
2674
|
['Owner', `<code>${esc(rs.owner || 'unknown')}</code>`],
|
|
2602
2675
|
['Action', `<code>${esc(rs.recovery_action || 'n/a')}</code>`],
|
|
2603
2676
|
['Detail', esc(rs.detail || 'n/a')],
|
|
2604
2677
|
['Turn retained', rs.turn_retained == null ? 'n/a' : (rs.turn_retained ? 'yes' : 'no')],
|
|
2605
|
-
])
|
|
2678
|
+
]);
|
|
2679
|
+
if (Array.isArray(rs.runtime_guidance) && rs.runtime_guidance.length > 0) {
|
|
2680
|
+
const items = '<ul>' + rs.runtime_guidance.map((entry) =>
|
|
2681
|
+
`<li><code>${esc(entry.code)}</code> — <code>${esc(entry.command)}</code>: ${esc(entry.reason)}</li>`
|
|
2682
|
+
).join('') + '</ul>';
|
|
2683
|
+
recoveryHtml += htmlSection('Runtime Guidance', items);
|
|
2684
|
+
}
|
|
2685
|
+
sections.push(`<div class="section">${htmlSection('Recovery', recoveryHtml)}</div>`);
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
if (run.next_actions?.length > 0) {
|
|
2689
|
+
const nextHtml = '<ol>' + run.next_actions.map((action) =>
|
|
2690
|
+
`<li><code>${esc(action.command)}</code>: ${esc(action.reason)}</li>`
|
|
2691
|
+
).join('') + '</ol>';
|
|
2692
|
+
sections.push(`<div class="section">${htmlSection('Next Actions', nextHtml)}</div>`);
|
|
2606
2693
|
}
|
|
2607
2694
|
|
|
2608
2695
|
// Continuity
|
|
@@ -2659,6 +2746,9 @@ function renderCoordinatorHtml(report) {
|
|
|
2659
2746
|
if (run.run_id_mismatches?.length > 0) {
|
|
2660
2747
|
metaPairs.push(['Run ID mismatches', `<strong class="warn">${run.run_id_mismatches.length}</strong>`]);
|
|
2661
2748
|
}
|
|
2749
|
+
if (run.repo_status_drifts?.length > 0) {
|
|
2750
|
+
metaPairs.push(['Repo status drift', `<strong class="warn">${run.repo_status_drifts.length}</strong>`]);
|
|
2751
|
+
}
|
|
2662
2752
|
|
|
2663
2753
|
metaPairs.push(
|
|
2664
2754
|
['Started', `<code>${esc(run.created_at || 'n/a')}</code>`],
|
|
@@ -2672,6 +2762,17 @@ function renderCoordinatorHtml(report) {
|
|
|
2672
2762
|
if (run.completed_at) metaPairs.push(['Completed', `<code>${esc(run.completed_at)}</code>`]);
|
|
2673
2763
|
if (run.duration_seconds != null) metaPairs.push(['Duration', `<code>${run.duration_seconds}s</code>`]);
|
|
2674
2764
|
if (run.pending_gate) metaPairs.push(['Pending gate', `<code>${esc(run.pending_gate.gate)}</code> (<code>${esc(run.pending_gate.gate_type)}</code>)`]);
|
|
2765
|
+
if (run.recent_coordinator_events) {
|
|
2766
|
+
metaPairs.push(['Recent coordinator events', esc(formatRecentEventSummaryLine(run.recent_coordinator_events))]);
|
|
2767
|
+
metaPairs.push(['Latest coordinator event', esc(formatRecentEventDetail(run.recent_coordinator_events))]);
|
|
2768
|
+
}
|
|
2769
|
+
if (run.recent_child_repo_events) {
|
|
2770
|
+
metaPairs.push(['Recent child repo events', esc(formatRecentEventSummaryLine(run.recent_child_repo_events))]);
|
|
2771
|
+
metaPairs.push(['Latest child repo event', esc(formatRecentEventDetail(run.recent_child_repo_events))]);
|
|
2772
|
+
}
|
|
2773
|
+
if (run.terminal_observability_note) {
|
|
2774
|
+
metaPairs.push(['Terminal drift note', esc(run.terminal_observability_note)]);
|
|
2775
|
+
}
|
|
2675
2776
|
|
|
2676
2777
|
sections.push(`<div class="meta">${htmlDl(metaPairs)}</div>`);
|
|
2677
2778
|
|
|
@@ -2681,6 +2782,17 @@ function renderCoordinatorHtml(report) {
|
|
|
2681
2782
|
sections.push(`<div class="section">${htmlSection('Next Actions', naHtml)}</div>`);
|
|
2682
2783
|
}
|
|
2683
2784
|
|
|
2785
|
+
if (run.repo_status_drifts?.length > 0) {
|
|
2786
|
+
const driftRows = run.repo_status_drifts.map((drift) => [
|
|
2787
|
+
`<code>${esc(drift.repo_id)}</code>`,
|
|
2788
|
+
`<code>${esc(drift.coordinator_status || 'unknown')}</code>`,
|
|
2789
|
+
`<code>${esc(drift.repo_status || 'unknown')}</code>`,
|
|
2790
|
+
]);
|
|
2791
|
+
sections.push(
|
|
2792
|
+
`<div class="section">${htmlSection('Repo Status Drift', htmlTable(['Repo', 'Coordinator', 'Repo Authority'], driftRows))}</div>`,
|
|
2793
|
+
);
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2684
2796
|
// Coordinator Timeline
|
|
2685
2797
|
if (coordinator_timeline?.length > 0) {
|
|
2686
2798
|
const tlRows = coordinator_timeline.map((ev, i) => [String(i + 1), `<code>${esc(ev.type)}</code>`, `<code>${esc(ev.timestamp || 'n/a')}</code>`, esc(ev.summary)]);
|
|
@@ -2805,6 +2917,102 @@ function renderCoordinatorHtml(report) {
|
|
|
2805
2917
|
if (repo.gate_summary?.length > 0) {
|
|
2806
2918
|
repoHtml += htmlSection('Gate Outcomes', '<ul>' + repo.gate_summary.map((g) => `<li><code>${esc(g.gate_id)}</code>: ${badge(g.status)}</li>`).join('') + '</ul>', 4);
|
|
2807
2919
|
}
|
|
2920
|
+
if (repo.gate_failures?.length > 0) {
|
|
2921
|
+
let gateFailureHtml = '<ul>';
|
|
2922
|
+
for (const failure of repo.gate_failures) {
|
|
2923
|
+
const request = failure.gate_type === 'run_completion'
|
|
2924
|
+
? 'run completion'
|
|
2925
|
+
: `${esc(failure.from_phase || '?')} → ${esc(failure.to_phase || '?')}`;
|
|
2926
|
+
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}`;
|
|
2927
|
+
if (failure.reasons?.length > 0) {
|
|
2928
|
+
gateFailureHtml += '<ul>' + failure.reasons.map((reason) => `<li>${esc(reason)}</li>`).join('') + '</ul>';
|
|
2929
|
+
}
|
|
2930
|
+
gateFailureHtml += '</li>';
|
|
2931
|
+
}
|
|
2932
|
+
gateFailureHtml += '</ul>';
|
|
2933
|
+
repoHtml += htmlSection('Gate Failures', gateFailureHtml, 4);
|
|
2934
|
+
}
|
|
2935
|
+
if (repo.approval_policy_events?.length > 0) {
|
|
2936
|
+
let approvalHtml = '<ul>';
|
|
2937
|
+
for (const evt of repo.approval_policy_events) {
|
|
2938
|
+
const transition = evt.gate_type === 'run_completion'
|
|
2939
|
+
? 'run completion'
|
|
2940
|
+
: `${esc(evt.from_phase || '?')} → ${esc(evt.to_phase || '?')}`;
|
|
2941
|
+
const rule = evt.matched_rule
|
|
2942
|
+
? ` — rule: <code>${esc(typeof evt.matched_rule === 'object' ? JSON.stringify(evt.matched_rule) : evt.matched_rule)}</code>`
|
|
2943
|
+
: '';
|
|
2944
|
+
approvalHtml += `<li><strong>${esc(evt.action || 'unknown')}</strong> (${esc(evt.gate_type || 'unknown')}) ${transition}${rule} at <code>${esc(evt.timestamp || 'n/a')}</code>`;
|
|
2945
|
+
if (evt.reason) approvalHtml += `<br>${esc(evt.reason)}`;
|
|
2946
|
+
approvalHtml += '</li>';
|
|
2947
|
+
}
|
|
2948
|
+
approvalHtml += '</ul>';
|
|
2949
|
+
repoHtml += htmlSection('Approval Policy', approvalHtml, 4);
|
|
2950
|
+
}
|
|
2951
|
+
if (repo.governance_events?.length > 0) {
|
|
2952
|
+
let governanceHtml = '<ul>';
|
|
2953
|
+
for (const evt of repo.governance_events) {
|
|
2954
|
+
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>`;
|
|
2955
|
+
}
|
|
2956
|
+
governanceHtml += '</ul>';
|
|
2957
|
+
repoHtml += htmlSection('Governance Events', governanceHtml, 4);
|
|
2958
|
+
}
|
|
2959
|
+
if (repo.timeout_events?.length > 0) {
|
|
2960
|
+
let timeoutHtml = '<ul>';
|
|
2961
|
+
for (const evt of repo.timeout_events) {
|
|
2962
|
+
const label = evt.type === 'timeout_warning' ? 'Warning'
|
|
2963
|
+
: evt.type === 'timeout_skip' ? 'Skip'
|
|
2964
|
+
: evt.type === 'timeout_skip_failed' ? 'Skip Failed'
|
|
2965
|
+
: 'Escalation';
|
|
2966
|
+
const elapsed = evt.elapsed_minutes != null ? `${evt.elapsed_minutes}m` : '?';
|
|
2967
|
+
const limit = evt.limit_minutes != null ? `${evt.limit_minutes}m` : '?';
|
|
2968
|
+
const exceeded = evt.exceeded_by_minutes != null ? ` (+${evt.exceeded_by_minutes}m)` : '';
|
|
2969
|
+
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>`;
|
|
2970
|
+
}
|
|
2971
|
+
timeoutHtml += '</ul>';
|
|
2972
|
+
repoHtml += htmlSection('Timeout Events', timeoutHtml, 4);
|
|
2973
|
+
}
|
|
2974
|
+
if (repo.hook_summary) {
|
|
2975
|
+
const eventList = Object.entries(repo.hook_summary.events)
|
|
2976
|
+
.sort(([left], [right]) => left.localeCompare(right, 'en'))
|
|
2977
|
+
.map(([event, count]) => `${esc(event)}(${count})`)
|
|
2978
|
+
.join(', ');
|
|
2979
|
+
const hookHtml = htmlDl([
|
|
2980
|
+
['Total executions', String(repo.hook_summary.total)],
|
|
2981
|
+
['Blocked', String(repo.hook_summary.blocked)],
|
|
2982
|
+
...(eventList ? [['Events', eventList]] : []),
|
|
2983
|
+
]);
|
|
2984
|
+
repoHtml += htmlSection('Hook Activity', hookHtml, 4);
|
|
2985
|
+
}
|
|
2986
|
+
if (repo.recovery_summary) {
|
|
2987
|
+
const recovery = repo.recovery_summary;
|
|
2988
|
+
let recoveryHtml = htmlDl([
|
|
2989
|
+
['Category', `<code>${esc(recovery.category || 'unknown')}</code>`],
|
|
2990
|
+
['Typed reason', `<code>${esc(recovery.typed_reason || 'unknown')}</code>`],
|
|
2991
|
+
['Owner', `<code>${esc(recovery.owner || 'unknown')}</code>`],
|
|
2992
|
+
['Action', `<code>${esc(recovery.recovery_action || 'n/a')}</code>`],
|
|
2993
|
+
['Detail', esc(recovery.detail || 'n/a')],
|
|
2994
|
+
['Turn retained', recovery.turn_retained == null ? 'n/a' : (recovery.turn_retained ? 'yes' : 'no')],
|
|
2995
|
+
]);
|
|
2996
|
+
if (Array.isArray(recovery.runtime_guidance) && recovery.runtime_guidance.length > 0) {
|
|
2997
|
+
recoveryHtml += htmlSection('Runtime Guidance', '<ul>' + recovery.runtime_guidance.map((entry) =>
|
|
2998
|
+
`<li><code>${esc(entry.code)}</code> — <code>${esc(entry.command)}</code>: ${esc(entry.reason)}</li>`
|
|
2999
|
+
).join('') + '</ul>', 5);
|
|
3000
|
+
}
|
|
3001
|
+
repoHtml += htmlSection('Recovery', recoveryHtml, 4);
|
|
3002
|
+
}
|
|
3003
|
+
if (repo.continuity) {
|
|
3004
|
+
const continuityPairs = [
|
|
3005
|
+
['Session', `<code>${esc(repo.continuity.session_id || 'unknown')}</code>`],
|
|
3006
|
+
['Checkpoint', `<code>${esc(repo.continuity.checkpoint_reason || 'unknown')}</code> at <code>${esc(repo.continuity.last_checkpoint_at || 'n/a')}</code>`],
|
|
3007
|
+
['Last turn', `<code>${esc(repo.continuity.last_turn_id || 'none')}</code>`],
|
|
3008
|
+
['Last role', `<code>${esc(repo.continuity.last_role || 'unknown')}</code>`],
|
|
3009
|
+
['Last phase', `<code>${esc(repo.continuity.last_phase || 'unknown')}</code>`],
|
|
3010
|
+
];
|
|
3011
|
+
if (repo.continuity.stale_checkpoint) {
|
|
3012
|
+
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>`]);
|
|
3013
|
+
}
|
|
3014
|
+
repoHtml += htmlSection('Continuity', htmlDl(continuityPairs), 4);
|
|
3015
|
+
}
|
|
2808
3016
|
}
|
|
2809
3017
|
sections.push(`<div class="section">${htmlSection('Repo Details', repoHtml)}</div>`);
|
|
2810
3018
|
}
|