opencastle 0.27.3 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/bin/cli.mjs +13 -5
  2. package/dist/cli/convoy/engine.js +2 -2
  3. package/dist/cli/convoy/engine.js.map +1 -1
  4. package/dist/cli/convoy/engine.test.js +1 -1
  5. package/dist/cli/convoy/engine.test.js.map +1 -1
  6. package/dist/cli/convoy/issues.js +3 -3
  7. package/dist/cli/convoy/issues.js.map +1 -1
  8. package/dist/cli/convoy/issues.test.js +4 -3
  9. package/dist/cli/convoy/issues.test.js.map +1 -1
  10. package/dist/cli/pipeline.d.ts +3 -0
  11. package/dist/cli/pipeline.d.ts.map +1 -0
  12. package/dist/cli/pipeline.js +305 -0
  13. package/dist/cli/pipeline.js.map +1 -0
  14. package/dist/cli/plan.d.ts +37 -0
  15. package/dist/cli/plan.d.ts.map +1 -1
  16. package/dist/cli/plan.js +321 -161
  17. package/dist/cli/plan.js.map +1 -1
  18. package/dist/cli/validate.d.ts +3 -0
  19. package/dist/cli/validate.d.ts.map +1 -0
  20. package/dist/cli/validate.js +60 -0
  21. package/dist/cli/validate.js.map +1 -0
  22. package/package.json +5 -4
  23. package/src/cli/convoy/engine.test.ts +1 -1
  24. package/src/cli/convoy/engine.ts +2 -2
  25. package/src/cli/convoy/issues.test.ts +3 -2
  26. package/src/cli/convoy/issues.ts +3 -3
  27. package/src/cli/pipeline.ts +343 -0
  28. package/src/cli/plan.ts +357 -153
  29. package/src/cli/validate.ts +65 -0
  30. package/src/dashboard/dist/data/convoy-list.json +54 -9
  31. package/src/dashboard/dist/data/convoys/demo-api-v2.json +177 -0
  32. package/src/dashboard/dist/data/convoys/demo-auth-revamp.json +239 -0
  33. package/src/dashboard/dist/data/convoys/demo-dashboard-ui.json +328 -0
  34. package/src/dashboard/dist/data/convoys/demo-data-pipeline.json +187 -0
  35. package/src/dashboard/dist/data/convoys/demo-deploy-ci.json +153 -0
  36. package/src/dashboard/dist/data/convoys/demo-docs-update.json +154 -0
  37. package/src/dashboard/dist/data/convoys/demo-perf-opt.json +227 -0
  38. package/src/dashboard/dist/data/events.ndjson +115 -0
  39. package/src/dashboard/dist/data/overall-stats.json +56 -13
  40. package/src/dashboard/dist/data/pipelines.ndjson +5285 -0
  41. package/src/dashboard/dist/index.html +39 -16
  42. package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
  43. package/src/dashboard/public/data/convoy-list.json +54 -9
  44. package/src/dashboard/public/data/convoys/demo-api-v2.json +177 -0
  45. package/src/dashboard/public/data/convoys/demo-auth-revamp.json +239 -0
  46. package/src/dashboard/public/data/convoys/demo-dashboard-ui.json +328 -0
  47. package/src/dashboard/public/data/convoys/demo-data-pipeline.json +187 -0
  48. package/src/dashboard/public/data/convoys/demo-deploy-ci.json +153 -0
  49. package/src/dashboard/public/data/convoys/demo-docs-update.json +154 -0
  50. package/src/dashboard/public/data/convoys/demo-perf-opt.json +227 -0
  51. package/src/dashboard/public/data/events.ndjson +115 -0
  52. package/src/dashboard/public/data/overall-stats.json +56 -13
  53. package/src/dashboard/public/data/pipelines.ndjson +5285 -0
  54. package/src/dashboard/scripts/etl.ts +24 -3
  55. package/src/dashboard/scripts/generate-demo-db.ts +482 -115
  56. package/src/dashboard/src/pages/index.astro +46 -23
  57. package/src/orchestrator/prompts/fix-convoy.prompt.md +79 -0
  58. package/src/orchestrator/prompts/generate-convoy.prompt.md +53 -58
  59. package/src/orchestrator/prompts/generate-prd.prompt.md +120 -0
  60. package/src/orchestrator/prompts/validate-convoy.prompt.md +89 -0
  61. package/src/orchestrator/prompts/validate-prd.prompt.md +83 -0
@@ -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.6L3_HsPT.css"></head> <body> <script>(function(){const overallStats = {"convoyCounts":{"total":2,"running":1,"done":1,"failed":0,"gate_failed":0},"durationStats":{"avg_sec":33.000022172927856,"p95_sec":33.000022172927856,"max_sec":33.000022172927856},"tokenCostTotals":{"total_tokens":8432,"total_cost_usd":0.84},"topAgents":[{"agent":"developer","task_count":3,"total_tokens":8432}],"topModels":[{"model":"claude-sonnet-4-6","task_count":3,"total_tokens":8432}],"dlqSummary":{"count":0,"top_failure_types":[]}};
2
- const convoyList = [{"id":"demo-convoy-2","name":"Demo Convoy Beta","status":"running","created_at":"2026-01-15T10:05:00.000Z","finished_at":null,"total_tokens":null,"total_cost_usd":null},{"id":"demo-convoy-1","name":"Demo Convoy Alpha","status":"done","created_at":"2026-01-15T10:00:00.000Z","finished_at":"2026-01-15T10:00:33.000Z","total_tokens":8432,"total_cost_usd":0.84}];
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.6L3_HsPT.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},{"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},{"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},{"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},{"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},{"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},{"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}];
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"> <img class="dash-header__icon" src="/icon-192.png" alt="OpenCastle" width="32" height="32"> <h1 class="dash-header__title">Observability Dashboard</h1> </div> <div class="dash-header__actions"> <div class="convoy-selector"> <label class="convoy-selector__label" for="convoy-select">Convoy</label> <select class="convoy-selector__select" id="convoy-select" aria-label="Select a convoy to view its details"> <option value="">Select convoy…</option> </select> </div> <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" aria-label="Overview section">Overview</a></li> <li><a class="dash-sidebar__link" href="#convoy-section" data-section="convoy-section" aria-label="Convoy section">Convoy</a></li> <li><a class="dash-sidebar__link" href="#tasks-section" data-section="tasks-section" aria-label="Tasks section">Tasks</a></li> <li><a class="dash-sidebar__link" href="#quality-section" data-section="quality-section" aria-label="Quality section">Quality</a></li> <li><a class="dash-sidebar__link" href="#reliability-section" data-section="reliability-section" aria-label="Reliability section">Reliability</a></li> <li><a class="dash-sidebar__link" href="#drift-section" data-section="drift-section" aria-label="Drift section">Drift</a></li> <li><a class="dash-sidebar__link" href="#outputs-section" data-section="outputs-section" aria-label="Outputs section">Outputs</a></li> <li><a class="dash-sidebar__link" href="#event-timeline-section" data-section="event-timeline-section" aria-label="Event Log section">Event Log</a></li> </ul> </nav> <main class="dash-main"> <!-- 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.">ℹ️</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">ℹ️</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> </section> <!-- Selected Convoy Header --> <section class="convoy-detail-header" id="convoy-detail-header"> <div class="convoy-detail-header__top"> <h2 class="convoy-detail-header__name" id="selected-convoy-name">No convoy selected</h2> <span class="status-badge" id="selected-convoy-status" role="status"></span> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: A convoy is a set of AI tasks working together on one goal." data-tooltip="A convoy is a set of AI tasks working together on one goal.">ℹ️</span> </div> <p class="convoy-status-explanation" id="convoy-status-explanation" role="status" aria-live="polite"></p> <div class="convoy-detail-header__meta" id="selected-convoy-meta"> <!-- Populated by JS: branch, timestamps, duration, tokens, cost --> </div> </section> <!-- Tasks Section --> <section class="chart-card" id="tasks-section" data-nav-section style="display:none"> <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.">ℹ️</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> <!-- Quality Section --> <section class="chart-card" id="quality-section" data-nav-section style="display:none"> <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.">ℹ️</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 style="display:none"> <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.">ℹ️</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 style="display:none"> <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.">ℹ️</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 style="display:none"> <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.">ℹ️</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 style="display:none"> <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.">ℹ️</span> <p class="chart-card__desc">Convoy events in reverse 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> <!-- Filter Bar --> <div class="filter-bar" id="filter-bar"> <div class="filter-group"> <label class="filter-label" for="filter-date-from">From</label> <input class="filter-input" type="date" id="filter-date-from"> </div> <div class="filter-group"> <label class="filter-label" for="filter-date-to">To</label> <input class="filter-input" type="date" id="filter-date-to"> </div> <div class="filter-group"> <label class="filter-label" for="filter-agent">Agent</label> <select class="filter-select" id="filter-agent"> <option value="">All agents</option> </select> </div> <div class="filter-group"> <label class="filter-label" for="filter-outcome">Outcome</label> <select class="filter-select" id="filter-outcome"> <option value="">All outcomes</option> <option value="success">Success</option> <option value="partial">Partial</option> <option value="failed">Failed</option> </select> </div> <div class="filter-group"> <label class="filter-label" for="filter-convoy">Convoy</label> <select class="filter-select" id="filter-convoy"> <option value="">All convoys</option> </select> </div> <div class="filter-group"> <label class="filter-label" for="filter-pipeline">Pipeline</label> <select class="filter-select" id="filter-pipeline"> <option value="">All</option> </select> </div> <button class="dash-btn dash-btn--ghost filter-reset" id="filter-reset" type="button">Reset</button> </div> <!-- Convoy Status Section --> <section class="chart-card convoy-status" id="convoy-section" data-nav-section style="display:none"> <div class="chart-card__header"> <h2 class="chart-card__title">Convoy Status</h2> <p class="chart-card__desc" id="convoy-desc">Select a convoy to view details</p> </div> <div class="chart-card__body" id="convoy-body"></div> </section> <!-- Convoy Pipeline (Chaining) Section --> <section class="chart-card" id="convoy-pipeline-section" data-nav-section style="display:none"> <div class="chart-card__header"> <h2 class="chart-card__title">Convoy Pipeline</h2> <p class="chart-card__desc" id="convoy-pipeline-desc">Pipeline convoy chain progress</p> </div> <div class="chart-card__body" id="convoy-pipeline-body"></div> </section> <!-- KPI Row --> <section class="kpi-row" id="kpi-row" data-nav-section> <div class="kpi-card" id="kpi-sessions"> <span class="kpi-card__label">Total Sessions</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> <div class="kpi-card" id="kpi-success"> <span class="kpi-card__label">Success Rate</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> <div class="kpi-card" id="kpi-delegations"> <span class="kpi-card__label">Total Delegations</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> <div class="kpi-card" id="kpi-duration"> <span class="kpi-card__label">Avg Duration</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> <div class="kpi-card" id="kpi-retries"> <span class="kpi-card__label">Total Retries</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> <div class="kpi-card" id="kpi-lessons"> <span class="kpi-card__label">Lessons Added</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> </section> <!-- Pipeline View (Steroids-inspired) --> <section class="chart-card" id="pipeline-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Task Pipeline</h2> <p class="chart-card__desc">Delegation flow across execution phases</p> </div> <div class="chart-card__body" id="pipeline-view"> <div class="loading-skeleton"></div> </div> </section> <!-- Charts Row 1 --> <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">Stacked by outcome</p> </div> <div class="chart-card__body" id="agent-chart"> <div class="loading-skeleton"></div> </div> </section> <section class="chart-card" id="tier-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Tier Distribution</h2> <p class="chart-card__desc">Delegation model tiers</p> </div> <div class="chart-card__body" id="tier-chart"> <div class="loading-skeleton"></div> </div> </section> </div> <!-- Charts Row: Delegation Insights --> <div class="charts-row" id="delegation-section" data-nav-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 class="loading-skeleton"></div> </div> </section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Delegation Outcomes</h2> <p class="chart-card__desc">Success rate by delegation</p> </div> <div class="chart-card__body" id="delegation-outcome-chart"> <div class="loading-skeleton"></div> </div> </section> </div> <!-- Charts Row 2 --> <div class="charts-row" id="timeline-section" data-nav-section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Timeline</h2> <p class="chart-card__desc">Sessions and delegations over time</p> </div> <div class="chart-card__body" id="timeline-chart"> <div class="loading-skeleton"></div> </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">Sessions by model</p> </div> <div class="chart-card__body" id="model-chart"> <div class="loading-skeleton"></div> </div> </section> </div> <!-- Execution Log (Duvo-inspired) --> <section class="chart-card" id="execution-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Execution Log</h2> <p class="chart-card__desc">Recent agent activity, step by step</p> </div> <div class="chart-card__body" id="execution-log"> <div class="loading-skeleton"></div> </div> </section> <!-- Panel Results --> <section class="chart-card" id="panel-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Panel Reviews</h2> <p class="chart-card__desc">Quality gate verdicts and fix items</p> </div> <div class="chart-card__body" id="panel-chart"> <div class="loading-skeleton"></div> </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> <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 class="loading-skeleton"></div> </div> </section> <!-- Sessions Table --> <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">Last 15 sessions by timestamp</p> </div> <div class="chart-card__body chart-card__body--table" id="sessions-table"> <div class="loading-skeleton"></div> </div> </section> </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" aria-label="Overview section">Overview</a></li> <li><a class="dash-sidebar__link" href="#tasks-section" data-section="tasks-section" aria-label="Tasks section">Tasks</a></li> <li><a class="dash-sidebar__link" href="#quality-section" data-section="quality-section" aria-label="Quality section">Quality</a></li> <li><a class="dash-sidebar__link" href="#reliability-section" data-section="reliability-section" aria-label="Reliability section">Reliability</a></li> <li><a class="dash-sidebar__link" href="#drift-section" data-section="drift-section" aria-label="Drift section">Drift</a></li> <li><a class="dash-sidebar__link" href="#outputs-section" data-section="outputs-section" aria-label="Outputs section">Outputs</a></li> <li><a class="dash-sidebar__link" href="#event-timeline-section" data-section="event-timeline-section" aria-label="Event Log section">Event Log</a></li> <li><a class="dash-sidebar__link" href="#convoy-section" data-section="convoy-section" aria-label="Convoy section">Convoy</a></li> </ul> </nav> <main class="dash-main"> <!-- 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> </section> <!-- Selected Convoy Header --> <section class="convoy-detail-header" id="convoy-detail-header"> <div class="convoy-detail-header__top"> <h2 class="convoy-detail-header__name" id="selected-convoy-name">No convoy selected</h2> <span class="status-badge" id="selected-convoy-status" role="status"></span> <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: A convoy is a set of AI tasks working together on one goal." data-tooltip="A convoy is a set of AI tasks working together on one goal."><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> <p class="convoy-status-explanation" id="convoy-status-explanation" role="status" aria-live="polite"></p> <div class="convoy-detail-header__meta" id="selected-convoy-meta"> <!-- Populated by JS: branch, timestamps, duration, tokens, cost --> </div> </section> <!-- Tasks Section --> <section class="chart-card" id="tasks-section" data-nav-section style="display:none"> <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> <!-- Quality Section --> <section class="chart-card" id="quality-section" data-nav-section style="display:none"> <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 style="display:none"> <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 style="display:none"> <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 style="display:none"> <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 style="display:none"> <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 reverse 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> <!-- Filter Bar --> <div class="filter-bar" id="filter-bar"> <div class="filter-group"> <label class="filter-label" for="filter-date-from">From</label> <input class="filter-input" type="date" id="filter-date-from"> </div> <div class="filter-group"> <label class="filter-label" for="filter-date-to">To</label> <input class="filter-input" type="date" id="filter-date-to"> </div> <div class="filter-group"> <label class="filter-label" for="filter-agent">Agent</label> <select class="filter-select" id="filter-agent"> <option value="">All agents</option> </select> </div> <div class="filter-group"> <label class="filter-label" for="filter-outcome">Outcome</label> <select class="filter-select" id="filter-outcome"> <option value="">All outcomes</option> <option value="success">Success</option> <option value="partial">Partial</option> <option value="failed">Failed</option> </select> </div> <div class="filter-group"> <label class="filter-label" for="filter-convoy">Convoy</label> <select class="filter-select" id="filter-convoy"> <option value="">All convoys</option> </select> </div> <div class="filter-group"> <label class="filter-label" for="filter-pipeline">Pipeline</label> <select class="filter-select" id="filter-pipeline"> <option value="">All</option> </select> </div> <button class="dash-btn dash-btn--ghost filter-reset" id="filter-reset" type="button">Reset</button> </div> <!-- Convoy Status Section --> <section class="chart-card convoy-status" id="convoy-section" data-nav-section style="display:none"> <div class="chart-card__header"> <h2 class="chart-card__title">Convoy Status</h2> <p class="chart-card__desc" id="convoy-desc">Select a convoy to view details</p> </div> <div class="chart-card__body" id="convoy-body"></div> </section> <!-- Convoy Pipeline (Chaining) Section --> <section class="chart-card" id="convoy-pipeline-section" data-nav-section style="display:none"> <div class="chart-card__header"> <h2 class="chart-card__title">Convoy Pipeline</h2> <p class="chart-card__desc" id="convoy-pipeline-desc">Pipeline convoy chain progress</p> </div> <div class="chart-card__body" id="convoy-pipeline-body"></div> </section> <!-- KPI Row --> <section class="kpi-row" id="kpi-row" data-nav-section> <div class="kpi-card" id="kpi-sessions"> <span class="kpi-card__label">Total Sessions</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> <div class="kpi-card" id="kpi-success"> <span class="kpi-card__label">Success Rate</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> <div class="kpi-card" id="kpi-delegations"> <span class="kpi-card__label">Total Delegations</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> <div class="kpi-card" id="kpi-duration"> <span class="kpi-card__label">Avg Duration</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> <div class="kpi-card" id="kpi-retries"> <span class="kpi-card__label">Total Retries</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> <div class="kpi-card" id="kpi-lessons"> <span class="kpi-card__label">Lessons Added</span> <span class="kpi-card__value">&mdash;</span> <span class="kpi-card__sub"></span> </div> </section> <!-- Pipeline View (Steroids-inspired) --> <section class="chart-card" id="pipeline-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Task Pipeline</h2> <p class="chart-card__desc">Delegation flow across execution phases</p> </div> <div class="chart-card__body" id="pipeline-view"> <div class="loading-skeleton"></div> </div> </section> <!-- Charts Row 1 --> <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">Stacked by outcome</p> </div> <div class="chart-card__body" id="agent-chart"> <div class="loading-skeleton"></div> </div> </section> <section class="chart-card" id="tier-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Tier Distribution</h2> <p class="chart-card__desc">Delegation model tiers</p> </div> <div class="chart-card__body" id="tier-chart"> <div class="loading-skeleton"></div> </div> </section> </div> <!-- Charts Row: Delegation Insights --> <div class="charts-row" id="delegation-section" data-nav-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 class="loading-skeleton"></div> </div> </section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Delegation Outcomes</h2> <p class="chart-card__desc">Success rate by delegation</p> </div> <div class="chart-card__body" id="delegation-outcome-chart"> <div class="loading-skeleton"></div> </div> </section> </div> <!-- Charts Row 2 --> <div class="charts-row" id="timeline-section" data-nav-section> <section class="chart-card"> <div class="chart-card__header"> <h2 class="chart-card__title">Timeline</h2> <p class="chart-card__desc">Sessions and delegations over time</p> </div> <div class="chart-card__body" id="timeline-chart"> <div class="loading-skeleton"></div> </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">Sessions by model</p> </div> <div class="chart-card__body" id="model-chart"> <div class="loading-skeleton"></div> </div> </section> </div> <!-- Execution Log (Duvo-inspired) --> <section class="chart-card" id="execution-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Execution Log</h2> <p class="chart-card__desc">Recent agent activity, step by step</p> </div> <div class="chart-card__body" id="execution-log"> <div class="loading-skeleton"></div> </div> </section> <!-- Panel Results --> <section class="chart-card" id="panel-section" data-nav-section> <div class="chart-card__header"> <h2 class="chart-card__title">Panel Reviews</h2> <p class="chart-card__desc">Quality gate verdicts and fix items</p> </div> <div class="chart-card__body" id="panel-chart"> <div class="loading-skeleton"></div> </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> <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 class="loading-skeleton"></div> </div> </section> <!-- Sessions Table --> <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">Last 15 sessions by timestamp</p> </div> <div class="chart-card__body chart-card__body--table" id="sessions-table"> <div class="loading-skeleton"></div> </div> </section> </main> </div> </body></html> <script>(function(){const base = "/";
8
8
 
9
9
  // ── Data Loading ──────────────────────────────────────────
10
10
 
@@ -23,8 +23,21 @@ Export
23
23
  }
24
24
  }
25
25
 
26
+ async function loadJson(path) {
27
+ try {
28
+ const res = await fetch(path);
29
+ if (!res.ok) return [];
30
+ return await res.json();
31
+ } catch {
32
+ return [];
33
+ }
34
+ }
35
+
26
36
  // ── Helpers ───────────────────────────────────────────────
27
37
 
38
+ // ── Info Icon SVG ─────────────────────────────────────
39
+ const INFO_ICON = '<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"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>';
40
+
28
41
  const TIER_COLORS = {
29
42
  premium: '#f59e0b',
30
43
  standard: '#a78bfa',
@@ -1608,7 +1621,7 @@ Export
1608
1621
  el.innerHTML = cards.map(card =>
1609
1622
  '<div class="task-summary-card task-summary-card--' + card.mod + '">' +
1610
1623
  '<span class="task-summary-card__label">' + escapeHtml(card.label) +
1611
- ' <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: ' + escapeHtml(card.tooltip) + '" data-tooltip="' + escapeHtml(card.tooltip) + '">\u2139\uFE0F</span>' +
1624
+ ' <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: ' + escapeHtml(card.tooltip) + '" data-tooltip="' + escapeHtml(card.tooltip) + '">' + INFO_ICON + '</span>' +
1612
1625
  '</span>' +
1613
1626
  '<span class="task-summary-card__value">' + card.value + '</span>' +
1614
1627
  '</div>'
@@ -1751,7 +1764,7 @@ Export
1751
1764
  cardsEl.innerHTML = qCards.map(function(card) {
1752
1765
  return '<div class="task-summary-card task-summary-card--' + card.mod + '">' +
1753
1766
  '<span class="task-summary-card__label">' + escapeHtml(card.label) +
1754
- ' <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: ' + escapeHtml(card.tooltip) + '" data-tooltip="' + escapeHtml(card.tooltip) + '">\u2139\uFE0F</span>' +
1767
+ ' <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: ' + escapeHtml(card.tooltip) + '" data-tooltip="' + escapeHtml(card.tooltip) + '">' + INFO_ICON + '</span>' +
1755
1768
  '</span>' +
1756
1769
  '<span class="task-summary-card__value">' + card.value + '</span>' +
1757
1770
  '</div>';
@@ -1809,7 +1822,7 @@ Export
1809
1822
  if (dlqCardEl) {
1810
1823
  dlqCardEl.innerHTML =
1811
1824
  '<div class="task-summary-card task-summary-card--' + (dlqCount > 0 ? 'errors' : 'done') + '">' +
1812
- '<span class="task-summary-card__label">Retry Queue <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Tasks that failed too many times and need manual attention. Also known as Dead Letter Queue (DLQ)." data-tooltip="Tasks that failed too many times and need manual attention. Also known as Dead Letter Queue (DLQ).">\u2139\uFE0F</span></span>' +
1825
+ '<span class="task-summary-card__label">Retry Queue <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: Tasks that failed too many times and need manual attention. Also known as Dead Letter Queue (DLQ)." data-tooltip="Tasks that failed too many times and need manual attention. Also known as Dead Letter Queue (DLQ).">' + INFO_ICON + '</span></span>' +
1813
1826
  '<span class="task-summary-card__value">' + dlqCount + '</span>' +
1814
1827
  '</div>';
1815
1828
  }
@@ -1889,7 +1902,7 @@ Export
1889
1902
  cardsEl.innerHTML = driftCards.map(function(card) {
1890
1903
  return '<div class="task-summary-card task-summary-card--' + card.mod + '">' +
1891
1904
  '<span class="task-summary-card__label">' + escapeHtml(String(card.label)) +
1892
- ' <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: ' + escapeHtml(card.tooltip) + '" data-tooltip="' + escapeHtml(card.tooltip) + '">\u2139\uFE0F</span>' +
1905
+ ' <span class="tooltip-trigger" tabindex="0" role="button" aria-label="Info: ' + escapeHtml(card.tooltip) + '" data-tooltip="' + escapeHtml(card.tooltip) + '">' + INFO_ICON + '</span>' +
1893
1906
  '</span>' +
1894
1907
  '<span class="task-summary-card__value">' + card.value + '</span>' +
1895
1908
  '</div>';
@@ -1930,7 +1943,7 @@ Export
1930
1943
  cardsEl.innerHTML =
1931
1944
  '<div class="task-summary-card task-summary-card--done">' +
1932
1945
  '<span class="task-summary-card__label">Outputs Produced ' +
1933
- '<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.">\u2139\uFE0F</span>' +
1946
+ '<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>' +
1934
1947
  '</span>' +
1935
1948
  '<span class="task-summary-card__value">' + artifactCount + '</span>' +
1936
1949
  '</div>';
@@ -2097,6 +2110,7 @@ Export
2097
2110
  async function main() {
2098
2111
  const events = await loadNdjson(base + 'data/events.ndjson');
2099
2112
  const pipelines = await loadNdjson(base + 'data/pipelines.ndjson');
2113
+ const convoys = window.__DASHBOARD_DATA__?.convoyList ?? [];
2100
2114
 
2101
2115
  const sessions = events.filter((e) => e.type === 'session');
2102
2116
  const delegations = events.filter((e) => e.type === 'delegation');
@@ -2177,7 +2191,7 @@ Export
2177
2191
  if (refreshInterval) return;
2178
2192
  refreshInterval = setInterval(async () => {
2179
2193
  const freshEvents = await loadNdjson(base + 'data/events.ndjson');
2180
- const freshConvoys = await loadNdjson(base + 'data/convoys.ndjson');
2194
+ const freshConvoys = await loadJson(base + 'data/convoy-list.json');
2181
2195
  const freshPipelines = await loadNdjson(base + 'data/pipelines.ndjson');
2182
2196
  rawSessions = freshEvents.filter((e) => e.type === 'session');
2183
2197
  rawDelegations = freshEvents.filter((e) => e.type === 'delegation');
@@ -2233,17 +2247,26 @@ Export
2233
2247
 
2234
2248
  sections.forEach((s) => observer.observe(s));
2235
2249
 
2250
+ // Convoy-detail sections that start hidden and need a convoy selected first
2251
+ const CONVOY_DETAIL_SECTIONS = ['tasks-section', 'quality-section', 'reliability-section', 'drift-section', 'outputs-section', 'event-timeline-section'];
2252
+
2236
2253
  // Smooth scroll on click
2237
2254
  links.forEach((link) => {
2238
- link.addEventListener('click', (e) => {
2255
+ link.addEventListener('click', async (e) => {
2239
2256
  e.preventDefault();
2240
2257
  const sectionId = link.dataset.section;
2241
2258
 
2242
- // Auto-select latest convoy/pipeline if section is hidden
2243
- if (sectionId === 'convoy-section') {
2244
- const convoySelect = document.getElementById('filter-convoy');
2245
- if (convoySelect && !convoySelect.value && convoySelect.options.length > 1) {
2246
- convoySelect.value = convoySelect.options[1].value;
2259
+ if (CONVOY_DETAIL_SECTIONS.includes(sectionId)) {
2260
+ // Auto-select and load convoy detail if none selected yet
2261
+ const convoySelectEl = document.getElementById('convoy-select');
2262
+ if (convoySelectEl && !convoySelectEl.value && convoySelectEl.options.length > 1) {
2263
+ convoySelectEl.value = convoySelectEl.options[1].value;
2264
+ await loadConvoyDetail(convoySelectEl.value);
2265
+ }
2266
+ } else if (sectionId === 'convoy-section') {
2267
+ const convoyFilterEl = document.getElementById('filter-convoy');
2268
+ if (convoyFilterEl && !convoyFilterEl.value && convoyFilterEl.options.length > 1) {
2269
+ convoyFilterEl.value = convoyFilterEl.options[1].value;
2247
2270
  applyFilters();
2248
2271
  }
2249
2272
  } else if (sectionId === 'convoy-pipeline-section') {
@@ -2255,7 +2278,7 @@ Export
2255
2278
  }
2256
2279
 
2257
2280
  const target = document.getElementById(sectionId);
2258
- if (target && target.style.display !== 'none') {
2281
+ if (target) {
2259
2282
  target.scrollIntoView({ behavior: 'smooth', block: 'start' });
2260
2283
  }
2261
2284
  });
@@ -1,25 +1,25 @@
1
1
  {
2
- "hash": "5561dc89",
2
+ "hash": "3120eea2",
3
3
  "configHash": "30f8ea04",
4
- "lockfileHash": "e2a32132",
5
- "browserHash": "c5e077c4",
4
+ "lockfileHash": "29db8675",
5
+ "browserHash": "392b96fc",
6
6
  "optimized": {
7
7
  "astro > cssesc": {
8
8
  "src": "../../../../../node_modules/cssesc/cssesc.js",
9
9
  "file": "astro___cssesc.js",
10
- "fileHash": "a5407c2f",
10
+ "fileHash": "c3358443",
11
11
  "needsInterop": true
12
12
  },
13
13
  "astro > aria-query": {
14
14
  "src": "../../../../../node_modules/aria-query/lib/index.js",
15
15
  "file": "astro___aria-query.js",
16
- "fileHash": "3bbe7ca8",
16
+ "fileHash": "f6721631",
17
17
  "needsInterop": true
18
18
  },
19
19
  "astro > axobject-query": {
20
20
  "src": "../../../../../node_modules/axobject-query/lib/index.js",
21
21
  "file": "astro___axobject-query.js",
22
- "fileHash": "a3c7a104",
22
+ "fileHash": "337d00e2",
23
23
  "needsInterop": true
24
24
  }
25
25
  },
@@ -1,20 +1,65 @@
1
1
  [
2
2
  {
3
- "id": "demo-convoy-2",
4
- "name": "Demo Convoy Beta",
3
+ "id": "demo-deploy-ci",
4
+ "name": "CI/CD Pipeline Setup",
5
5
  "status": "running",
6
- "created_at": "2026-01-15T10:05:00.000Z",
6
+ "created_at": "2026-03-11T08:00:00.000Z",
7
7
  "finished_at": null,
8
8
  "total_tokens": null,
9
9
  "total_cost_usd": null
10
10
  },
11
11
  {
12
- "id": "demo-convoy-1",
13
- "name": "Demo Convoy Alpha",
12
+ "id": "demo-docs-update",
13
+ "name": "Documentation Refresh",
14
14
  "status": "done",
15
- "created_at": "2026-01-15T10:00:00.000Z",
16
- "finished_at": "2026-01-15T10:00:33.000Z",
17
- "total_tokens": 8432,
18
- "total_cost_usd": 0.84
15
+ "created_at": "2026-02-28T15:00:00.000Z",
16
+ "finished_at": "2026-02-28T15:22:00.000Z",
17
+ "total_tokens": 14800,
18
+ "total_cost_usd": 1.48
19
+ },
20
+ {
21
+ "id": "demo-data-pipeline",
22
+ "name": "Analytics ETL Pipeline",
23
+ "status": "done",
24
+ "created_at": "2026-02-22T13:00:00.000Z",
25
+ "finished_at": "2026-02-22T13:38:00.000Z",
26
+ "total_tokens": 28900,
27
+ "total_cost_usd": 2.89
28
+ },
29
+ {
30
+ "id": "demo-perf-opt",
31
+ "name": "Frontend Performance Boost",
32
+ "status": "done",
33
+ "created_at": "2026-02-17T10:00:00.000Z",
34
+ "finished_at": "2026-02-17T11:02:00.000Z",
35
+ "total_tokens": 37200,
36
+ "total_cost_usd": 3.72
37
+ },
38
+ {
39
+ "id": "demo-api-v2",
40
+ "name": "REST API v2 Migration",
41
+ "status": "gate_failed",
42
+ "created_at": "2026-02-12T16:00:00.000Z",
43
+ "finished_at": "2026-02-12T16:28:00.000Z",
44
+ "total_tokens": 24600,
45
+ "total_cost_usd": 2.46
46
+ },
47
+ {
48
+ "id": "demo-dashboard-ui",
49
+ "name": "Observability Dashboard UI",
50
+ "status": "done",
51
+ "created_at": "2026-02-07T14:00:00.000Z",
52
+ "finished_at": "2026-02-07T15:38:00.000Z",
53
+ "total_tokens": 78400,
54
+ "total_cost_usd": 7.84
55
+ },
56
+ {
57
+ "id": "demo-auth-revamp",
58
+ "name": "Auth System Revamp",
59
+ "status": "done",
60
+ "created_at": "2026-02-03T09:00:00.000Z",
61
+ "finished_at": "2026-02-03T09:47:00.000Z",
62
+ "total_tokens": 42850,
63
+ "total_cost_usd": 4.28
19
64
  }
20
65
  ]
@@ -0,0 +1,177 @@
1
+ {
2
+ "convoy": {
3
+ "id": "demo-api-v2",
4
+ "name": "REST API v2 Migration",
5
+ "status": "gate_failed",
6
+ "created_at": "2026-02-12T16:00:00.000Z",
7
+ "finished_at": "2026-02-12T16:28:00.000Z",
8
+ "branch": "feat/api-v2",
9
+ "total_tokens": 24600,
10
+ "total_cost_usd": 2.46
11
+ },
12
+ "taskSummary": {
13
+ "total": 3,
14
+ "done": 2,
15
+ "running": 0,
16
+ "failed": 0,
17
+ "review_blocked": 0,
18
+ "disputed": 0,
19
+ "reviewed": 1,
20
+ "panel_reviewed": 1,
21
+ "tasks_with_drift": 0,
22
+ "max_drift_score": null,
23
+ "drift_retried": 0
24
+ },
25
+ "quality": {
26
+ "reviewed_tasks": 1,
27
+ "review_blocked_tasks": 0,
28
+ "disputed_tasks": 0,
29
+ "panel_reviews": 1
30
+ },
31
+ "drift": {
32
+ "tasks_with_drift": 0,
33
+ "max_drift_score": null,
34
+ "drift_retried_tasks": 0
35
+ },
36
+ "dlq_count": 1,
37
+ "dlq_entries": [
38
+ {
39
+ "id": "dlq-api-1",
40
+ "task_id": "api-t3",
41
+ "agent": "Security Expert",
42
+ "failure_type": "gate_failed",
43
+ "attempts": 1,
44
+ "resolved": 0
45
+ }
46
+ ],
47
+ "artifact_count": 3,
48
+ "artifacts": [
49
+ {
50
+ "id": "artifact-demo-api-v2-docs-api-v2-contract-json",
51
+ "name": "docs/api-v2-contract.json",
52
+ "type": "json",
53
+ "task_id": "api-t1",
54
+ "created_at": "2026-03-12T22:26:01.388Z"
55
+ },
56
+ {
57
+ "id": "artifact-demo-api-v2-reports-security-gate-failure-md",
58
+ "name": "reports/security-gate-failure.md",
59
+ "type": "summary",
60
+ "task_id": "api-t3",
61
+ "created_at": "2026-03-12T22:26:01.388Z"
62
+ },
63
+ {
64
+ "id": "artifact-demo-api-v2-src-api-rate-limiter-ts",
65
+ "name": "src/api/rate-limiter.ts",
66
+ "type": "file",
67
+ "task_id": "api-t2",
68
+ "created_at": "2026-03-12T22:26:01.388Z"
69
+ }
70
+ ],
71
+ "has_more_events": false,
72
+ "events": [
73
+ {
74
+ "type": "task_gate_failed",
75
+ "task_id": "api-t3",
76
+ "data": {
77
+ "reason": "SQL injection risk"
78
+ },
79
+ "created_at": "2026-02-12T16:27:00.000Z"
80
+ },
81
+ {
82
+ "type": "task_started",
83
+ "task_id": "api-t3",
84
+ "data": null,
85
+ "created_at": "2026-02-12T16:24:00.000Z"
86
+ },
87
+ {
88
+ "type": "task_done",
89
+ "task_id": "api-t2",
90
+ "data": null,
91
+ "created_at": "2026-02-12T16:23:00.000Z"
92
+ },
93
+ {
94
+ "type": "task_started",
95
+ "task_id": "api-t2",
96
+ "data": null,
97
+ "created_at": "2026-02-12T16:12:00.000Z"
98
+ },
99
+ {
100
+ "type": "task_done",
101
+ "task_id": "api-t1",
102
+ "data": null,
103
+ "created_at": "2026-02-12T16:11:00.000Z"
104
+ },
105
+ {
106
+ "type": "task_started",
107
+ "task_id": "api-t1",
108
+ "data": null,
109
+ "created_at": "2026-02-12T16:00:05.000Z"
110
+ }
111
+ ],
112
+ "tasks": [
113
+ {
114
+ "id": "api-t1",
115
+ "phase": 1,
116
+ "agent": "API Designer",
117
+ "model": "claude-sonnet-4-6",
118
+ "status": "done",
119
+ "retries": 0,
120
+ "started_at": "2026-02-12T16:00:05.000Z",
121
+ "finished_at": "2026-02-12T16:11:00.000Z",
122
+ "total_tokens": 7200,
123
+ "cost_usd": 0.72,
124
+ "review_level": null,
125
+ "review_verdict": null,
126
+ "review_tokens": null,
127
+ "review_model": null,
128
+ "panel_attempts": 0,
129
+ "dispute_id": null,
130
+ "drift_score": null,
131
+ "drift_retried": 0,
132
+ "files": null
133
+ },
134
+ {
135
+ "id": "api-t2",
136
+ "phase": 2,
137
+ "agent": "Developer",
138
+ "model": "claude-sonnet-4-6",
139
+ "status": "done",
140
+ "retries": 2,
141
+ "started_at": "2026-02-12T16:12:00.000Z",
142
+ "finished_at": "2026-02-12T16:23:00.000Z",
143
+ "total_tokens": 11400,
144
+ "cost_usd": 1.14,
145
+ "review_level": null,
146
+ "review_verdict": null,
147
+ "review_tokens": null,
148
+ "review_model": null,
149
+ "panel_attempts": 0,
150
+ "dispute_id": null,
151
+ "drift_score": null,
152
+ "drift_retried": 0,
153
+ "files": null
154
+ },
155
+ {
156
+ "id": "api-t3",
157
+ "phase": 3,
158
+ "agent": "Security Expert",
159
+ "model": "claude-sonnet-4-6",
160
+ "status": "gate_failed",
161
+ "retries": 0,
162
+ "started_at": "2026-02-12T16:24:00.000Z",
163
+ "finished_at": "2026-02-12T16:27:00.000Z",
164
+ "total_tokens": 6000,
165
+ "cost_usd": 0.6,
166
+ "review_level": "deep",
167
+ "review_verdict": "block",
168
+ "review_tokens": 2100,
169
+ "review_model": "claude-sonnet-4-6",
170
+ "panel_attempts": 1,
171
+ "dispute_id": null,
172
+ "drift_score": null,
173
+ "drift_retried": 0,
174
+ "files": null
175
+ }
176
+ ]
177
+ }