feat(workflows): pipeline pass rates and skill/plugin usage

Restores the five analytics sections that turned out to be worth keeping
(patterns, model delegation, error propagation, session complexity, compaction
impact) and reworks what the page opens on.

The lane panel no longer mirrors the Workspace's live lane strip. It now
answers the question the strip cannot: how often each pipeline node actually
passes. GET /api/lanes/pipeline-stats aggregates every stage record of every
lane AND every archived feature — a lane's own `stages` only holds its current
feature, so without the archive the history resets each time a lane starts new
work. Evidence-backed passes are counted apart from bare claims, the same line
the pipeline map draws between `done` and `passed-no-evidence`.

Beside it, GET /api/workflows/skill-usage reports which skills and plugins the
sessions actually reach for. Only PreToolUse rows are counted: every call also
emits a PostToolUse, and counting both would double every number. A plugin's
total includes calls to the MCP tools it ships, so one used purely through MCP
does not read as unused.

Still dropped from issue #6: the agent collaboration network and the subagent
effectiveness scorecards.
This commit is contained in:
2026-08-26 10:32:13 +07:00
parent 4a0f99583d
commit 348c645ef3
23 changed files with 3944 additions and 239 deletions
+92 -16
View File
@@ -1,11 +1,12 @@
/**
* @file Workflows.tsx
* @description Workflows page: this dashboard's own lane pipelines first, then the
* few Claude Code orchestration views that stay readable and interactive — headline
* stats, Workflow-tool runs, the spawning DAG, the tool flow, and the per-session
* drill-in. The deeper analytics (collaboration network, subagent effectiveness,
* patterns, model delegation, error propagation, concurrency, complexity,
* compaction) were dropped in issue #6 as too specialized to act on.
* @description Workflows page. Opens on this dashboard's OWN pipelines — per-node
* pass rates across every lane, and which skills/plugins actually get used — then
* the Claude Code orchestration analytics: headline stats, Workflow-tool runs, the
* spawning DAG, tool flow, patterns, model delegation, error propagation, session
* complexity, compaction impact and the per-session drill-in. Issue #6 dropped the
* agent collaboration network and the subagent effectiveness scorecards as too
* specialized to act on.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
/* =============================================================================
@@ -31,9 +32,15 @@
* - `../lib/types`
* - `../components/workflows/WorkflowStats`
* - `../components/workflows/LanePipelinePanel`
* - `../components/workflows/SkillUsagePanel`
* - `../components/workflows/WorkflowRunsPanel`
* - `../components/workflows/OrchestrationDAG`
* - `../components/workflows/ToolExecutionFlow`
* - `../components/workflows/WorkflowPatterns`
* - `../components/workflows/ModelDelegationFlow`
* - `../components/workflows/ErrorPropagationMap`
* - `../components/workflows/SessionComplexityScatter`
* - `../components/workflows/CompactionImpact`
* - `../components/workflows/SessionDrillIn`
*
* ## Public surface
@@ -76,9 +83,15 @@ import type { WorkflowData, WSMessage } from "../lib/types";
import { WorkflowStats } from "../components/workflows/WorkflowStats";
import { OrchestrationDAG } from "../components/workflows/OrchestrationDAG";
import { ToolExecutionFlow } from "../components/workflows/ToolExecutionFlow";
import { WorkflowPatterns } from "../components/workflows/WorkflowPatterns";
import { ModelDelegationFlow } from "../components/workflows/ModelDelegationFlow";
import { ErrorPropagationMap } from "../components/workflows/ErrorPropagationMap";
import { SessionComplexityScatter } from "../components/workflows/SessionComplexityScatter";
import { CompactionImpact } from "../components/workflows/CompactionImpact";
import { SessionDrillIn } from "../components/workflows/SessionDrillIn";
import { WorkflowRunsPanel } from "../components/workflows/WorkflowRunsPanel";
import { LanePipelinePanel } from "../components/workflows/LanePipelinePanel";
import { SkillUsagePanel } from "../components/workflows/SkillUsagePanel";
type StatusFilter = "all" | "active" | "completed";
@@ -198,10 +211,21 @@ export function Workflows() {
{/* Stats Row */}
<WorkflowStats stats={data.stats} />
{/* This dashboard's own pipelines - lane strip + the selected lane's map */}
<Section number={1} title={t("lanes.title")} subtitle={t("lanes.subtitle")} infoKey="lanes">
<LanePipelinePanel />
</Section>
{/* Section 1 + 2: this dashboard's own pipelines, and what drives them */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Section number={1} title={t("lanes.title")} subtitle={t("lanes.subtitle")} infoKey="lanes">
<LanePipelinePanel />
</Section>
<Section
number={2}
title={t("skillUsage.title")}
subtitle={t("skillUsage.subtitle")}
infoKey="skillUsage"
>
<SkillUsagePanel statusFilter={statusFilter} />
</Section>
</div>
{/* Workflow-tool runs (issue #167) - fleets ingested from on-disk journals */}
<div className="card p-4 space-y-3">
@@ -215,9 +239,9 @@ export function Workflows() {
<WorkflowRunsPanel statusFilter={statusFilter} />
</div>
{/* Section 2: Agent Orchestration DAG */}
{/* Section 3: Agent Orchestration DAG */}
<Section
number={2}
number={3}
title={t("orchestration.title")}
subtitle={t("orchestration.subtitle")}
infoKey="orchestration"
@@ -243,9 +267,9 @@ export function Workflows() {
)}
</Section>
{/* Section 3: Tool Execution Flow */}
{/* Section 4: Tool Execution Flow */}
<Section
number={3}
number={4}
title={t("toolFlow.title")}
subtitle={t("toolFlow.subtitle")}
infoKey="toolFlow"
@@ -253,9 +277,61 @@ export function Workflows() {
<ToolExecutionFlow data={data.toolFlow} filterAgentType={selectedNode} />
</Section>
{/* Section 4: Session Drill-In */}
{/* Section 5: Workflow Patterns */}
<Section
number={4}
number={5}
title={t("patterns.title")}
subtitle={t("patterns.subtitle")}
infoKey="patterns"
>
<WorkflowPatterns data={data.patterns} onPatternClick={() => {}} />
</Section>
{/* Section 6 + 7: Two Column */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Section
number={6}
title={t("modelDelegation.title")}
subtitle={t("modelDelegation.subtitle")}
infoKey="modelDelegation"
>
<ModelDelegationFlow data={data.modelDelegation} />
</Section>
<Section
number={7}
title={t("errorPropagation.title")}
subtitle={t("errorPropagation.subtitle")}
infoKey="errorPropagation"
>
<ErrorPropagationMap data={data.errorPropagation} />
</Section>
</div>
{/* Section 8 + 9: Two Column */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Section
number={8}
title={t("complexity.title")}
subtitle={t("complexity.subtitle")}
infoKey="complexity"
>
<SessionComplexityScatter data={data.complexity} onSessionClick={setSelectedSessionId} />
</Section>
<Section
number={9}
title={t("compaction.title")}
subtitle={t("compaction.subtitle")}
infoKey="compaction"
>
<CompactionImpact data={data.compaction} />
</Section>
</div>
{/* Section 10: Session Drill-In */}
<Section
number={10}
title={t("drillIn.title")}
subtitle={t("drillIn.subtitle")}
infoKey="drillIn"