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.
- package/dist/cli/convoy/engine.d.ts.map +1 -1
- package/dist/cli/convoy/engine.js +79 -4
- package/dist/cli/convoy/engine.js.map +1 -1
- package/dist/cli/convoy/engine.test.js +11 -9
- package/dist/cli/convoy/engine.test.js.map +1 -1
- package/dist/dashboard/scripts/etl.js +17 -2
- package/dist/dashboard/scripts/etl.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/convoy/engine.test.ts +11 -9
- package/src/cli/convoy/engine.ts +78 -4
- package/src/dashboard/dist/_astro/index.6xXNs4L2.css +1 -0
- package/src/dashboard/dist/data/convoy-list.json +27 -13
- package/src/dashboard/dist/data/convoys/demo-api-v2.json +16 -10
- package/src/dashboard/dist/data/convoys/demo-auth-revamp.json +25 -15
- package/src/dashboard/dist/data/convoys/demo-dashboard-ui.json +35 -21
- package/src/dashboard/dist/data/convoys/demo-data-pipeline.json +17 -11
- package/src/dashboard/dist/data/convoys/demo-deploy-ci.json +8 -4
- package/src/dashboard/dist/data/convoys/demo-docs-update.json +13 -9
- package/src/dashboard/dist/data/convoys/demo-perf-opt.json +22 -14
- package/src/dashboard/dist/data/overall-stats.json +36 -2
- package/src/dashboard/dist/index.html +149 -93
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/dashboard/public/data/convoy-list.json +27 -13
- package/src/dashboard/public/data/convoys/demo-api-v2.json +16 -10
- package/src/dashboard/public/data/convoys/demo-auth-revamp.json +25 -15
- package/src/dashboard/public/data/convoys/demo-dashboard-ui.json +35 -21
- package/src/dashboard/public/data/convoys/demo-data-pipeline.json +17 -11
- package/src/dashboard/public/data/convoys/demo-deploy-ci.json +8 -4
- package/src/dashboard/public/data/convoys/demo-docs-update.json +13 -9
- package/src/dashboard/public/data/convoys/demo-perf-opt.json +22 -14
- package/src/dashboard/public/data/overall-stats.json +36 -2
- package/src/dashboard/scripts/etl.ts +15 -3
- package/src/dashboard/scripts/generate-demo-db.ts +42 -34
- package/src/dashboard/src/pages/index.astro +159 -112
- package/src/dashboard/src/styles/dashboard.css +58 -3
- package/src/dashboard/dist/_astro/index.wyN9vmjZ.css +0 -1
- package/src/dashboard/dist/data/convoys/demo-convoy-1.json +0 -111
- package/src/dashboard/dist/data/convoys/demo-convoy-2.json +0 -72
- package/src/dashboard/dist/data/pipelines.ndjson +0 -5285
- package/src/dashboard/public/data/convoys/demo-convoy-1.json +0 -111
- package/src/dashboard/public/data/convoys/demo-convoy-2.json +0 -72
- 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":
|
|
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.
|
|
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
|
+
<!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">—</span> </div> <div class="overall-kpi" id="overall-running"> <span class="overall-kpi__label">Running Now</span> <span class="overall-kpi__value">—</span> </div> <div class="overall-kpi" id="overall-success-rate"> <span class="overall-kpi__label">Success Rate</span> <span class="overall-kpi__value">—</span> </div> <div class="overall-kpi" id="overall-avg-duration"> <span class="overall-kpi__label">Avg Duration</span> <span class="overall-kpi__value">—</span> </div> <div class="overall-kpi" id="overall-total-tokens"> <span class="overall-kpi__label">Total Tokens</span> <span class="overall-kpi__value">—</span> </div> <div class="overall-kpi" id="overall-total-cost"> <span class="overall-kpi__label">Total Cost</span> <span class="overall-kpi__value">—</span> </div> <div class="overall-kpi" id="overall-total-tasks"> <span class="overall-kpi__label">Total Tasks</span> <span class="overall-kpi__value">—</span> </div> <div class="overall-kpi" id="overall-total-retries"> <span class="overall-kpi__label">Total Retries</span> <span class="overall-kpi__value">—</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 & 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">—</span> </div> <div class="overall-kpi" id="overall-running"> <span class="overall-kpi__label">Running Now</span> <span class="overall-kpi__value">—</span> </div> <div class="overall-kpi" id="overall-total-tasks"> <span class="overall-kpi__label">Total Tasks</span> <span class="overall-kpi__value">—</span> </div> <div class="overall-kpi" id="overall-total-retries"> <span class="overall-kpi__label">Total Retries</span> <span class="overall-kpi__value">—</span> </div> <div class="overall-kpi" id="overall-success-rate"> <span class="overall-kpi__label">Success Rate</span> <span class="overall-kpi__value">—</span> </div> <div class="overall-kpi" id="overall-avg-duration"> <span class="overall-kpi__label">Avg Duration</span> <span class="overall-kpi__value">—</span> </div> <div class="overall-kpi" id="overall-total-tokens"> <span class="overall-kpi__label">Total Tokens</span> <span class="overall-kpi__value">—</span> </div> <div class="overall-kpi" id="overall-total-cost"> <span class="overall-kpi__label">Total Cost</span> <span class="overall-kpi__value">—</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 & 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="
|
|
616
|
-
'<span class="
|
|
617
|
-
'<div class="
|
|
618
|
-
'<span class="
|
|
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>' +
|
|
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 (
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
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 :
|
|
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
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
{
|
|
963
|
-
{
|
|
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
|
-
|
|
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--' +
|
|
972
|
-
'<span class="pipeline-stage__count">' +
|
|
973
|
-
'<span class="pipeline-stage__label">' +
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
1487
|
-
{ label: 'Max
|
|
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">
|
|
1537
|
-
'<span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info:
|
|
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">' +
|
|
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
|
|
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
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
'<
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
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 ─────────────────────────────
|