opencastle 0.29.0 → 0.30.1
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.js +1 -1
- package/dist/cli/convoy/engine.js.map +1 -1
- package/dist/cli/pipeline.d.ts +17 -0
- package/dist/cli/pipeline.d.ts.map +1 -1
- package/dist/cli/pipeline.js +241 -20
- package/dist/cli/pipeline.js.map +1 -1
- package/dist/cli/pipeline.test.d.ts +2 -0
- package/dist/cli/pipeline.test.d.ts.map +1 -0
- package/dist/cli/pipeline.test.js +178 -0
- package/dist/cli/pipeline.test.js.map +1 -0
- package/dist/cli/plan.d.ts +8 -0
- package/dist/cli/plan.d.ts.map +1 -1
- package/dist/cli/plan.js +77 -1
- package/dist/cli/plan.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/convoy/engine.ts +1 -1
- package/src/cli/pipeline.test.ts +191 -0
- package/src/cli/pipeline.ts +305 -22
- package/src/cli/plan.ts +83 -1
- package/src/dashboard/dist/index.html +398 -5
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/dashboard/src/pages/index.astro +490 -4
- package/src/orchestrator/agents/team-lead.agent.md +13 -0
- package/src/orchestrator/prompts/fix-prd.prompt.md +58 -0
- package/src/orchestrator/prompts/generate-convoy.prompt.md +23 -0
- package/src/orchestrator/prompts/generate-prd.prompt.md +37 -2
|
@@ -4,7 +4,7 @@ const convoyList = [{"id":"demo-deploy-ci","name":"CI/CD Pipeline Setup","status
|
|
|
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"> <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="#tasks-section" data-section="tasks-section" data-view="detail" aria-label="Tasks section">Tasks</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="#event-timeline-section" data-section="event-timeline-section" data-view="detail" aria-label="Event Log section">Event Log</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> </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> </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__title" id="detail-hero-title"></div> <span class="convoy-detail-hero__status" id="detail-hero-status"></span> <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> <!-- 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 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> </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="#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="#panel-section" data-section="panel-section" data-view="detail" aria-label="Panel Reviews section">Panel Reviews</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> </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> </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> </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__title" id="detail-hero-title"></div> <span class="convoy-detail-hero__status" id="detail-hero-status"></span> <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: Task flow across execution phases." data-tooltip="Task flow across execution phases."><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 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> <!-- 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> <!-- 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> <!-- 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> </div><!-- .view-convoy-detail --> </main> </div> </body></html> <script>(function(){const base = "/";
|
|
8
8
|
|
|
9
9
|
// ── Data Loading ──────────────────────────────────────────
|
|
10
10
|
|
|
@@ -314,7 +314,7 @@ Export
|
|
|
314
314
|
async function loadConvoyDetail(convoyId) {
|
|
315
315
|
if (!convoyId) {
|
|
316
316
|
renderConvoyDetailHeader(null);
|
|
317
|
-
['quality-section', 'reliability-section', 'drift-section', 'outputs-section', 'event-timeline-section'].forEach(function(id) {
|
|
317
|
+
['pipeline-section', 'agent-section', 'tier-section', 'model-section', 'quality-section', 'reliability-section', 'drift-section', 'outputs-section', 'execution-section', 'event-timeline-section', 'panel-section', 'reviews-section'].forEach(function(id) {
|
|
318
318
|
var el = document.getElementById(id);
|
|
319
319
|
if (el) el.style.display = 'none';
|
|
320
320
|
});
|
|
@@ -331,15 +331,24 @@ Export
|
|
|
331
331
|
renderPhaseBreakdown(detail.tasks || []);
|
|
332
332
|
const tasksSection = document.getElementById('tasks-section');
|
|
333
333
|
if (tasksSection) tasksSection.style.display = '';
|
|
334
|
+
renderDetailPipeline(detail.tasks || []);
|
|
335
|
+
renderDetailAgentChart(detail.tasks || []);
|
|
336
|
+
renderDetailOutcomeChart(detail.tasks || []);
|
|
337
|
+
renderDetailTierChart(detail.tasks || []);
|
|
338
|
+
renderDetailMechanismChart(detail.events || []);
|
|
339
|
+
renderDetailModelChart(detail.tasks || []);
|
|
334
340
|
renderQualitySection(detail);
|
|
335
341
|
renderReliabilitySection(detail);
|
|
336
342
|
renderDriftSection(detail);
|
|
337
343
|
renderOutputsSection(detail);
|
|
344
|
+
renderDetailExecutionLog(detail.tasks || []);
|
|
338
345
|
renderEventTimeline(detail);
|
|
346
|
+
renderDetailPanelChart(detail.tasks || []);
|
|
347
|
+
renderDetailReviewsTable(detail.tasks || []);
|
|
339
348
|
} catch (e) {
|
|
340
349
|
console.error('Failed to load convoy detail:', e);
|
|
341
350
|
renderConvoyDetailHeader(null);
|
|
342
|
-
['quality-section', 'reliability-section', 'drift-section', 'outputs-section', 'event-timeline-section'].forEach(function(id) {
|
|
351
|
+
['pipeline-section', 'agent-section', 'tier-section', 'model-section', 'quality-section', 'reliability-section', 'drift-section', 'outputs-section', 'execution-section', 'event-timeline-section', 'panel-section', 'reviews-section'].forEach(function(id) {
|
|
343
352
|
var el = document.getElementById(id);
|
|
344
353
|
if (el) el.style.display = 'none';
|
|
345
354
|
});
|
|
@@ -355,7 +364,7 @@ Export
|
|
|
355
364
|
if (nameEl) nameEl.textContent = 'No convoy selected';
|
|
356
365
|
if (statusEl) { statusEl.textContent = ''; statusEl.className = 'status-badge'; }
|
|
357
366
|
if (metaEl) metaEl.innerHTML = '';
|
|
358
|
-
['tasks-section', 'quality-section', 'reliability-section', 'drift-section', 'outputs-section', 'event-timeline-section'].forEach(function(id) {
|
|
367
|
+
['tasks-section', 'pipeline-section', 'agent-section', 'tier-section', 'model-section', 'quality-section', 'reliability-section', 'drift-section', 'outputs-section', 'execution-section', 'event-timeline-section', 'panel-section', 'reviews-section'].forEach(function(id) {
|
|
359
368
|
var el = document.getElementById(id);
|
|
360
369
|
if (el) el.style.display = 'none';
|
|
361
370
|
});
|
|
@@ -553,6 +562,390 @@ Export
|
|
|
553
562
|
el.innerHTML = html;
|
|
554
563
|
}
|
|
555
564
|
|
|
565
|
+
// ── Convoy Detail: Derive Tier from Model ────────────────
|
|
566
|
+
|
|
567
|
+
function deriveTier(model) {
|
|
568
|
+
if (!model) return 'unknown';
|
|
569
|
+
var m = model.toLowerCase();
|
|
570
|
+
if (m.includes('opus')) return 'premium';
|
|
571
|
+
if (m.includes('sonnet') || m.includes('pro')) return 'standard';
|
|
572
|
+
if (m.includes('haiku') || m.includes('flash') || m.includes('mini')) return 'economy';
|
|
573
|
+
return 'utility';
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// ── Convoy Detail: Pipeline View ─────────────────────────
|
|
577
|
+
|
|
578
|
+
function renderDetailPipeline(tasks) {
|
|
579
|
+
var el = document.getElementById('pipeline-view');
|
|
580
|
+
if (!el) return;
|
|
581
|
+
|
|
582
|
+
if (!tasks || tasks.length === 0) {
|
|
583
|
+
el.innerHTML = emptyStateHtml('pipeline', 'No pipeline activity yet', 'Tasks will flow through execution phases here.');
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
var phases = {};
|
|
588
|
+
tasks.forEach(function(t) {
|
|
589
|
+
var p = t.phase != null ? t.phase : 1;
|
|
590
|
+
if (!phases[p]) phases[p] = 0;
|
|
591
|
+
phases[p]++;
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
var stageConfig = [
|
|
595
|
+
{ label: 'Foundation', phase: 1, iconClass: 'pending', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2"/><line x1="8" y1="10" x2="16" y2="10"/><line x1="8" y1="14" x2="13" y2="14"/></svg>' },
|
|
596
|
+
{ label: 'Integration', phase: 2, iconClass: 'active', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>' },
|
|
597
|
+
{ label: 'Validation', phase: 3, iconClass: 'review', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>' },
|
|
598
|
+
{ label: 'QA Gate', phase: 4, iconClass: 'done', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>' },
|
|
599
|
+
];
|
|
600
|
+
|
|
601
|
+
el.innerHTML =
|
|
602
|
+
'<div class="pipeline">' +
|
|
603
|
+
stageConfig.map(function(stage, i) {
|
|
604
|
+
return (i > 0 ? '<div class="pipeline-arrow">\u2192</div>' : '') +
|
|
605
|
+
'<div class="pipeline-stage">' +
|
|
606
|
+
'<div class="pipeline-stage__icon pipeline-stage__icon--' + stage.iconClass + '">' + stage.icon + '</div>' +
|
|
607
|
+
'<span class="pipeline-stage__count">' + (phases[stage.phase] || 0) + '</span>' +
|
|
608
|
+
'<span class="pipeline-stage__label">' + stage.label + '</span>' +
|
|
609
|
+
'</div>';
|
|
610
|
+
}).join('') +
|
|
611
|
+
'</div>';
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// ── Convoy Detail: Agent Chart ───────────────────────────
|
|
615
|
+
|
|
616
|
+
function renderDetailAgentChart(tasks) {
|
|
617
|
+
var el = document.getElementById('agent-chart');
|
|
618
|
+
if (!el) return;
|
|
619
|
+
|
|
620
|
+
if (!tasks || tasks.length === 0) {
|
|
621
|
+
el.innerHTML = emptyStateHtml('agents', 'No agent data yet', 'A breakdown of tasks per agent will appear here.');
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
var agentMap = {};
|
|
626
|
+
tasks.forEach(function(t) {
|
|
627
|
+
var agent = t.agent || 'unknown';
|
|
628
|
+
if (!agentMap[agent]) agentMap[agent] = { done: 0, failed: 0, running: 0, other: 0, total: 0 };
|
|
629
|
+
if (t.status === 'done') agentMap[agent].done++;
|
|
630
|
+
else if (t.status === 'running') agentMap[agent].running++;
|
|
631
|
+
else if (['failed', 'gate-failed', 'timed-out', 'hook-failed'].includes(t.status)) agentMap[agent].failed++;
|
|
632
|
+
else agentMap[agent].other++;
|
|
633
|
+
agentMap[agent].total++;
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
var agents = Object.entries(agentMap).sort(function(a, b) { return b[1].total - a[1].total; });
|
|
637
|
+
var maxTotal = Math.max.apply(null, agents.map(function(a) { return a[1].total; }));
|
|
638
|
+
|
|
639
|
+
el.innerHTML = agents.map(function(entry) {
|
|
640
|
+
var name = entry[0];
|
|
641
|
+
var data = entry[1];
|
|
642
|
+
return '<div class="bar-row">' +
|
|
643
|
+
'<span class="bar-label">' + escapeHtml(name) + '</span>' +
|
|
644
|
+
'<div class="bar-track">' +
|
|
645
|
+
(data.done > 0 ? '<div class="bar-segment bar--success" style="width: ' + ((data.done / maxTotal) * 100).toFixed(1) + '%"></div>' : '') +
|
|
646
|
+
(data.running > 0 ? '<div class="bar-segment" style="width: ' + ((data.running / maxTotal) * 100).toFixed(1) + '%; background: #3b82f6"></div>' : '') +
|
|
647
|
+
(data.failed > 0 ? '<div class="bar-segment bar--failed" style="width: ' + ((data.failed / maxTotal) * 100).toFixed(1) + '%"></div>' : '') +
|
|
648
|
+
(data.other > 0 ? '<div class="bar-segment" style="width: ' + ((data.other / maxTotal) * 100).toFixed(1) + '%; background: #64748b"></div>' : '') +
|
|
649
|
+
'</div>' +
|
|
650
|
+
'<span class="bar-value">' + data.total + '</span>' +
|
|
651
|
+
'</div>';
|
|
652
|
+
}).join('');
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// ── Convoy Detail: Tier Donut Chart ──────────────────────
|
|
656
|
+
|
|
657
|
+
function renderDetailTierChart(tasks) {
|
|
658
|
+
var el = document.getElementById('tier-chart');
|
|
659
|
+
if (!el) return;
|
|
660
|
+
|
|
661
|
+
if (!tasks || tasks.length === 0) {
|
|
662
|
+
el.innerHTML = emptyStateHtml('tiers', 'No tier data yet', 'Model tier distribution will be shown as a donut chart.');
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
var tierCounts = {};
|
|
667
|
+
tasks.forEach(function(t) {
|
|
668
|
+
var tier = deriveTier(t.model);
|
|
669
|
+
tierCounts[tier] = (tierCounts[tier] || 0) + 1;
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
var order = ['premium', 'standard', 'utility', 'economy', 'unknown'];
|
|
673
|
+
var tiers = order.filter(function(t) { return tierCounts[t]; }).map(function(t) { return { name: t, count: tierCounts[t] }; });
|
|
674
|
+
var total = tasks.length;
|
|
675
|
+
var r = 70;
|
|
676
|
+
var circumference = 2 * Math.PI * r;
|
|
677
|
+
var cumOffset = 0;
|
|
678
|
+
|
|
679
|
+
var circles = tiers.map(function(t) {
|
|
680
|
+
var pct = t.count / total;
|
|
681
|
+
var dashLen = pct * circumference;
|
|
682
|
+
var linecap = tiers.length === 1 ? 'butt' : 'round';
|
|
683
|
+
var segment = '<circle cx="90" cy="90" r="' + r + '" fill="none" stroke="' + (TIER_COLORS[t.name] || '#64748b') + '" stroke-width="18" stroke-dasharray="' + dashLen.toFixed(2) + ' ' + (circumference - dashLen).toFixed(2) + '" stroke-dashoffset="' + (-cumOffset).toFixed(2) + '" transform="rotate(-90 90 90)" stroke-linecap="' + linecap + '"/>';
|
|
684
|
+
cumOffset += dashLen;
|
|
685
|
+
return segment;
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
var legend = tiers.map(function(t) {
|
|
689
|
+
return '<div class="legend-item"><span class="legend-dot" style="background: ' + (TIER_COLORS[t.name] || '#64748b') + '"></span><span class="legend-name">' + t.name + '</span><span class="legend-count">' + t.count + ' (' + Math.round((t.count / total) * 100) + '%)</span></div>';
|
|
690
|
+
}).join('');
|
|
691
|
+
|
|
692
|
+
el.innerHTML =
|
|
693
|
+
'<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>';
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// ── Convoy Detail: Mechanism Donut Chart ─────────────────
|
|
697
|
+
|
|
698
|
+
function renderDetailMechanismChart(events) {
|
|
699
|
+
var el = document.getElementById('mechanism-chart');
|
|
700
|
+
if (!el) return;
|
|
701
|
+
|
|
702
|
+
var MECH_COLORS = { 'sub-agent': '#3b82f6', 'background': '#a78bfa', 'unknown': '#64748b' };
|
|
703
|
+
var MECH_LABELS = { 'sub-agent': 'Sub-agent (inline)', 'background': 'Background (worktree)', 'unknown': 'Unknown' };
|
|
704
|
+
|
|
705
|
+
if (!events || events.length === 0) {
|
|
706
|
+
el.innerHTML = emptyStateHtml('mechanism', 'No delegation data yet', 'The split between sub-agent and background delegations will be shown here.');
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
var mechCounts = {};
|
|
711
|
+
events.forEach(function(e) {
|
|
712
|
+
if (e.type === 'task_assigned' || e.type === 'task_started') {
|
|
713
|
+
var mech = (e.data && e.data.mechanism) || 'unknown';
|
|
714
|
+
mechCounts[mech] = (mechCounts[mech] || 0) + 1;
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
var mechOrder = ['sub-agent', 'background', 'unknown'];
|
|
719
|
+
var mechs = mechOrder.filter(function(m) { return mechCounts[m]; }).map(function(m) { return { name: m, count: mechCounts[m] }; });
|
|
720
|
+
|
|
721
|
+
if (mechs.length === 0) {
|
|
722
|
+
el.innerHTML = emptyStateHtml('mechanism', 'No delegation data yet', 'The split between sub-agent and background delegations will be shown here.');
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
var total = mechs.reduce(function(s, m) { return s + m.count; }, 0);
|
|
727
|
+
var r = 70;
|
|
728
|
+
var circumference = 2 * Math.PI * r;
|
|
729
|
+
var cumOffset = 0;
|
|
730
|
+
|
|
731
|
+
var circles = mechs.map(function(m) {
|
|
732
|
+
var pct = m.count / total;
|
|
733
|
+
var dashLen = pct * circumference;
|
|
734
|
+
var linecap = mechs.length === 1 ? 'butt' : 'round';
|
|
735
|
+
var segment = '<circle cx="90" cy="90" r="' + r + '" fill="none" stroke="' + (MECH_COLORS[m.name] || '#64748b') + '" stroke-width="18" stroke-dasharray="' + dashLen.toFixed(2) + ' ' + (circumference - dashLen).toFixed(2) + '" stroke-dashoffset="' + (-cumOffset).toFixed(2) + '" transform="rotate(-90 90 90)" stroke-linecap="' + linecap + '"/>';
|
|
736
|
+
cumOffset += dashLen;
|
|
737
|
+
return segment;
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
var legend = mechs.map(function(m) {
|
|
741
|
+
return '<div class="legend-item"><span class="legend-dot" style="background: ' + (MECH_COLORS[m.name] || '#64748b') + '"></span><span class="legend-name">' + (MECH_LABELS[m.name] || m.name) + '</span><span class="legend-count">' + m.count + ' (' + Math.round((m.count / total) * 100) + '%)</span></div>';
|
|
742
|
+
}).join('');
|
|
743
|
+
|
|
744
|
+
el.innerHTML =
|
|
745
|
+
'<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>';
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// ── Convoy Detail: Delegation Outcome Chart ──────────────
|
|
749
|
+
|
|
750
|
+
function renderDetailOutcomeChart(tasks) {
|
|
751
|
+
var el = document.getElementById('delegation-outcome-chart');
|
|
752
|
+
if (!el) return;
|
|
753
|
+
|
|
754
|
+
if (!tasks || tasks.length === 0) {
|
|
755
|
+
el.innerHTML = emptyStateHtml('outcomes', 'No outcome data yet', 'Task outcome distribution will be shown here.');
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
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' };
|
|
760
|
+
|
|
761
|
+
var outcomeCounts = {};
|
|
762
|
+
tasks.forEach(function(t) {
|
|
763
|
+
var outcome = t.status || 'unknown';
|
|
764
|
+
outcomeCounts[outcome] = (outcomeCounts[outcome] || 0) + 1;
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
var outcomes = Object.entries(outcomeCounts).sort(function(a, b) { return b[1] - a[1]; });
|
|
768
|
+
var maxCount = Math.max.apply(null, outcomes.map(function(o) { return o[1]; }));
|
|
769
|
+
|
|
770
|
+
el.innerHTML = outcomes.map(function(entry) {
|
|
771
|
+
var name = entry[0];
|
|
772
|
+
var count = entry[1];
|
|
773
|
+
return '<div class="bar-row">' +
|
|
774
|
+
'<span class="bar-label">' + escapeHtml(name) + '</span>' +
|
|
775
|
+
'<div class="bar-track"><div class="bar-segment" style="width: ' + ((count / maxCount) * 100).toFixed(1) + '%; background: ' + (OUTCOME_COLORS[name] || '#64748b') + '"></div></div>' +
|
|
776
|
+
'<span class="bar-value">' + count + '</span>' +
|
|
777
|
+
'</div>';
|
|
778
|
+
}).join('');
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// ── Convoy Detail: Model Chart ───────────────────────────
|
|
782
|
+
|
|
783
|
+
function renderDetailModelChart(tasks) {
|
|
784
|
+
var el = document.getElementById('model-chart');
|
|
785
|
+
if (!el) return;
|
|
786
|
+
|
|
787
|
+
if (!tasks || tasks.length === 0) {
|
|
788
|
+
el.innerHTML = emptyStateHtml('models', 'No model data yet', 'Model utilization across tasks will be shown here.');
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
var modelCounts = {};
|
|
793
|
+
tasks.forEach(function(t) {
|
|
794
|
+
if (t.model) modelCounts[t.model] = (modelCounts[t.model] || 0) + 1;
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
var models = Object.entries(modelCounts).sort(function(a, b) { return b[1] - a[1]; });
|
|
798
|
+
|
|
799
|
+
if (models.length === 0) {
|
|
800
|
+
el.innerHTML = emptyStateHtml('models', 'No model data yet', 'Model utilization across tasks will be shown here.');
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
var maxCount = Math.max.apply(null, models.map(function(m) { return m[1]; }));
|
|
805
|
+
|
|
806
|
+
el.innerHTML = models.map(function(entry) {
|
|
807
|
+
var name = entry[0];
|
|
808
|
+
var count = entry[1];
|
|
809
|
+
return '<div class="bar-row">' +
|
|
810
|
+
'<span class="bar-label">' + escapeHtml(name) + '</span>' +
|
|
811
|
+
'<div class="bar-track"><div class="bar-segment" style="width: ' + ((count / maxCount) * 100).toFixed(1) + '%; background: ' + (MODEL_COLORS[name] || '#64748b') + '"></div></div>' +
|
|
812
|
+
'<span class="bar-value">' + count + '</span>' +
|
|
813
|
+
'</div>';
|
|
814
|
+
}).join('');
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// ── Convoy Detail: Execution Log ─────────────────────────
|
|
818
|
+
|
|
819
|
+
function renderDetailExecutionLog(tasks) {
|
|
820
|
+
var el = document.getElementById('execution-log');
|
|
821
|
+
if (!el) return;
|
|
822
|
+
|
|
823
|
+
if (!tasks || tasks.length === 0) {
|
|
824
|
+
el.innerHTML = emptyStateHtml('execLog', 'No execution history yet', 'A step-by-step trace of task activity will appear here.');
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
var sorted = tasks.slice().filter(function(t) { return t.started_at; }).sort(function(a, b) { return new Date(b.started_at) - new Date(a.started_at); }).slice(0, 10);
|
|
829
|
+
|
|
830
|
+
if (sorted.length === 0) {
|
|
831
|
+
el.innerHTML = emptyStateHtml('execLog', 'No execution history yet', 'A step-by-step trace of task activity will appear here.');
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
var statusToOutcome = function(s) {
|
|
836
|
+
if (s === 'done') return 'success';
|
|
837
|
+
if (['failed', 'gate-failed', 'timed-out', 'hook-failed'].includes(s)) return 'failed';
|
|
838
|
+
return 'partial';
|
|
839
|
+
};
|
|
840
|
+
|
|
841
|
+
el.innerHTML =
|
|
842
|
+
'<div class="exec-log">' +
|
|
843
|
+
sorted.map(function(t, i) {
|
|
844
|
+
var outcome = statusToOutcome(t.status);
|
|
845
|
+
var dur = formatDuration(t.started_at, t.finished_at);
|
|
846
|
+
var fileCount = t.files ? t.files.length : 0;
|
|
847
|
+
return '<div class="exec-step">' +
|
|
848
|
+
'<div class="exec-step__indicator">' +
|
|
849
|
+
'<div class="exec-step__dot exec-step__dot--' + outcome + '">' + (OUTCOME_ICONS[outcome] || '') + '</div>' +
|
|
850
|
+
(i < sorted.length - 1 ? '<div class="exec-step__line"></div>' : '') +
|
|
851
|
+
'</div>' +
|
|
852
|
+
'<div class="exec-step__content">' +
|
|
853
|
+
'<div class="exec-step__header">' +
|
|
854
|
+
'<span class="exec-step__agent">' + escapeHtml(t.agent || 'unknown') + '</span>' +
|
|
855
|
+
'<span class="exec-step__badge exec-step__badge--' + outcome + '">' + escapeHtml(t.status) + '</span>' +
|
|
856
|
+
'</div>' +
|
|
857
|
+
'<div class="exec-step__task">' + escapeHtml(t.id) + '</div>' +
|
|
858
|
+
'<div class="exec-step__meta">' +
|
|
859
|
+
'<span class="exec-step__meta-item">\uD83D\uDD52 ' + formatTime(t.started_at) + '</span>' +
|
|
860
|
+
(dur ? '<span class="exec-step__meta-item">\u23F1 ' + dur + '</span>' : '') +
|
|
861
|
+
(fileCount > 0 ? '<span class="exec-step__meta-item">\uD83D\uDCC1 ' + fileCount + ' files</span>' : '') +
|
|
862
|
+
(t.model ? '<span class="exec-step__meta-item">\uD83E\uDD16 ' + escapeHtml(t.model) + '</span>' : '') +
|
|
863
|
+
(t.retries > 0 ? '<span class="exec-step__meta-item">\uD83D\uDD04 ' + t.retries + ' retries</span>' : '') +
|
|
864
|
+
(t.total_tokens != null ? '<span class="exec-step__meta-item">\uD83D\uDD24 ' + formatTokens(t.total_tokens) + '</span>' : '') +
|
|
865
|
+
'</div>' +
|
|
866
|
+
'</div>' +
|
|
867
|
+
'</div>';
|
|
868
|
+
}).join('') +
|
|
869
|
+
'</div>';
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
// ── Convoy Detail: Panel Reviews ─────────────────────────
|
|
873
|
+
|
|
874
|
+
function renderDetailPanelChart(tasks) {
|
|
875
|
+
var el = document.getElementById('panel-chart');
|
|
876
|
+
if (!el) return;
|
|
877
|
+
|
|
878
|
+
var panelTasks = (tasks || []).filter(function(t) { return t.panel_attempts > 0; });
|
|
879
|
+
|
|
880
|
+
if (panelTasks.length === 0) {
|
|
881
|
+
el.innerHTML = emptyStateHtml('panels', 'No panel reviews yet', 'Quality gate verdicts from majority-vote panels will be shown here.');
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
el.innerHTML =
|
|
886
|
+
'<div class="panel-grid">' +
|
|
887
|
+
panelTasks.map(function(t) {
|
|
888
|
+
var verdictUpper = t.review_verdict ? t.review_verdict.toUpperCase() : 'PENDING';
|
|
889
|
+
var verdictClass = verdictUpper === 'PASS' ? 'PASS' : 'BLOCK';
|
|
890
|
+
return '<div class="panel-item">' +
|
|
891
|
+
'<div class="panel-item__header">' +
|
|
892
|
+
'<span class="panel-item__key">' + escapeHtml(t.id) + '</span>' +
|
|
893
|
+
'<span class="panel-item__verdict panel-item__verdict--' + verdictClass + '">' + verdictUpper + '</span>' +
|
|
894
|
+
'</div>' +
|
|
895
|
+
'<div class="panel-item__votes">' +
|
|
896
|
+
(verdictUpper === 'PASS' ? '<div class="panel-item__vote panel-item__vote--pass">\u2713</div>' : '<div class="panel-item__vote panel-item__vote--block">\u2717</div>') +
|
|
897
|
+
'</div>' +
|
|
898
|
+
'<div class="panel-item__meta">' +
|
|
899
|
+
'<span class="panel-item__meta-item">\uD83E\uDD16 ' + escapeHtml(t.review_model || 'unknown') + '</span>' +
|
|
900
|
+
(t.panel_attempts > 1 ? '<span class="panel-item__meta-item">\uD83D\uDD04 ' + t.panel_attempts + ' attempts</span>' : '') +
|
|
901
|
+
(t.review_tokens != null ? '<span class="panel-item__meta-item">\uD83D\uDD24 ' + formatTokens(t.review_tokens) + '</span>' : '') +
|
|
902
|
+
'</div>' +
|
|
903
|
+
'</div>';
|
|
904
|
+
}).join('') +
|
|
905
|
+
'</div>';
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// ── Convoy Detail: Fast Reviews ──────────────────────────
|
|
909
|
+
|
|
910
|
+
function renderDetailReviewsTable(tasks) {
|
|
911
|
+
var el = document.getElementById('reviews-table');
|
|
912
|
+
if (!el) return;
|
|
913
|
+
|
|
914
|
+
var reviewedTasks = (tasks || []).filter(function(t) { return t.review_level != null; });
|
|
915
|
+
|
|
916
|
+
if (reviewedTasks.length === 0) {
|
|
917
|
+
el.innerHTML = emptyStateHtml('panels', 'No fast reviews yet', 'Single-reviewer quality gate results will be listed here.');
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
el.innerHTML =
|
|
922
|
+
'<table class="sessions-table">' +
|
|
923
|
+
'<thead><tr>' +
|
|
924
|
+
'<th scope="col">Task ID</th>' +
|
|
925
|
+
'<th scope="col">Agent</th>' +
|
|
926
|
+
'<th scope="col">Review Level</th>' +
|
|
927
|
+
'<th scope="col">Verdict</th>' +
|
|
928
|
+
'<th scope="col">Model</th>' +
|
|
929
|
+
'<th scope="col">Tokens</th>' +
|
|
930
|
+
'</tr></thead>' +
|
|
931
|
+
'<tbody>' +
|
|
932
|
+
reviewedTasks.map(function(t) {
|
|
933
|
+
var verdictUpper = t.review_verdict ? t.review_verdict.toUpperCase() : '';
|
|
934
|
+
var verdictBadge = t.review_verdict
|
|
935
|
+
? '<span class="status-badge status-badge--' + (verdictUpper === 'PASS' ? 'done' : 'failed') + '">' + escapeHtml(t.review_verdict) + '</span>'
|
|
936
|
+
: '<span style="opacity:0.4">\u2014</span>';
|
|
937
|
+
return '<tr>' +
|
|
938
|
+
'<td class="td-task">' + escapeHtml(t.id || '\u2014') + '</td>' +
|
|
939
|
+
'<td class="td-agent">' + escapeHtml(t.agent || '\u2014') + '</td>' +
|
|
940
|
+
'<td>' + escapeHtml(t.review_level || '\u2014') + '</td>' +
|
|
941
|
+
'<td>' + verdictBadge + '</td>' +
|
|
942
|
+
'<td>' + escapeHtml(t.review_model || '\u2014') + '</td>' +
|
|
943
|
+
'<td class="td-num">' + (t.review_tokens != null ? formatTokens(t.review_tokens) : '\u2014') + '</td>' +
|
|
944
|
+
'</tr>';
|
|
945
|
+
}).join('') +
|
|
946
|
+
'</tbody></table>';
|
|
947
|
+
}
|
|
948
|
+
|
|
556
949
|
// ── Quality Section ──────────────────────────────────────
|
|
557
950
|
|
|
558
951
|
function renderQualitySection(detail) {
|
|
@@ -1002,7 +1395,7 @@ Export
|
|
|
1002
1395
|
|
|
1003
1396
|
sections.forEach((s) => observer.observe(s));
|
|
1004
1397
|
|
|
1005
|
-
const CONVOY_DETAIL_SECTIONS = ["tasks-section", "quality-section", "reliability-section", "drift-section", "outputs-section", "event-timeline-section"];
|
|
1398
|
+
const CONVOY_DETAIL_SECTIONS = ["tasks-section", "pipeline-section", "agent-section", "tier-section", "model-section", "quality-section", "reliability-section", "drift-section", "outputs-section", "execution-section", "event-timeline-section", "panel-section", "reviews-section"];
|
|
1006
1399
|
|
|
1007
1400
|
links.forEach((link) => {
|
|
1008
1401
|
link.addEventListener("click", async (e) => {
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
{
|
|
2
|
-
"hash": "
|
|
2
|
+
"hash": "019f3d08",
|
|
3
3
|
"configHash": "30f8ea04",
|
|
4
|
-
"lockfileHash": "
|
|
5
|
-
"browserHash": "
|
|
4
|
+
"lockfileHash": "d116e77a",
|
|
5
|
+
"browserHash": "68231191",
|
|
6
6
|
"optimized": {
|
|
7
7
|
"astro > cssesc": {
|
|
8
8
|
"src": "../../../../../node_modules/cssesc/cssesc.js",
|
|
9
9
|
"file": "astro___cssesc.js",
|
|
10
|
-
"fileHash": "
|
|
10
|
+
"fileHash": "f4a93516",
|
|
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": "
|
|
16
|
+
"fileHash": "3b18a33f",
|
|
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": "
|
|
22
|
+
"fileHash": "dc8d5b49",
|
|
23
23
|
"needsInterop": true
|
|
24
24
|
}
|
|
25
25
|
},
|