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
+19
View File
@@ -390,9 +390,11 @@ import type {
LaneRuntime,
ModelPricing,
NamedLock,
PipelineStats,
Session,
SessionDrillIn,
SessionStats,
SkillUsage,
Stats,
TranscriptListResult,
TranscriptResult,
@@ -1122,6 +1124,17 @@ export const api = {
*/
session: (id: string) =>
request<SessionDrillIn>(`/workflows/session/${encodeURIComponent(id)}`),
/**
* GET /api/workflows/skill-usage — how often each skill was invoked and how
* much each plugin is used (its skills plus its own MCP tools).
*
* @param status Optional lifecycle filter; "all" (or omitted) means no filter.
* @returns {@link SkillUsage} — per-skill rows and per-plugin totals.
*/
skillUsage: (status?: string) =>
request<SkillUsage>(
`/workflows/skill-usage${status && status !== "all" ? `?status=${status}` : ""}`
),
// Workflow-tool runs (issue #167) - fleets ingested from on-disk journals.
// These two endpoints cover fleets that emit no hooks: the server reads their
// run journals off disk (see server/lib/workflow-ingest.js) instead of the
@@ -1908,6 +1921,12 @@ export const api = {
request<{ pipelines: { id: string; name: string; nodes: { id: string }[] }[] }>(
"/lanes/pipelines"
),
/**
* GET /api/lanes/pipeline-stats — per-node pass counts aggregated over every
* lane and every archived feature.
* @returns `{ pipelines }` — see {@link PipelineStats}.
*/
pipelineStats: () => request<PipelineStats>("/lanes/pipeline-stats"),
/**
* GET /api/lanes/branches — a candidate source repo's local branches.
* @param repo Absolute path to an existing git repository.
+47
View File
@@ -2465,6 +2465,53 @@ export interface LaneCounts {
dead: number;
}
/**
* How often one pipeline node was actually passed, summed over every lane and
* every archived feature. `passed + failed === entered`; `withEvidence` is the
* subset of passes that left an artifact, so a claim never reads as a proof.
*/
export interface PipelineNodeStat {
id: string;
label: string;
icon: string;
gate: boolean;
entered: number;
passed: number;
withEvidence: number;
failed: number;
}
/** Per-node pass counts, grouped by pipeline template (busiest first). */
export interface PipelineStats {
pipelines: {
id: string;
name: string;
/** Lanes + archived features recorded against this template. */
runs: number;
nodes: PipelineNodeStat[];
}[];
}
/** Skill invocation counts and the per-plugin totals derived from them. */
export interface SkillUsage {
skills: {
/** Skill name as invoked, e.g. `ccam:ship-feature` or a bare `daily-report`. */
name: string;
/** Owning plugin, or null for a built-in/personal skill. */
plugin: string | null;
count: number;
sessions: number;
}[];
plugins: {
name: string;
/** Distinct skills of this plugin that were used at least once. */
skills: number;
skillCalls: number;
/** Calls to the plugin's own MCP tools. */
mcpCalls: number;
}[];
}
// ───── Status presentation lookup (agents) ─────
/**