);
}
// Per-stage timeline for today's brief. Reads /api/brief/status's
// {stages, attempts} and renders a 5-column row (COLLECT → ENRICH →
// SYNTHESIZE → COMPOSE → DELIVER) with each stage's tick / cross /
// dash + the most recent error if the stage failed. Plus a redeliver
// button so the user can retry the DELIVER stage after fixing creds
// without leaving the panel.
//
// SYNTHESIZE is the heavy LLM stage (multi-persona team round) — the
// most common failure point when Anthropic credits run out. Surfacing
// it as a distinct cell makes the failure mode legible.
const BRIEF_STAGES = ['COLLECT', 'ENRICH', 'SYNTHESIZE', 'COMPOSE', 'DELIVER'];
function BriefStageTimeline({ status, redelivering, redeliverMsg, onRedeliver }) {
// For each stage, find the LAST attempt's outcome to color the cell.
// 'ok' = green, 'fail' = red, anything else (skipped, missing) = dim.
const lastByStage = {};
for (const a of (status.attempts || [])) {
if (a.stage) lastByStage[a.stage.toUpperCase()] = a;
}
// Most recent failure (if any) — surfaced under the timeline so the
// user sees the error string without expanding the full attempts log.
const recentFail = (status.attempts || []).slice().reverse()
.find(a => a.status === 'fail');
const cellColor = (stageKey) => {
const last = lastByStage[stageKey];
if (!last) return 'var(--text-dim)';
if (last.status === 'ok') return '#5fb37c';
if (last.status === 'fail') return '#d96a6a';
return 'var(--text-dim)';
};
const cellGlyph = (stageKey) => {
const last = lastByStage[stageKey];
if (!last) return '·';
if (last.status === 'ok') return '✓';
if (last.status === 'fail') return '✗';
return '~';
};
return (
stages
{/* Timeline row: each cell has glyph above + label below */}
);
}
// Autonomy section content. Surfaces three streams from the autonomous-
// triggers branch (claude/autonomous-codebase-oDhbG) via best-effort
// fetches; renders the subset that answered.
//
// /api/events/snapshot → live signal (vix, drawdown, bwd, ...) the
// triggers are evaluated against
// /api/events/personas → every persona with `trigger:` frontmatter,
// its armed/latched state, last-fire ts
// /api/trade-recs → propose_trade-filed recs awaiting review
//
// Why duplicate Today tab's AutonomyStrip here: Health is the single
// "is everything OK" diagnostic surface — the trader checks it before
// market open. Mixing autonomy state into the same surface saves a tab
// hunt for "why didn't the hedge fire when VIX spiked?".
function AutonomySnapshot({ autonomy }) {
const snap = autonomy.snapshot || {};
const personas = (autonomy.personas?.personas || autonomy.personas?.entries || autonomy.personas || []);
const personaList = Array.isArray(personas) ? personas : (personas.personas || []);
const recs = (autonomy.recs?.recs || autonomy.recs || []);
const recList = Array.isArray(recs) ? recs : (recs.recs || []);
const Row = ({ k, v, color }) => (