// Timeline (Gantt-style swimlanes) + Activity feed views
/* ---------------- Timeline ---------------- */
// Window: -75min … +25min relative to "now". 100 units wide, now at 75%.
// left% = minutesRelativeToNow + 75 ; width% = durationMinutes
const TL_WINDOW = 100;
const TL_NOW = 75;
// Deterministic timing per task id, in minutes relative to now (neg = past).
const TL_TIMING = {
// installer-agent
'TSK-3964': { start: -68, end: -54 }, // done · Install Auth
'TSK-3965': { start: -50, end: -40 }, // done · Bump Next
'TSK-3954': { start: -32, end: -20, halt: true }, // failed · Analytics
'TSK-3999': { start: -14, end: 22 }, // running 28%
'TSK-4006': { start: 2, end: 14 }, // ready
'TSK-4012': { start: 16, end: 24 }, // backlog
// tester-agent
'TSK-4000': { start: -22, end: 4 }, // running 81%
'TSK-4008': { start: 6, end: 20 }, // ready
// migrator-agent
'TSK-3998': { start: -28, end: 12 }, // running 62%
'TSK-4015': { start: 14, end: 24 }, // backlog
// reviewer-agent
'TSK-3987': { start: -45, end: -29, await: true }, // review
'TSK-3988': { start: -27, end: -15, await: true }, // review
// sentinel-agent
'TSK-3966': { start: -67, end: -53 }, // done · autoscaler
'TSK-4007': { start: 1, end: 9 }, // ready
'TSK-4013': { start: 11, end: 21 }, // backlog
};
const TL_LANES = [
{ agent: 'installer-agent', kind: 'installer' },
{ agent: 'migrator-agent', kind: 'migrator' },
{ agent: 'tester-agent', kind: 'tester' },
{ agent: 'reviewer-agent', kind: 'reviewer' },
{ agent: 'sentinel-agent', kind: 'sentinel' },
];
const TL_TICKS = [-75, -60, -45, -30, -15, 0, 15, 25];
function tickLabel(m) {
if (m === 0) return 'now';
return (m > 0 ? '+' : '') + m + 'm';
}
function TimelineBar({ task, project, timing, onSelect, selected }) {
const left = timing.start + 75;
const width = Math.max(2, timing.end - timing.start);
const status = task.column;
// For running tasks, split into elapsed (solid) and projected (ghost)
const isRunning = status === 'running';
const elapsedW = isRunning ? Math.max(0, 0 - timing.start) : width;
const elapsedPct = (elapsedW / width) * 100;
return (
);
}
function TimelineView({ tasks, onSelect, selectedId }) {
const { PROJECTS } = window.ORC_DATA;
const projectsById = Object.fromEntries(PROJECTS.map(p => [p.id, p]));
const tasksById = Object.fromEntries(tasks.map(t => [t.id, t]));
const counts = {
running: tasks.filter(t => t.column === 'running').length,
review: tasks.filter(t => t.column === 'review').length,
queued: tasks.filter(t => t.column === 'ready' || t.column === 'backlog').length,
};
return (
Execution timeline
window −75m … +25m · projected work shown ghosted
{[
{ k: 'running', label: 'running' },
{ k: 'review', label: 'review' },
{ k: 'done', label: 'done' },
{ k: 'failed', label: 'failed' },
{ k: 'ready', label: 'queued' },
].map(s => (
{s.label}
))}
{/* Axis header */}
{TL_TICKS.map(m => (
{tickLabel(m)}
))}
{/* Lanes */}
{TL_LANES.map(lane => {
const laneTasks = tasks
.filter(t => t.agent === lane.agent && TL_TIMING[t.id])
.sort((a, b) => TL_TIMING[a.id].start - TL_TIMING[b.id].start);
return (
{lane.kind.slice(0, 2).toUpperCase()}
{lane.agent}
{laneTasks.length}
{/* gridlines */}
{TL_TICKS.map(m => (
))}
{laneTasks.map(t => (
))}
);
})}
{/* now line spanning lanes */}
{counts.running} running
{counts.review} in review
{counts.queued} queued
);
}
/* ---------------- Activity ---------------- */
const ACTIVITY_FULL = [
{ id: 'a1', type: 'install', agent: 'installer-agent', project: 'zeny',
title: 'Installed auth@2.4.0', detail: 'Applied to prod-us · 4 services restarted · 0 errors',
ago: '12s ago', time: '00:14:22', task: 'TSK-3964' },
{ id: 'a2', type: 'migrate', agent: 'migrator-agent', project: 'tlsdeck',
title: 'Partition rollout on users table', detail: '7 / 12 shards backfilled · shadow read parity 100%',
ago: '38s ago', time: '00:13:08', task: 'TSK-3998' },
{ id: 'a3', type: 'review', agent: 'reviewer-agent', project: 'vidix',
title: 'Requested approval — Stripe v3 webhooks', detail: '+412 / −188 across 6 files · affects payments',
ago: '1m ago', time: '00:11:51', task: 'TSK-3987' },
{ id: 'a4', type: 'fail', agent: 'installer-agent', project: 'worldtime',
title: 'Halted analytics@0.4.0 install', detail: 'dependency conflict: pg-extensions/citext@1.6.3 missing',
ago: '4m ago', time: '00:09:42', task: 'TSK-3954' },
{ id: 'a5', type: 'test', agent: 'tester-agent', project: 'zeny',
title: 'Dispatched regression suite', detail: '218 assertions queued on staging.zeny',
ago: '6m ago', time: '00:07:30', task: 'TSK-4008' },
{ id: 'a6', type: 'rotate', agent: 'sentinel-agent', project: 'tlsdeck',
title: 'Rotated KMS signing keys', detail: 'us-east-2 · propagated to 14 services',
ago: '9m ago', time: '00:04:12', task: 'TSK-4007' },
{ id: 'a7', type: 'update', agent: 'installer-agent', project: 'zeny',
title: 'Applied next@15.3 runtime bump', detail: 'across 3 projects · cold-start −8%',
ago: '16m ago', time: '23:58:01', task: 'TSK-3965' },
{ id: 'a8', type: 'install', agent: 'installer-agent', project: 'worldtime',
title: 'Started blog@0.8.4 install', detail: 'building bundle · 28% · est. 1m 40s',
ago: '18m ago', time: '23:55:18', task: 'TSK-3999' },
{ id: 'a9', type: 'review', agent: 'reviewer-agent', project: 'tlsdeck',
title: 'Requested approval — backfill org_id', detail: '1 migration · affects DB · 41k invoice rows',
ago: '24m ago', time: '23:49:30', task: 'TSK-3988' },
{ id: 'a10', type: 'install', agent: 'sentinel-agent', project: 'vidix',
title: 'Deployed autoscaler config', detail: 'prod-us · min 4 / max 32 replicas',
ago: '31m ago', time: '23:42:55', task: 'TSK-3966' },
{ id: 'a11', type: 'test', agent: 'tester-agent', project: 'vidix',
title: 'Rebuilt knowledge embeddings', detail: '81% · 6,402 docs re-indexed',
ago: '40m ago', time: '23:33:12', task: 'TSK-4000' },
];
const ACT_TYPES = {
install: { icon: 'box', color: 'var(--c-status-done)', label: 'Install' },
update: { icon: 'arrowRight', color: 'var(--c-status-running)', label: 'Update' },
migrate: { icon: 'database', color: 'var(--c-status-running)', label: 'Migration' },
review: { icon: 'shield', color: 'var(--c-status-review)', label: 'Approval' },
fail: { icon: 'alert', color: 'var(--c-status-failed)', label: 'Failure' },
test: { icon: 'zap', color: 'var(--c-status-running)', label: 'Test' },
rotate: { icon: 'refresh', color: 'var(--c-status-done)', label: 'Security' },
};
const ACT_FILTERS = [
{ id: 'all', label: 'All' },
{ id: 'install', label: 'Installs & updates', types: ['install', 'update', 'migrate'] },
{ id: 'review', label: 'Approvals', types: ['review'] },
{ id: 'fail', label: 'Failures', types: ['fail'] },
{ id: 'test', label: 'Tests', types: ['test'] },
];
function ActivityView({ onSelect }) {
const { PROJECTS } = window.ORC_DATA;
const projectsById = Object.fromEntries(PROJECTS.map(p => [p.id, p]));
const [filter, setFilter] = React.useState('all');
const def = ACT_FILTERS.find(f => f.id === filter);
const items = filter === 'all'
? ACTIVITY_FULL
: ACTIVITY_FULL.filter(a => def.types.includes(a.type));
// bucket by recency
const recent = items.filter(a => a.ago.includes('s ago') || a.ago === '1m ago');
const earlier = items.filter(a => !(a.ago.includes('s ago') || a.ago === '1m ago'));
const renderRow = (a) => {
const t = ACT_TYPES[a.type];
const project = projectsById[a.project];
return (
);
};
return (
{ACT_FILTERS.map(f => (
))}
live stream
{recent.length > 0 &&
Just now
}
{recent.map(renderRow)}
{earlier.length > 0 &&
Earlier
}
{earlier.map(renderRow)}
{items.length === 0 &&
no events match this filter
}
);
}
window.TimelineView = TimelineView;
window.ActivityView = ActivityView;