opencastle 0.32.6 → 0.32.7

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.
Files changed (42) hide show
  1. package/dist/cli/convoy/engine.d.ts.map +1 -1
  2. package/dist/cli/convoy/engine.js +79 -4
  3. package/dist/cli/convoy/engine.js.map +1 -1
  4. package/dist/cli/convoy/engine.test.js +11 -9
  5. package/dist/cli/convoy/engine.test.js.map +1 -1
  6. package/dist/dashboard/scripts/etl.js +17 -2
  7. package/dist/dashboard/scripts/etl.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/cli/convoy/engine.test.ts +11 -9
  10. package/src/cli/convoy/engine.ts +78 -4
  11. package/src/dashboard/dist/_astro/index.6xXNs4L2.css +1 -0
  12. package/src/dashboard/dist/data/convoy-list.json +27 -13
  13. package/src/dashboard/dist/data/convoys/demo-api-v2.json +16 -10
  14. package/src/dashboard/dist/data/convoys/demo-auth-revamp.json +25 -15
  15. package/src/dashboard/dist/data/convoys/demo-dashboard-ui.json +35 -21
  16. package/src/dashboard/dist/data/convoys/demo-data-pipeline.json +17 -11
  17. package/src/dashboard/dist/data/convoys/demo-deploy-ci.json +8 -4
  18. package/src/dashboard/dist/data/convoys/demo-docs-update.json +13 -9
  19. package/src/dashboard/dist/data/convoys/demo-perf-opt.json +22 -14
  20. package/src/dashboard/dist/data/overall-stats.json +36 -2
  21. package/src/dashboard/dist/index.html +149 -93
  22. package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
  23. package/src/dashboard/public/data/convoy-list.json +27 -13
  24. package/src/dashboard/public/data/convoys/demo-api-v2.json +16 -10
  25. package/src/dashboard/public/data/convoys/demo-auth-revamp.json +25 -15
  26. package/src/dashboard/public/data/convoys/demo-dashboard-ui.json +35 -21
  27. package/src/dashboard/public/data/convoys/demo-data-pipeline.json +17 -11
  28. package/src/dashboard/public/data/convoys/demo-deploy-ci.json +8 -4
  29. package/src/dashboard/public/data/convoys/demo-docs-update.json +13 -9
  30. package/src/dashboard/public/data/convoys/demo-perf-opt.json +22 -14
  31. package/src/dashboard/public/data/overall-stats.json +36 -2
  32. package/src/dashboard/scripts/etl.ts +15 -3
  33. package/src/dashboard/scripts/generate-demo-db.ts +42 -34
  34. package/src/dashboard/src/pages/index.astro +159 -112
  35. package/src/dashboard/src/styles/dashboard.css +58 -3
  36. package/src/dashboard/dist/_astro/index.wyN9vmjZ.css +0 -1
  37. package/src/dashboard/dist/data/convoys/demo-convoy-1.json +0 -111
  38. package/src/dashboard/dist/data/convoys/demo-convoy-2.json +0 -72
  39. package/src/dashboard/dist/data/pipelines.ndjson +0 -5285
  40. package/src/dashboard/public/data/convoys/demo-convoy-1.json +0 -111
  41. package/src/dashboard/public/data/convoys/demo-convoy-2.json +0 -72
  42. package/src/dashboard/public/data/pipelines.ndjson +0 -5285
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "tokenCostTotals": {
15
15
  "total_tokens": 226750,
16
- "total_cost_usd": 22.67
16
+ "total_cost_usd": 2.8241
17
17
  },
18
18
  "topAgents": [
19
19
  {
@@ -75,5 +75,39 @@
75
75
  "count": 1
76
76
  }
77
77
  ]
78
- }
78
+ },
79
+ "taskTotals": {
80
+ "totalTasks": 30,
81
+ "totalRetries": 5
82
+ },
83
+ "activityTimeline": [
84
+ {
85
+ "date": "2026-02-03",
86
+ "count": 1
87
+ },
88
+ {
89
+ "date": "2026-02-07",
90
+ "count": 1
91
+ },
92
+ {
93
+ "date": "2026-02-12",
94
+ "count": 1
95
+ },
96
+ {
97
+ "date": "2026-02-17",
98
+ "count": 1
99
+ },
100
+ {
101
+ "date": "2026-02-22",
102
+ "count": 1
103
+ },
104
+ {
105
+ "date": "2026-02-28",
106
+ "count": 1
107
+ },
108
+ {
109
+ "date": "2026-03-11",
110
+ "count": 1
111
+ }
112
+ ]
79
113
  }
@@ -1,10 +1,10 @@
1
- <!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Observability Dashboard — OpenCastle</title><meta name="description" content="Real-time observability for OpenCastle multi-agent orchestration — sessions, delegations, model tiers, and quality gates."><meta name="theme-color" content="#0a0a0f"><link rel="icon" type="image/png" sizes="192x192" href="/icon-192.png"><link rel="stylesheet" href="/_astro/index.wyN9vmjZ.css"></head> <body> <script>(function(){const overallStats = {"convoyCounts":{"total":7,"running":1,"done":5,"failed":0,"gate_failed":0},"durationStats":{"avg_sec":2950.0000052154064,"p95_sec":3720.000019669533,"max_sec":5879.9999713897705},"tokenCostTotals":{"total_tokens":226750,"total_cost_usd":22.67},"topAgents":[{"agent":"Developer","task_count":6,"total_tokens":70100},{"agent":"Reviewer","task_count":5,"total_tokens":16250},{"agent":"UI/UX Expert","task_count":3,"total_tokens":32300},{"agent":"Testing Expert","task_count":3,"total_tokens":28800},{"agent":"DevOps Expert","task_count":3,"total_tokens":6400}],"topModels":[{"model":"claude-sonnet-4-6","task_count":21,"total_tokens":177650},{"model":"claude-haiku-3-5","task_count":5,"total_tokens":17700},{"model":"claude-opus-4-6","task_count":4,"total_tokens":40700}],"dlqSummary":{"count":3,"top_failure_types":[{"type":"timeout","count":1},{"type":"review_blocked","count":1},{"type":"gate_failed","count":1}]}};
2
- const convoyList = [{"id":"demo-deploy-ci","name":"CI/CD Pipeline Setup","status":"running","created_at":"2026-03-11T08:00:00.000Z","finished_at":null,"total_tokens":null,"total_cost_usd":null,"task_count":3,"pipeline_id":null},{"id":"demo-docs-update","name":"Documentation Refresh","status":"done","created_at":"2026-02-28T15:00:00.000Z","finished_at":"2026-02-28T15:22:00.000Z","total_tokens":14800,"total_cost_usd":1.48,"task_count":3,"pipeline_id":null},{"id":"demo-data-pipeline","name":"Analytics ETL Pipeline","status":"done","created_at":"2026-02-22T13:00:00.000Z","finished_at":"2026-02-22T13:38:00.000Z","total_tokens":28900,"total_cost_usd":2.89,"task_count":4,"pipeline_id":null},{"id":"demo-perf-opt","name":"Frontend Performance Boost","status":"done","created_at":"2026-02-17T10:00:00.000Z","finished_at":"2026-02-17T11:02:00.000Z","total_tokens":37200,"total_cost_usd":3.72,"task_count":5,"pipeline_id":null},{"id":"demo-api-v2","name":"REST API v2 Migration","status":"gate_failed","created_at":"2026-02-12T16:00:00.000Z","finished_at":"2026-02-12T16:28:00.000Z","total_tokens":24600,"total_cost_usd":2.46,"task_count":3,"pipeline_id":null},{"id":"demo-dashboard-ui","name":"Observability Dashboard UI","status":"done","created_at":"2026-02-07T14:00:00.000Z","finished_at":"2026-02-07T15:38:00.000Z","total_tokens":78400,"total_cost_usd":7.84,"task_count":7,"pipeline_id":"demo-pipeline-1"},{"id":"demo-auth-revamp","name":"Auth System Revamp","status":"done","created_at":"2026-02-03T09:00:00.000Z","finished_at":"2026-02-03T09:47:00.000Z","total_tokens":42850,"total_cost_usd":4.28,"task_count":5,"pipeline_id":"demo-pipeline-1"}];
1
+ <!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Observability Dashboard — OpenCastle</title><meta name="description" content="Real-time observability for OpenCastle multi-agent orchestration — sessions, delegations, model tiers, and quality gates."><meta name="theme-color" content="#0a0a0f"><link rel="icon" type="image/png" sizes="192x192" href="/icon-192.png"><link rel="stylesheet" href="/_astro/index.6xXNs4L2.css"></head> <body> <script>(function(){const overallStats = {"convoyCounts":{"total":7,"running":1,"done":5,"failed":0,"gate_failed":0},"durationStats":{"avg_sec":2950.0000052154064,"p95_sec":3720.000019669533,"max_sec":5879.9999713897705},"tokenCostTotals":{"total_tokens":226750,"total_cost_usd":2.8241},"topAgents":[{"agent":"Developer","task_count":6,"total_tokens":70100},{"agent":"Reviewer","task_count":5,"total_tokens":16250},{"agent":"UI/UX Expert","task_count":3,"total_tokens":32300},{"agent":"Testing Expert","task_count":3,"total_tokens":28800},{"agent":"DevOps Expert","task_count":3,"total_tokens":6400}],"topModels":[{"model":"claude-sonnet-4-6","task_count":21,"total_tokens":177650},{"model":"claude-haiku-3-5","task_count":5,"total_tokens":17700},{"model":"claude-opus-4-6","task_count":4,"total_tokens":40700}],"dlqSummary":{"count":3,"top_failure_types":[{"type":"timeout","count":1},{"type":"review_blocked","count":1},{"type":"gate_failed","count":1}]},"taskTotals":{"totalTasks":30,"totalRetries":5},"activityTimeline":[{"date":"2026-02-03","count":1},{"date":"2026-02-07","count":1},{"date":"2026-02-12","count":1},{"date":"2026-02-17","count":1},{"date":"2026-02-22","count":1},{"date":"2026-02-28","count":1},{"date":"2026-03-11","count":1}]};
2
+ const convoyList = [{"id":"demo-deploy-ci","name":"CI/CD Pipeline Setup","status":"running","created_at":"2026-03-11T08:00:00.000Z","started_at":"2026-03-11T08:00:00.000Z","finished_at":null,"total_tokens":null,"total_cost_usd":null,"task_count":3,"pipeline_id":null,"pipeline_name":null},{"id":"demo-docs-update","name":"Documentation Refresh","status":"done","created_at":"2026-02-28T15:00:00.000Z","started_at":"2026-02-28T15:00:00.000Z","finished_at":"2026-02-28T15:22:00.000Z","total_tokens":14800,"total_cost_usd":0.0296,"task_count":3,"pipeline_id":null,"pipeline_name":null},{"id":"demo-data-pipeline","name":"Analytics ETL Pipeline","status":"done","created_at":"2026-02-22T13:00:00.000Z","started_at":"2026-02-22T13:00:00.000Z","finished_at":"2026-02-22T13:38:00.000Z","total_tokens":28900,"total_cost_usd":0.2312,"task_count":4,"pipeline_id":null,"pipeline_name":null},{"id":"demo-perf-opt","name":"Frontend Performance Boost","status":"done","created_at":"2026-02-17T10:00:00.000Z","started_at":"2026-02-17T10:00:00.000Z","finished_at":"2026-02-17T11:02:00.000Z","total_tokens":37200,"total_cost_usd":0.2976,"task_count":5,"pipeline_id":null,"pipeline_name":null},{"id":"demo-api-v2","name":"REST API v2 Migration","status":"gate_failed","created_at":"2026-02-12T16:00:00.000Z","started_at":"2026-02-12T16:00:00.000Z","finished_at":"2026-02-12T16:28:00.000Z","total_tokens":24600,"total_cost_usd":0.1968,"task_count":3,"pipeline_id":null,"pipeline_name":null},{"id":"demo-dashboard-ui","name":"Observability Dashboard UI","status":"done","created_at":"2026-02-07T14:00:00.000Z","started_at":"2026-02-07T14:00:00.000Z","finished_at":"2026-02-07T15:38:00.000Z","total_tokens":78400,"total_cost_usd":1.4993,"task_count":7,"pipeline_id":"demo-pipeline-1","pipeline_name":"Auth & Dashboard Sprint"},{"id":"demo-auth-revamp","name":"Auth System Revamp","status":"done","created_at":"2026-02-03T09:00:00.000Z","started_at":"2026-02-03T09:00:00.000Z","finished_at":"2026-02-03T09:47:00.000Z","total_tokens":42850,"total_cost_usd":0.5696,"task_count":5,"pipeline_id":"demo-pipeline-1","pipeline_name":"Auth & Dashboard Sprint"}];
3
3
 
4
4
  window.__DASHBOARD_DATA__ = { overallStats, convoyList }
5
5
  })();</script> <header class="dash-header"> <div class="dash-header__inner"> <div class="dash-header__brand"> <a href="https://www.opencastle.dev/" class="dash-header__logo-link"> <img class="dash-header__icon" src="/icon-192.png" alt="OpenCastle" width="28" height="28"> </a> <h1 class="dash-header__title">Observability Dashboard</h1> </div> <div class="dash-header__actions"> <button class="dash-btn dash-btn--ghost" id="export-btn" type="button" title="Export data as JSON" aria-label="Export dashboard data as JSON"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
6
6
  Export
7
- </button> </div> </div> </header> <div class="dash-layout"> <!-- Sidebar Navigation --> <nav class="dash-sidebar" id="dash-sidebar"> <ul class="dash-sidebar__list"> <li><a class="dash-sidebar__link dash-sidebar__link--active" href="#overall-section" data-section="overall-section" data-view="home" aria-label="Overview section">Overview</a></li> <li><a class="dash-sidebar__link" href="#convoy-list-section" data-section="convoy-list-section" data-view="home" aria-label="Convoys section">Convoys</a></li> <li><a class="dash-sidebar__link" href="#activity-timeline-section" data-section="activity-timeline-section" data-view="home" aria-label="Activity Timeline section">Activity</a></li> <li><a class="dash-sidebar__link" href="#sessions-section" data-section="sessions-section" data-view="home" aria-label="Sessions section">Sessions</a></li> <li><a class="dash-sidebar__link" href="#tasks-section" data-section="tasks-section" data-view="detail" aria-label="Tasks section">Tasks</a></li> <li><a class="dash-sidebar__link" href="#pipeline-section" data-section="pipeline-section" data-view="detail" aria-label="Pipeline section">Pipeline</a></li> <li><a class="dash-sidebar__link" href="#agent-section" data-section="agent-section" data-view="detail" aria-label="Agents section">Agents</a></li> <li><a class="dash-sidebar__link" href="#tier-section" data-section="tier-section" data-view="detail" aria-label="Tiers section">Tiers</a></li> <li><a class="dash-sidebar__link" href="#model-section" data-section="model-section" data-view="detail" aria-label="Models section">Models</a></li> <li><a class="dash-sidebar__link" href="#quality-section" data-section="quality-section" data-view="detail" aria-label="Quality section">Quality</a></li> <li><a class="dash-sidebar__link" href="#reliability-section" data-section="reliability-section" data-view="detail" aria-label="Reliability section">Reliability</a></li> <li><a class="dash-sidebar__link" href="#drift-section" data-section="drift-section" data-view="detail" aria-label="Drift section">Drift</a></li> <li><a class="dash-sidebar__link" href="#outputs-section" data-section="outputs-section" data-view="detail" aria-label="Outputs section">Outputs</a></li> <li><a class="dash-sidebar__link" href="#execution-section" data-section="execution-section" data-view="detail" aria-label="Execution Log section">Execution Log</a></li> <li><a class="dash-sidebar__link" href="#event-timeline-section" data-section="event-timeline-section" data-view="detail" aria-label="Event Log section">Event Log</a></li> <li><a class="dash-sidebar__link" href="#reviews-section" data-section="reviews-section" data-view="detail" aria-label="Fast Reviews section">Fast Reviews</a></li> <li><a class="dash-sidebar__link" href="#panel-section" data-section="panel-section" data-view="detail" aria-label="Panel Reviews section">Panel Reviews</a></li> </ul> </nav> <main class="dash-main"> <nav class="breadcrumbs" id="breadcrumbs" data-view-hidden> <a class="breadcrumbs__link" href="#" id="breadcrumbs-home">Observability</a> <span class="breadcrumbs__separator">/</span> <span class="breadcrumbs__current" id="breadcrumbs-convoy"></span> </nav> <div class="view-home" id="view-home"> <!-- Overall Stats Section --> <section class="overall-stats" id="overall-section" data-nav-section> <div class="overall-stats__header"> <h2 class="overall-stats__title">Overall Stats</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: How all runs behave across your project." data-tooltip="How all runs behave across your project."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> </div> <div class="overall-stats__grid"> <div class="overall-kpi" id="overall-total-runs"> <span class="overall-kpi__label">Total Runs <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Number of convoy runs executed" data-tooltip="Number of convoy runs executed"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span></span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-running"> <span class="overall-kpi__label">Running Now</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-success-rate"> <span class="overall-kpi__label">Success Rate</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-avg-duration"> <span class="overall-kpi__label">Avg Duration</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-total-tokens"> <span class="overall-kpi__label">Total Tokens</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-total-cost"> <span class="overall-kpi__label">Total Cost</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-total-tasks"> <span class="overall-kpi__label">Total Tasks</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-total-retries"> <span class="overall-kpi__label">Total Retries</span> <span class="overall-kpi__value">&mdash;</span> </div> </div> </section> <!-- Activity Timeline --> <section class="chart-card" id="activity-timeline-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Activity Timeline</h2> <p class="chart-card__desc">Convoy runs over time</p> </div> <div class="chart-card__body" id="activity-timeline-chart"></div> </section> <!-- Convoy List Section --> <section class="convoy-list-section" id="convoy-list-section"> <div class="convoy-list-section__header"> <h2>Convoys</h2> <p class="convoy-list-section__desc">All convoy runs across your project</p> </div> <div class="convoy-list-filters" id="convoy-list-filters"> <div class="convoy-list-filters__group"> <label for="cl-filter-search">Search</label> <input class="convoy-list-filters__input" type="text" id="cl-filter-search" placeholder="Convoy name…"> </div> <div class="convoy-list-filters__group"> <label for="cl-filter-status">Status</label> <select class="convoy-list-filters__select" id="cl-filter-status"> <option value="">All</option> <option value="done">Done</option> <option value="running">Running</option> <option value="failed">Failed</option> <option value="gate-failed">Gate Failed</option> <option value="pending">Pending</option> </select> </div> <div class="convoy-list-filters__group"> <label for="cl-filter-from">From</label> <input class="convoy-list-filters__date" type="date" id="cl-filter-from"> </div> <div class="convoy-list-filters__group"> <label for="cl-filter-to">To</label> <input class="convoy-list-filters__date" type="date" id="cl-filter-to"> </div> <button class="convoy-list-filters__reset" type="button" id="cl-filter-reset">Reset</button> </div> <div id="convoy-list-table-wrap"></div> <div class="convoy-list-empty" id="convoy-list-empty" style="display:none"> <div class="convoy-list-empty__icon"> <svg width="40" height="40" viewBox="0 0 40 40" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="6" y="6" width="28" height="28" rx="4"></rect><line x1="14" y1="20" x2="26" y2="20" opacity="0.5"></line></svg> </div> <p class="convoy-list-empty__text">No convoys match your filters</p> </div> </section> <!-- Recent Sessions --> <section class="chart-card" id="sessions-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Recent Sessions</h2> <p class="chart-card__desc">Latest agent sessions from observability log</p> </div> <div class="chart-card__body chart-card__body--table" id="sessions-table"></div> </section> </div><!-- .view-home --> <div class="view-convoy-detail" id="view-convoy-detail" data-view-hidden> <section class="convoy-detail-hero" id="convoy-detail-hero"> <div class="convoy-detail-hero__top"> <div class="convoy-detail-hero__title" id="detail-hero-title"></div> <span class="convoy-detail-hero__status" id="detail-hero-status"></span> </div> <div class="convoy-detail-hero__meta" id="detail-hero-meta"></div> </section> <!-- Tasks Section --> <section class="chart-card" id="tasks-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Tasks</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Individual units of work in this convoy." data-tooltip="Individual units of work in this convoy."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc" id="tasks-section-desc">Task breakdown for the selected convoy</p> </div> <div class="chart-card__body" id="tasks-section-body"> <div id="task-summary-cards" class="task-summary-cards"></div> <div id="phase-breakdown" class="phase-breakdown"></div> <div id="task-table-wrap" class="task-table-wrap"></div> </div> </section> <!-- Pipeline View --> <section class="chart-card" id="pipeline-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Task Pipeline</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Visualizes how tasks progress through sequential execution phases." data-tooltip="Visualizes how tasks progress through sequential execution phases — from initial setup through integration, validation, and final quality gate."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Task flow across execution phases</p> </div> <div class="chart-card__body" id="pipeline-view"></div> </section> <!-- Charts Row: Agent + Outcomes --> <div class="charts-row" id="agent-section" data-nav-section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Sessions by Agent</h2> <p class="chart-card__desc">Task count per agent, stacked by outcome</p> </div> <div class="chart-card__body" id="agent-chart"></div> </section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Delegation Outcomes</h2> <p class="chart-card__desc">Task outcome distribution</p> </div> <div class="chart-card__body" id="delegation-outcome-chart"></div> </section> </div> <!-- Charts Row: Tiers + Mechanism --> <div class="charts-row" id="tier-section" data-nav-section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Tier Distribution</h2> <p class="chart-card__desc">Model tier breakdown</p> </div> <div class="chart-card__body" id="tier-chart"></div> </section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Delegation Mechanism</h2> <p class="chart-card__desc">Sub-agent vs background split</p> </div> <div class="chart-card__body" id="mechanism-chart"></div> </section> </div> <!-- Model Usage --> <section class="chart-card" id="model-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Model Usage</h2> <p class="chart-card__desc">Tasks by model</p> </div> <div class="chart-card__body" id="model-chart"></div> </section> <!-- Quality Section --> <section class="chart-card" id="quality-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Quality / Review</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Code review results and dispute resolution." data-tooltip="Code review results and dispute resolution."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Code review results and quality gate outcomes for the selected convoy</p> </div> <div class="chart-card__body"> <div id="quality-cards" class="task-summary-cards"></div> <div id="quality-review-table"></div> </div> </section> <!-- Reliability Section --> <section class="chart-card" id="reliability-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Reliability</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Errors, retry attempts, and safety mechanisms." data-tooltip="Errors, retry attempts, and safety mechanisms."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Retry queue and error overview for the selected convoy</p> </div> <div class="chart-card__body"> <div id="reliability-dlq-card"></div> <div id="reliability-dlq-table"></div> <div style="margin-top:24px"> <h3 style="font-size:1rem;font-weight:600;color:var(--text-secondary);margin:0 0 12px">Error Overview</h3> <div id="reliability-error-overview"></div> </div> </div> </section> <!-- Drift Section --> <section class="chart-card" id="drift-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Drift</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: How far the actual work deviated from the original plan." data-tooltip="How far the actual work deviated from the original plan."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Plan adherence and deviation metrics for the selected convoy</p> </div> <div class="chart-card__body"> <div id="drift-cards" class="task-summary-cards"></div> <div id="drift-secret-banner" style="display:none"></div> </div> </section> <!-- Outputs Section --> <section class="chart-card" id="outputs-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Outputs &amp; Artifacts</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Files, data, and summaries produced by this convoy." data-tooltip="Files, data, and summaries produced by this convoy."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Files, summaries, and structured data produced by tasks in this convoy</p> </div> <div class="chart-card__body"> <div id="outputs-cards" class="task-summary-cards"></div> <div id="artifact-table-wrap"></div> </div> </section> <!-- Event Timeline Section --> <section class="chart-card" id="event-timeline-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Event Timeline</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Chronological log of everything that happened during this run." data-tooltip="Chronological log of everything that happened during this run."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Convoy events in chronological order</p> </div> <div class="chart-card__body"> <div id="event-timeline-filters" class="timeline-filters"></div> <div id="event-timeline-list"></div> <div id="event-timeline-load-more" style="display:none;text-align:center;padding:16px"> <button class="dash-btn dash-btn--ghost" id="event-timeline-more-btn" type="button">Load more</button> </div> </div> </section> <!-- Execution Log --> <section class="chart-card" id="execution-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Execution Log</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Step-by-step trace of recent task activity." data-tooltip="Step-by-step trace of recent task activity."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Recent agent activity, step by step</p> </div> <div class="chart-card__body" id="execution-log"></div> </section> <!-- Fast Reviews --> <section class="chart-card" id="reviews-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Fast Reviews</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Single-reviewer quality gate results." data-tooltip="Single-reviewer quality gate results."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Single-reviewer quality gate results</p> </div> <div class="chart-card__body chart-card__body--table" id="reviews-table"></div> </section> <!-- Panel Reviews --> <section class="chart-card" id="panel-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Panel Reviews</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Quality gate verdicts from majority-vote panels." data-tooltip="Quality gate verdicts from majority-vote panels."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Quality gate verdicts and fix items</p> </div> <div class="chart-card__body" id="panel-chart"></div> </section> </div><!-- .view-convoy-detail --> </main> </div> </body></html> <script>(function(){const base = "/";
7
+ </button> </div> </div> </header> <div class="dash-layout"> <!-- Sidebar Navigation --> <nav class="dash-sidebar" id="dash-sidebar"> <ul class="dash-sidebar__list"> <li><a class="dash-sidebar__link dash-sidebar__link--active" href="#overall-section" data-section="overall-section" data-view="home" aria-label="Overview section">Overview</a></li> <li><a class="dash-sidebar__link" href="#convoy-list-section" data-section="convoy-list-section" data-view="home" aria-label="Convoys section">Convoys</a></li> <li><a class="dash-sidebar__link" href="#activity-timeline-section" data-section="activity-timeline-section" data-view="home" aria-label="Activity Timeline section">Activity</a></li> <li><a class="dash-sidebar__link" href="#sessions-section" data-section="sessions-section" data-view="home" aria-label="Sessions section">Sessions</a></li> <li><a class="dash-sidebar__link" href="#tasks-section" data-section="tasks-section" data-view="detail" aria-label="Tasks section">Tasks</a></li> <li><a class="dash-sidebar__link" href="#pipeline-section" data-section="pipeline-section" data-view="detail" aria-label="Pipeline section">Pipeline</a></li> <li><a class="dash-sidebar__link" href="#agent-section" data-section="agent-section" data-view="detail" aria-label="Agents section">Agents</a></li> <li><a class="dash-sidebar__link" href="#tier-section" data-section="tier-section" data-view="detail" aria-label="Tiers section">Tiers</a></li> <li><a class="dash-sidebar__link" href="#model-section" data-section="model-section" data-view="detail" aria-label="Models section">Models</a></li> <li><a class="dash-sidebar__link" href="#quality-section" data-section="quality-section" data-view="detail" aria-label="Quality section">Quality</a></li> <li><a class="dash-sidebar__link" href="#reliability-section" data-section="reliability-section" data-view="detail" aria-label="Reliability section">Reliability</a></li> <li><a class="dash-sidebar__link" href="#drift-section" data-section="drift-section" data-view="detail" aria-label="Drift section">Drift</a></li> <li><a class="dash-sidebar__link" href="#outputs-section" data-section="outputs-section" data-view="detail" aria-label="Outputs section">Outputs</a></li> <li><a class="dash-sidebar__link" href="#execution-section" data-section="execution-section" data-view="detail" aria-label="Execution Log section">Execution Log</a></li> <li><a class="dash-sidebar__link" href="#event-timeline-section" data-section="event-timeline-section" data-view="detail" aria-label="Event Log section">Event Log</a></li> <li><a class="dash-sidebar__link" href="#reviews-section" data-section="reviews-section" data-view="detail" aria-label="Fast Reviews section">Fast Reviews</a></li> <li><a class="dash-sidebar__link" href="#panel-section" data-section="panel-section" data-view="detail" aria-label="Panel Reviews section">Panel Reviews</a></li> </ul> </nav> <main class="dash-main"> <nav class="breadcrumbs" id="breadcrumbs" data-view-hidden> <a class="breadcrumbs__link" href="#" id="breadcrumbs-home">Observability</a> <span class="breadcrumbs__separator">/</span> <span class="breadcrumbs__current" id="breadcrumbs-convoy"></span> </nav> <div class="view-home" id="view-home"> <!-- Overall Stats Section --> <section class="overall-stats" id="overall-section" data-nav-section> <div class="overall-stats__header"> <h2 class="overall-stats__title">Overall Stats</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: How all runs behave across your project." data-tooltip="How all runs behave across your project."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> </div> <div class="overall-stats__grid"> <div class="overall-kpi" id="overall-total-runs"> <span class="overall-kpi__label">Total Runs <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Number of convoy runs executed" data-tooltip="Number of convoy runs executed"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span></span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-running"> <span class="overall-kpi__label">Running Now</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-total-tasks"> <span class="overall-kpi__label">Total Tasks</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-total-retries"> <span class="overall-kpi__label">Total Retries</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-success-rate"> <span class="overall-kpi__label">Success Rate</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-avg-duration"> <span class="overall-kpi__label">Avg Duration</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-total-tokens"> <span class="overall-kpi__label">Total Tokens</span> <span class="overall-kpi__value">&mdash;</span> </div> <div class="overall-kpi" id="overall-total-cost"> <span class="overall-kpi__label">Total Cost</span> <span class="overall-kpi__value">&mdash;</span> </div> </div> </section> <!-- Activity Timeline --> <section class="chart-card" id="activity-timeline-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Activity Timeline</h2> <p class="chart-card__desc">Convoy runs over time</p> </div> <div class="chart-card__body" id="activity-timeline-chart"></div> </section> <!-- Convoy List Section --> <section class="convoy-list-section" id="convoy-list-section"> <div class="convoy-list-section__header"> <h2>Convoys</h2> <p class="convoy-list-section__desc">All convoy runs across your project</p> </div> <div class="convoy-list-filters" id="convoy-list-filters"> <div class="convoy-list-filters__group"> <label for="cl-filter-search">Search</label> <input class="convoy-list-filters__input" type="text" id="cl-filter-search" placeholder="Convoy name…"> </div> <div class="convoy-list-filters__group"> <label for="cl-filter-status">Status</label> <select class="convoy-list-filters__select" id="cl-filter-status"> <option value="">All</option> <option value="done">Done</option> <option value="running">Running</option> <option value="failed">Failed</option> <option value="gate-failed">Gate Failed</option> <option value="pending">Pending</option> </select> </div> <div class="convoy-list-filters__group"> <label for="cl-filter-from">From</label> <input class="convoy-list-filters__date" type="date" id="cl-filter-from"> </div> <div class="convoy-list-filters__group"> <label for="cl-filter-to">To</label> <input class="convoy-list-filters__date" type="date" id="cl-filter-to"> </div> <button class="convoy-list-filters__reset" type="button" id="cl-filter-reset">Reset</button> </div> <div id="convoy-list-table-wrap"></div> <div class="convoy-list-empty" id="convoy-list-empty" style="display:none"> <div class="convoy-list-empty__icon"> <svg width="40" height="40" viewBox="0 0 40 40" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="6" y="6" width="28" height="28" rx="4"></rect><line x1="14" y1="20" x2="26" y2="20" opacity="0.5"></line></svg> </div> <p class="convoy-list-empty__text">No convoys match your filters</p> </div> </section> <!-- Recent Sessions --> <section class="chart-card" id="sessions-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Recent Sessions</h2> <p class="chart-card__desc">Latest agent sessions from observability log</p> </div> <div class="chart-card__body chart-card__body--table" id="sessions-table"></div> </section> </div><!-- .view-home --> <div class="view-convoy-detail" id="view-convoy-detail" data-view-hidden> <section class="convoy-detail-hero" id="convoy-detail-hero"> <div class="convoy-detail-hero__top"> <div class="convoy-detail-hero__title" id="detail-hero-title"></div> <span class="convoy-detail-hero__status" id="detail-hero-status"></span> </div> <div class="convoy-detail-hero__meta" id="detail-hero-meta"></div> </section> <!-- Tasks Section --> <section class="chart-card" id="tasks-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Tasks</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Individual units of work in this convoy." data-tooltip="Individual units of work in this convoy."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc" id="tasks-section-desc">Task breakdown for the selected convoy</p> </div> <div class="chart-card__body" id="tasks-section-body"> <div id="task-summary-cards" class="task-summary-cards"></div> <div id="phase-breakdown" class="phase-breakdown"></div> <div id="task-table-wrap" class="task-table-wrap"></div> </div> </section> <!-- Pipeline View --> <section class="chart-card" id="pipeline-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Task Pipeline</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Visualizes how tasks progress through sequential execution phases." data-tooltip="Visualizes how tasks progress through sequential execution phases — from initial setup through integration, validation, and final quality gate."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Task flow across execution phases</p> </div> <div class="chart-card__body" id="pipeline-view"></div> </section> <!-- Sessions by Agent --> <div class="charts-row" id="agent-section" data-nav-section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Sessions by Agent</h2> <p class="chart-card__desc">Task count per agent, stacked by outcome</p> </div> <div class="chart-card__body" id="agent-chart"></div> </section> <section class="chart-card" id="model-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Model Usage</h2> <p class="chart-card__desc">Tasks by model</p> </div> <div class="chart-card__body" id="model-chart"></div> </section> </div> <!-- Charts Row: Tiers + Mechanism --> <div class="charts-row" id="tier-section" data-nav-section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Tier Distribution</h2> <p class="chart-card__desc">Model tier breakdown</p> </div> <div class="chart-card__body" id="tier-chart"></div> </section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Delegation Mechanism</h2> <p class="chart-card__desc">Sub-agent vs background split</p> </div> <div class="chart-card__body" id="mechanism-chart"></div> </section> </div> <!-- Quality Section --> <section class="chart-card" id="quality-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Quality / Review</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Code review results and dispute resolution." data-tooltip="Code review results and dispute resolution."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Code review results and quality gate outcomes for the selected convoy</p> </div> <div class="chart-card__body"> <div id="quality-cards" class="task-summary-cards"></div> <div id="quality-review-table"></div> </div> </section> <!-- Reliability Section --> <section class="chart-card" id="reliability-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Reliability</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Errors, retry attempts, and safety mechanisms." data-tooltip="Errors, retry attempts, and safety mechanisms."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Retry queue and error overview for the selected convoy</p> </div> <div class="chart-card__body"> <div id="reliability-dlq-card"></div> <div id="reliability-dlq-table"></div> <div style="margin-top:24px"> <h3 style="font-size:1rem;font-weight:600;color:var(--text-secondary);margin:0 0 12px">Error Overview</h3> <div id="reliability-error-overview"></div> </div> </div> </section> <!-- Drift Section --> <section class="chart-card" id="drift-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Drift</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: How far the actual work deviated from the original plan." data-tooltip="How far the actual work deviated from the original plan."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Plan adherence and deviation metrics for the selected convoy</p> </div> <div class="chart-card__body"> <div id="drift-cards" class="task-summary-cards"></div> <div id="drift-secret-banner" style="display:none"></div> </div> </section> <!-- Outputs Section --> <section class="chart-card" id="outputs-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Outputs &amp; Artifacts</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Files, data, and summaries produced by this convoy." data-tooltip="Files, data, and summaries produced by this convoy."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Files, summaries, and structured data produced by tasks in this convoy</p> </div> <div class="chart-card__body"> <div id="outputs-cards" class="task-summary-cards"></div> <div id="artifact-table-wrap"></div> </div> </section> <!-- Event Timeline Section --> <section class="chart-card" id="event-timeline-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Event Timeline</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Chronological log of everything that happened during this run." data-tooltip="Chronological log of everything that happened during this run."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Convoy events in chronological order</p> </div> <div class="chart-card__body"> <div id="event-timeline-filters" class="timeline-filters"></div> <div id="event-timeline-list"></div> <div id="event-timeline-load-more" style="display:none;text-align:center;padding:16px"> <button class="dash-btn dash-btn--ghost" id="event-timeline-more-btn" type="button">Load more</button> </div> </div> </section> <!-- Execution Log --> <section class="chart-card" id="execution-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Execution Log</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Step-by-step trace of recent task activity." data-tooltip="Step-by-step trace of recent task activity."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Recent agent activity, step by step</p> </div> <div class="chart-card__body" id="execution-log"></div> </section> <!-- Fast Reviews --> <section class="chart-card" id="reviews-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Fast Reviews</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Single-reviewer quality gate results." data-tooltip="Single-reviewer quality gate results."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Single-reviewer quality gate results</p> </div> <div class="chart-card__body chart-card__body--table" id="reviews-table"></div> </section> <!-- Panel Reviews --> <section class="chart-card" id="panel-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Panel Reviews</h2> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Quality gate verdicts from majority-vote panels." data-tooltip="Quality gate verdicts from majority-vote panels."><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></span> <p class="chart-card__desc">Quality gate verdicts and fix items</p> </div> <div class="chart-card__body" id="panel-chart"></div> </section> </div><!-- .view-convoy-detail --> </main> </div> </body></html> <script>(function(){const base = "/";
8
8
 
9
9
  // ── Data Loading ──────────────────────────────────────────
10
10
 
@@ -570,6 +570,18 @@ Export
570
570
  if (sumTokens > 0) {
571
571
  setKpiValue('overall-total-tokens', formatTokens(sumTokens));
572
572
  setKpiValue('overall-total-cost', '$' + sumCost.toFixed(2));
573
+ } else {
574
+ // Final fallback: estimate from task count (~5000 tokens/task at ~$8/1M tokens avg)
575
+ var taskTotal = (stats.taskTotals && stats.taskTotals.totalTasks) || 0;
576
+ if (taskTotal > 0) {
577
+ var estTokens = taskTotal * 5000;
578
+ var estCost = estTokens * 0.000008;
579
+ setKpiValue('overall-total-tokens', '~' + formatTokens(estTokens));
580
+ setKpiValue('overall-total-cost', '~$' + estCost.toFixed(2));
581
+ } else {
582
+ setKpiValue('overall-total-tokens', 'N/A (estimated)');
583
+ setKpiValue('overall-total-cost', 'N/A (estimated)');
584
+ }
573
585
  }
574
586
  }
575
587
  }
@@ -608,14 +620,14 @@ Export
608
620
  return;
609
621
  }
610
622
  var maxCount = Math.max.apply(null, timeline.map(function(d) { return d.count; }));
611
- var html = '<div class="activity-timeline">';
623
+ var html = '<div class="activity-timeline activity-timeline--vertical">';
612
624
  for (var i = 0; i < timeline.length; i++) {
613
625
  var d = timeline[i];
614
626
  var pct = maxCount > 0 ? Math.round((d.count / maxCount) * 100) : 0;
615
- html += '<div class="bar-row">' +
616
- '<span class="bar-label">' + formatShortDate(d.date) + '</span>' +
617
- '<div class="bar-track"><div class="bar-segment bar--accent" style="width:' + pct + '%"></div></div>' +
618
- '<span class="bar-value">' + d.count + '</span>' +
627
+ html += '<div class="vbar-col">' +
628
+ '<span class="vbar-value">' + d.count + '</span>' +
629
+ '<div class="vbar-track"><div class="vbar-fill" style="height:' + pct + '%"></div></div>' +
630
+ '<span class="vbar-label">' + formatShortDate(d.date) + '</span>' +
619
631
  '</div>';
620
632
  }
621
633
  html += '</div>';
@@ -643,13 +655,22 @@ Export
643
655
  for (var i = 0; i < sessions.length; i++) {
644
656
  var s = sessions[i];
645
657
  var outcomeClass = s.outcome === 'success' ? 'outcome-badge--success' : s.outcome === 'failed' ? 'outcome-badge--failed' : 'outcome-badge--partial';
658
+ var filesVal = s.files_changed;
659
+ var filesCell;
660
+ if (Array.isArray(s.file_partition) && s.file_partition.length > 0) {
661
+ filesCell = '<span class="tooltip-trigger" data-tooltip="' + escapeHtml(s.file_partition.join('\n')) + '">' + s.file_partition.length + ' file' + (s.file_partition.length !== 1 ? 's' : '') + '</span>';
662
+ } else if (typeof filesVal === 'number') {
663
+ filesCell = String(filesVal);
664
+ } else {
665
+ filesCell = '0';
666
+ }
646
667
  tbody += '<tr>' +
647
- '<td class="td-agent">' + escapeHtml(s.agent || '\u2014') + '</td>' +
648
- '<td class="td-task">' + escapeHtml(s.task || '\u2014') + '</td>' +
668
+ '<td class="td-agent" title="' + escapeHtml(s.agent || '') + '">' + escapeHtml(s.agent || '\u2014') + '</td>' +
669
+ '<td class="td-task" title="' + escapeHtml(s.task || '') + '">' + escapeHtml(s.task || '\u2014') + '</td>' +
649
670
  '<td><span class="outcome-badge ' + outcomeClass + '">' + escapeHtml(s.outcome || '\u2014') + '</span></td>' +
650
671
  '<td>' + (s.duration_min != null ? s.duration_min + 'm' : '\u2014') + '</td>' +
651
672
  '<td>' + (s.retries || 0) + '</td>' +
652
- '<td>' + (s.files_changed || 0) + '</td>' +
673
+ '<td>' + filesCell + '</td>' +
653
674
  '<td>' + (s.timestamp ? formatTime(s.timestamp) : '\u2014') + '</td>' +
654
675
  '</tr>';
655
676
  }
@@ -683,7 +704,6 @@ Export
683
704
  if (tasksSection) tasksSection.style.display = '';
684
705
  renderDetailPipeline(detail.tasks || []);
685
706
  renderDetailAgentChart(detail.tasks || []);
686
- renderDetailOutcomeChart(detail.tasks || []);
687
707
  renderDetailTierChart(detail.tasks || []);
688
708
  renderDetailMechanismChart(detail.events || []);
689
709
  renderDetailModelChart(detail.tasks || []);
@@ -914,8 +934,7 @@ Export
914
934
 
915
935
  // ── Convoy Detail: Derive Tier from Model ────────────────
916
936
 
917
- function deriveTier(model) {
918
- if (!model) return 'unknown';
937
+ function deriveTier(model, agent) {
919
938
  var TIER_MAP = {
920
939
  'claude-opus-4-6': 'premium',
921
940
  'claude-opus-4': 'premium',
@@ -929,15 +948,47 @@ Export
929
948
  'gemini-3.1-pro': 'standard',
930
949
  'gemini-3.0-flash': 'economy',
931
950
  };
932
- if (TIER_MAP[model]) return TIER_MAP[model];
933
- // Fallback heuristics for unknown models
934
- var m = model.toLowerCase();
935
- if (m.includes('opus')) return 'premium';
936
- if (m.includes('sonnet') || m.includes('pro')) return 'standard';
937
- if (m.includes('haiku') || m.includes('flash') || m.includes('mini')) return 'economy';
951
+ if (model && model !== 'copilot' && model !== 'cursor' && model !== 'opencode' && model !== 'claude') {
952
+ if (TIER_MAP[model]) return TIER_MAP[model];
953
+ var m = model.toLowerCase();
954
+ if (m.includes('opus')) return 'premium';
955
+ if (m.includes('sonnet') || m.includes('pro')) return 'standard';
956
+ if (m.includes('haiku') || m.includes('flash') || m.includes('mini')) return 'economy';
957
+ }
958
+ // Derive from agent name when model is a generic adapter name
959
+ if (agent) {
960
+ var a = (typeof agent === 'string' ? agent : '').toLowerCase().replace(/[\s\/]+/g, '-');
961
+ if (a === 'architect' || a === 'team-lead' || a.includes('team-lead')) return 'premium';
962
+ if (a === 'reviewer' || a === 'documentation-writer' || a === 'seo-specialist' || a === 'session-guard') return 'economy';
963
+ if (['developer', 'ui-ux-expert', 'testing-expert', 'security-expert', 'performance-expert', 'devops-expert', 'data-expert', 'api-designer', 'copywriter', 'release-manager', 'content-engineer', 'database-engineer', 'researcher'].includes(a)) return 'standard';
964
+ }
965
+ if (!model) return 'unknown';
938
966
  return 'utility';
939
967
  }
940
968
 
969
+ function deriveModel(model, agent) {
970
+ if (model && model !== 'copilot' && model !== 'cursor' && model !== 'opencode' && model !== 'claude') {
971
+ return model;
972
+ }
973
+ if (agent) {
974
+ var AGENT_MODEL = {
975
+ 'developer': 'claude-sonnet-4-6', 'ui-ux-expert': 'claude-sonnet-4-6',
976
+ 'testing-expert': 'claude-sonnet-4-6', 'security-expert': 'claude-sonnet-4-6',
977
+ 'performance-expert': 'claude-sonnet-4-6', 'devops-expert': 'claude-sonnet-4-6',
978
+ 'data-expert': 'claude-sonnet-4-6', 'api-designer': 'claude-sonnet-4-6',
979
+ 'copywriter': 'claude-sonnet-4-6', 'release-manager': 'claude-sonnet-4-6',
980
+ 'content-engineer': 'claude-sonnet-4-6', 'database-engineer': 'claude-sonnet-4-6',
981
+ 'researcher': 'claude-sonnet-4-6',
982
+ 'architect': 'claude-opus-4-6', 'team-lead': 'claude-opus-4-6',
983
+ 'reviewer': 'claude-haiku-3-5', 'documentation-writer': 'claude-haiku-3-5',
984
+ 'seo-specialist': 'claude-haiku-3-5', 'session-guard': 'claude-haiku-3-5',
985
+ };
986
+ var a = agent.toLowerCase().replace(/[\s\/]+/g, '-');
987
+ return AGENT_MODEL[a] || model || 'unknown';
988
+ }
989
+ return model || 'unknown';
990
+ }
991
+
941
992
  // ── Convoy Detail: Pipeline View ─────────────────────────
942
993
 
943
994
  function renderDetailPipeline(tasks) {
@@ -951,26 +1002,34 @@ Export
951
1002
 
952
1003
  var phases = {};
953
1004
  tasks.forEach(function(t) {
954
- var p = t.phase != null ? t.phase : 1;
955
- if (!phases[p]) phases[p] = 0;
956
- phases[p]++;
1005
+ var p = t.phase != null ? t.phase : 0;
1006
+ if (!phases[p]) phases[p] = { count: 0, done: 0, failed: 0, running: 0 };
1007
+ phases[p].count++;
1008
+ if (t.status === 'done') phases[p].done++;
1009
+ else if (['failed', 'gate-failed', 'timed-out', 'hook-failed'].includes(t.status)) phases[p].failed++;
1010
+ else if (t.status === 'running' || t.status === 'assigned') phases[p].running++;
957
1011
  });
958
1012
 
959
- var stageConfig = [
960
- { label: 'Foundation', phase: 1, iconClass: 'pending', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2"/><line x1="8" y1="10" x2="16" y2="10"/><line x1="8" y1="14" x2="13" y2="14"/></svg>' },
961
- { label: 'Integration', phase: 2, iconClass: 'active', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>' },
962
- { label: 'Validation', phase: 3, iconClass: 'review', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>' },
963
- { label: 'QA Gate', phase: 4, iconClass: 'done', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>' },
1013
+ var phaseNums = Object.keys(phases).map(Number).sort(function(a, b) { return a - b; });
1014
+ var PHASE_LABELS = ['Foundation', 'Integration', 'Validation', 'QA Gate', 'Phase 5', 'Phase 6'];
1015
+ var PHASE_ICONS = [
1016
+ { iconClass: 'pending', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2"/><line x1="8" y1="10" x2="16" y2="10"/><line x1="8" y1="14" x2="13" y2="14"/></svg>' },
1017
+ { iconClass: 'active', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>' },
1018
+ { iconClass: 'review', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>' },
1019
+ { iconClass: 'done', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>' },
964
1020
  ];
965
1021
 
966
1022
  el.innerHTML =
967
1023
  '<div class="pipeline">' +
968
- stageConfig.map(function(stage, i) {
1024
+ phaseNums.map(function(pn, i) {
1025
+ var label = PHASE_LABELS[pn] !== undefined ? PHASE_LABELS[pn] : ('Phase ' + pn);
1026
+ var iconIdx = Math.min(i, PHASE_ICONS.length - 1);
1027
+ var phaseData = phases[pn];
969
1028
  return (i > 0 ? '<div class="pipeline-arrow">\u2192</div>' : '') +
970
1029
  '<div class="pipeline-stage">' +
971
- '<div class="pipeline-stage__icon pipeline-stage__icon--' + stage.iconClass + '">' + stage.icon + '</div>' +
972
- '<span class="pipeline-stage__count">' + (phases[stage.phase] || 0) + '</span>' +
973
- '<span class="pipeline-stage__label">' + stage.label + '</span>' +
1030
+ '<div class="pipeline-stage__icon pipeline-stage__icon--' + PHASE_ICONS[iconIdx].iconClass + '">' + PHASE_ICONS[iconIdx].icon + '</div>' +
1031
+ '<span class="pipeline-stage__count">' + phaseData.count + '</span>' +
1032
+ '<span class="pipeline-stage__label">' + label + '</span>' +
974
1033
  '</div>';
975
1034
  }).join('') +
976
1035
  '</div>';
@@ -1030,7 +1089,7 @@ Export
1030
1089
 
1031
1090
  var tierCounts = {};
1032
1091
  tasks.forEach(function(t) {
1033
- var tier = deriveTier(t.model);
1092
+ var tier = deriveTier(t.model, t.agent);
1034
1093
  tierCounts[tier] = (tierCounts[tier] || 0) + 1;
1035
1094
  });
1036
1095
 
@@ -1074,7 +1133,7 @@ Export
1074
1133
 
1075
1134
  var mechCounts = {};
1076
1135
  events.forEach(function(e) {
1077
- if (e.type === 'task_assigned' || e.type === 'task_started') {
1136
+ if (e.type === 'task_assigned' || e.type === 'task_started' || e.type === 'delegation') {
1078
1137
  var mech = (e.data && e.data.mechanism) || 'unknown';
1079
1138
  mechCounts[mech] = (mechCounts[mech] || 0) + 1;
1080
1139
  }
@@ -1111,39 +1170,6 @@ Export
1111
1170
  '<div class="donut-container"><div class="donut-wrap"><svg viewBox="0 0 180 180" class="donut-svg">' + circles.join('') + '</svg><div class="donut-center"><span class="donut-total">' + total + '</span><span class="donut-total-label">total</span></div></div><div class="donut-legend">' + legend + '</div></div>';
1112
1171
  }
1113
1172
 
1114
- // ── Convoy Detail: Delegation Outcome Chart ──────────────
1115
-
1116
- function renderDetailOutcomeChart(tasks) {
1117
- var el = document.getElementById('delegation-outcome-chart');
1118
- if (!el) return;
1119
-
1120
- if (!tasks || tasks.length === 0) {
1121
- el.innerHTML = emptyStateHtml('outcomes', 'No outcome data yet', 'Task outcome distribution will be shown here.');
1122
- return;
1123
- }
1124
-
1125
- var OUTCOME_COLORS = { done: '#22c55e', running: '#3b82f6', pending: '#64748b', assigned: '#64748b', failed: '#ef4444', 'gate-failed': '#f59e0b', 'timed-out': '#a78bfa', 'hook-failed': '#64748b', 'review-blocked': '#f59e0b', skipped: '#94a3b8' };
1126
-
1127
- var outcomeCounts = {};
1128
- tasks.forEach(function(t) {
1129
- var outcome = t.status || 'unknown';
1130
- outcomeCounts[outcome] = (outcomeCounts[outcome] || 0) + 1;
1131
- });
1132
-
1133
- var outcomes = Object.entries(outcomeCounts).sort(function(a, b) { return b[1] - a[1]; });
1134
- var maxCount = Math.max.apply(null, outcomes.map(function(o) { return o[1]; }));
1135
-
1136
- el.innerHTML = outcomes.map(function(entry) {
1137
- var name = entry[0];
1138
- var count = entry[1];
1139
- return '<div class="bar-row">' +
1140
- '<span class="bar-label">' + escapeHtml(name) + '</span>' +
1141
- '<div class="bar-track"><div class="bar-segment" style="width: ' + ((count / maxCount) * 100).toFixed(1) + '%; background: ' + (OUTCOME_COLORS[name] || '#64748b') + '"></div></div>' +
1142
- '<span class="bar-value">' + count + '</span>' +
1143
- '</div>';
1144
- }).join('');
1145
- }
1146
-
1147
1173
  // ── Convoy Detail: Model Chart ───────────────────────────
1148
1174
 
1149
1175
  function renderDetailModelChart(tasks) {
@@ -1157,7 +1183,8 @@ Export
1157
1183
 
1158
1184
  var modelCounts = {};
1159
1185
  tasks.forEach(function(t) {
1160
- if (t.model) modelCounts[t.model] = (modelCounts[t.model] || 0) + 1;
1186
+ var model = deriveModel(t.model, t.agent);
1187
+ if (model) modelCounts[model] = (modelCounts[model] || 0) + 1;
1161
1188
  });
1162
1189
 
1163
1190
  var models = Object.entries(modelCounts).sort(function(a, b) { return b[1] - a[1]; });
@@ -1481,10 +1508,13 @@ Export
1481
1508
  section.style.display = '';
1482
1509
 
1483
1510
  var d = detail.drift || {};
1484
- var maxScore = d.max_drift_score != null ? Math.round(d.max_drift_score * 100) : 0;
1511
+ // drift_score represents on-plan confidence (0.95 = 95% on-plan). Invert to deviation.
1512
+ var maxScoreRaw = d.max_drift_score;
1513
+ var deviationPct = maxScoreRaw != null ? Math.round((1 - maxScoreRaw) * 100) : 0;
1514
+ var tasksWithDrift = d.tasks_with_drift != null ? d.tasks_with_drift : 0;
1485
1515
  var driftCards = [
1486
- { label: 'Tasks With Drift', value: d.tasks_with_drift != null ? d.tasks_with_drift : 0, tooltip: 'Tasks where the agent deviated from the original plan or instructions.', mod: 'waiting' },
1487
- { label: 'Max Drift Score', value: maxScore + '%', tooltip: 'The highest drift score recorded. 0% = on-track, 100% = fully off-plan.', mod: 'errors' },
1516
+ { label: 'Tasks With Drift', value: tasksWithDrift, tooltip: 'Tasks where the agent was checked for plan deviation.', mod: tasksWithDrift > 0 ? 'waiting' : 'done' },
1517
+ { label: 'Max Deviation', value: deviationPct + '%', tooltip: 'Highest deviation from the original plan. 0% = perfectly on-track, 100% = completely off-plan.', mod: deviationPct > 30 ? 'errors' : deviationPct > 10 ? 'waiting' : 'done' },
1488
1518
  { label: 'Drift Retried', value: d.drift_retried_tasks != null ? d.drift_retried_tasks : 0, tooltip: 'Tasks that were retried because drift exceeded the allowed threshold.', mod: 'running' },
1489
1519
  ];
1490
1520
 
@@ -1529,44 +1559,70 @@ Export
1529
1559
  var artifacts = detail.artifacts || [];
1530
1560
  var artifactCount = artifacts.length;
1531
1561
 
1562
+ // Fallback: count unique files touched across all tasks
1563
+ var fileSet = {};
1564
+ var tasks = detail.tasks || [];
1565
+ tasks.forEach(function(t) {
1566
+ if (t.files && Array.isArray(t.files)) {
1567
+ t.files.forEach(function(f) { fileSet[f] = true; });
1568
+ }
1569
+ });
1570
+ var fileCount = Object.keys(fileSet).length;
1571
+ var displayCount = artifactCount > 0 ? artifactCount : fileCount;
1572
+ var countLabel = artifactCount > 0 ? 'Artifacts' : 'Files Touched';
1573
+ var countTooltip = artifactCount > 0 ? 'Structured outputs produced by tasks.' : 'Files created or modified by tasks in this convoy.';
1574
+
1532
1575
  var cardsEl = document.getElementById('outputs-cards');
1533
1576
  if (cardsEl) {
1534
1577
  cardsEl.innerHTML =
1535
1578
  '<div class="task-summary-card task-summary-card--done">' +
1536
- '<span class="task-summary-card__label">Outputs Produced ' +
1537
- '<span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Files, data, and summaries produced by this convoy." data-tooltip="Files, data, and summaries produced by this convoy.">' + INFO_ICON + '</span>' +
1579
+ '<span class="task-summary-card__label">' + countLabel + ' ' +
1580
+ '<span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: ' + escapeHtml(countTooltip) + '" data-tooltip="' + escapeHtml(countTooltip) + '">' + INFO_ICON + '</span>' +
1538
1581
  '</span>' +
1539
- '<span class="task-summary-card__value">' + artifactCount + '</span>' +
1582
+ '<span class="task-summary-card__value">' + displayCount + '</span>' +
1540
1583
  '</div>';
1541
1584
  }
1542
1585
 
1543
1586
  var tableEl = document.getElementById('artifact-table-wrap');
1544
1587
  if (!tableEl) return;
1545
1588
 
1546
- if (artifacts.length === 0) {
1547
- tableEl.innerHTML = emptyStateHtml('pipeline', 'No artifacts', 'Artifacts produced by tasks will appear here.');
1589
+ if (artifacts.length === 0 && fileCount === 0) {
1590
+ tableEl.innerHTML = emptyStateHtml('pipeline', 'No outputs', 'No artifacts or file changes recorded.');
1548
1591
  return;
1549
1592
  }
1550
1593
 
1551
1594
  var TYPE_BADGE_COLORS = { file: '#64748b', summary: '#3b82f6', json: '#a78bfa' };
1552
1595
 
1553
- tableEl.innerHTML =
1554
- '<table class="sessions-table" style="margin-top:16px">' +
1555
- '<thead><tr><th scope="col">Name</th><th scope="col">Type</th><th scope="col">Task</th><th scope="col">Created At</th></tr></thead>' +
1556
- '<tbody>' +
1557
- artifacts.map(function(a) {
1558
- var badgeColor = TYPE_BADGE_COLORS[a.type] || '#64748b';
1559
- var typeBadge = '<span class="artifact-type-badge" style="background:' + badgeColor + '">' + escapeHtml(a.type) + '</span>';
1560
- var taskCell = a.task_id ? escapeHtml(a.task_id) : '<span style="opacity:0.4">—</span>';
1561
- var created = a.created_at ? formatTime(a.created_at) : '';
1562
- return '<tr>' +
1563
- '<td>' + escapeHtml(a.name) + '</td>' +
1564
- '<td>' + typeBadge + '</td>' +
1565
- '<td class="td-task">' + taskCell + '</td>' +
1566
- '<td>' + created + '</td>' +
1567
- '</tr>';
1568
- }).join('') +
1569
- '</tbody></table>';
1596
+ if (artifacts.length > 0) {
1597
+ tableEl.innerHTML =
1598
+ '<table class="sessions-table" style="margin-top:16px">' +
1599
+ '<thead><tr><th scope="col">Name</th><th scope="col">Type</th><th scope="col">Task</th><th scope="col">Created At</th></tr></thead>' +
1600
+ '<tbody>' +
1601
+ artifacts.map(function(a) {
1602
+ var badgeColor = TYPE_BADGE_COLORS[a.type] || '#64748b';
1603
+ var typeBadge = '<span class="artifact-type-badge" style="background:' + badgeColor + '">' + escapeHtml(a.type) + '</span>';
1604
+ var taskCell = a.task_id ? escapeHtml(a.task_id) : '<span style="opacity:0.4">\u2014</span>';
1605
+ var created = a.created_at ? formatTime(a.created_at) : '\u2014';
1606
+ return '<tr>' +
1607
+ '<td>' + escapeHtml(a.name) + '</td>' +
1608
+ '<td>' + typeBadge + '</td>' +
1609
+ '<td class="td-task">' + taskCell + '</td>' +
1610
+ '<td>' + created + '</td>' +
1611
+ '</tr>';
1612
+ }).join('') +
1613
+ '</tbody></table>';
1614
+ } else {
1615
+ var fileEntries = Object.keys(fileSet).sort();
1616
+ tableEl.innerHTML =
1617
+ '<table class="sessions-table" style="margin-top:16px">' +
1618
+ '<thead><tr><th scope="col">File</th><th scope="col">Task</th></tr></thead>' +
1619
+ '<tbody>' +
1620
+ fileEntries.map(function(f) {
1621
+ var ownerTask = tasks.find(function(t) { return t.files && Array.isArray(t.files) && t.files.indexOf(f) >= 0; });
1622
+ return '<tr><td>' + escapeHtml(f) + '</td><td class="td-task">' + (ownerTask ? escapeHtml(ownerTask.id) : '\u2014') + '</td></tr>';
1623
+ }).join('') +
1624
+ '</tbody></table>';
1625
+ }
1570
1626
  }
1571
1627
 
1572
1628
  // ── Event Timeline Section ─────────────────────────────