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"
@@ -10265,75 +10265,147 @@ exports[`screen snapshots > Workflows 1`] = `
</div>
</div>
<div
class="flex flex-col h-full"
class="grid grid-cols-1 lg:grid-cols-2 gap-6"
>
<div
class="flex items-center justify-between gap-4 mb-3"
class="flex flex-col h-full"
>
<div
class="flex items-center gap-2.5 min-w-0"
class="flex items-center justify-between gap-4 mb-3"
>
<span
class="w-5 h-5 rounded-md bg-accent/15 text-accent text-[11px] font-bold flex items-center justify-center flex-shrink-0"
<div
class="flex items-center gap-2.5 min-w-0"
>
1
</span>
<h2
class="text-sm font-semibold text-fg-primary"
>
Lane Pipelines
</h2>
<button
aria-expanded="false"
aria-label="What this shows"
class="flex items-center justify-center rounded-full p-0.5 -m-0.5 text-fg-muted hover:text-fg-secondary transition-colors focus:outline-none focus:ring-1 focus:ring-accent/40"
type="button"
>
<svg
class="lucide lucide-info w-3.5 h-3.5"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
<span
class="w-5 h-5 rounded-md bg-accent/15 text-accent text-[11px] font-bold flex items-center justify-center flex-shrink-0"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M12 16v-4"
/>
<path
d="M12 8h.01"
/>
</svg>
</button>
1
</span>
<h2
class="text-sm font-semibold text-fg-primary"
>
Lane Pipeline Pass Rates
</h2>
<button
aria-expanded="false"
aria-label="What this shows"
class="flex items-center justify-center rounded-full p-0.5 -m-0.5 text-fg-muted hover:text-fg-secondary transition-colors focus:outline-none focus:ring-1 focus:ring-accent/40"
type="button"
>
<svg
class="lucide lucide-info w-3.5 h-3.5"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M12 16v-4"
/>
<path
d="M12 8h.01"
/>
</svg>
</button>
</div>
<span
class="hidden lg:block flex-shrink-0 max-w-[20rem] xl:max-w-sm truncate text-right text-[11px] text-fg-muted"
title="How often each stage of your own pipelines is actually passed"
>
How often each stage of your own pipelines is actually passed
</span>
</div>
<span
class="hidden lg:block flex-shrink-0 max-w-[20rem] xl:max-w-sm truncate text-right text-[11px] text-fg-muted"
title="Your own lanes and the stage each one has reached"
<div
class="card p-4 flex-1"
>
Your own lanes and the stage each one has reached
</span>
<p
class="text-sm text-fg-muted"
>
No lane has recorded a stage yet. Create one with
<code>
ccam lanes add --cwd $(pwd)
</code>
</p>
</div>
</div>
<div
class="card p-4 flex-1"
class="flex flex-col h-full"
>
<p
class="text-sm text-fg-muted"
<div
class="flex items-center justify-between gap-4 mb-3"
>
No lanes yet. Create one from a working directory:
<code>
ccam lanes add --cwd $(pwd)
</code>
</p>
<div
class="flex items-center gap-2.5 min-w-0"
>
<span
class="w-5 h-5 rounded-md bg-accent/15 text-accent text-[11px] font-bold flex items-center justify-center flex-shrink-0"
>
2
</span>
<h2
class="text-sm font-semibold text-fg-primary"
>
Skill & Plugin Usage
</h2>
<button
aria-expanded="false"
aria-label="What this shows"
class="flex items-center justify-center rounded-full p-0.5 -m-0.5 text-fg-muted hover:text-fg-secondary transition-colors focus:outline-none focus:ring-1 focus:ring-accent/40"
type="button"
>
<svg
class="lucide lucide-info w-3.5 h-3.5"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M12 16v-4"
/>
<path
d="M12 8h.01"
/>
</svg>
</button>
</div>
<span
class="hidden lg:block flex-shrink-0 max-w-[20rem] xl:max-w-sm truncate text-right text-[11px] text-fg-muted"
title="Which skills your sessions actually reach for"
>
Which skills your sessions actually reach for
</span>
</div>
<div
class="card p-4 flex-1"
>
<p
class="text-sm text-fg-muted"
>
No skill invocation recorded yet.
</p>
</div>
</div>
</div>
<div
@@ -10525,7 +10597,7 @@ exports[`screen snapshots > Workflows 1`] = `
<span
class="w-5 h-5 rounded-md bg-accent/15 text-accent text-[11px] font-bold flex items-center justify-center flex-shrink-0"
>
2
3
</span>
<h2
class="text-sm font-semibold text-fg-primary"
@@ -10641,7 +10713,7 @@ exports[`screen snapshots > Workflows 1`] = `
<span
class="w-5 h-5 rounded-md bg-accent/15 text-accent text-[11px] font-bold flex items-center justify-center flex-shrink-0"
>
3
4
</span>
<h2
class="text-sm font-semibold text-fg-primary"
@@ -10832,7 +10904,507 @@ exports[`screen snapshots > Workflows 1`] = `
<span
class="w-5 h-5 rounded-md bg-accent/15 text-accent text-[11px] font-bold flex items-center justify-center flex-shrink-0"
>
4
5
</span>
<h2
class="text-sm font-semibold text-fg-primary"
>
Detected Workflow Patterns
</h2>
<button
aria-expanded="false"
aria-label="What this shows"
class="flex items-center justify-center rounded-full p-0.5 -m-0.5 text-fg-muted hover:text-fg-secondary transition-colors focus:outline-none focus:ring-1 focus:ring-accent/40"
type="button"
>
<svg
class="lucide lucide-info w-3.5 h-3.5"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M12 16v-4"
/>
<path
d="M12 8h.01"
/>
</svg>
</button>
</div>
<span
class="hidden lg:block flex-shrink-0 max-w-[20rem] xl:max-w-sm truncate text-right text-[11px] text-fg-muted"
title="Common agent orchestration sequences"
>
Common agent orchestration sequences
</span>
</div>
<div
class="card p-4 flex-1"
>
<div
class="card p-5"
>
<h2
class="text-sm font-semibold text-fg-secondary uppercase tracking-wider mb-4"
>
Workflow Patterns
</h2>
<div
class="flex flex-col items-center justify-center py-12 text-center"
>
<div
class="w-10 h-10 rounded-xl bg-surface-4 flex items-center justify-center mb-3"
>
<svg
class="lucide lucide-zap w-5 h-5 text-fg-muted"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"
/>
</svg>
</div>
<p
class="text-sm font-medium text-fg-secondary"
>
No patterns detected yet
</p>
<p
class="text-xs text-fg-muted mt-1"
>
Patterns appear once multiple sessions share the same agent sequence.
</p>
</div>
</div>
</div>
</div>
<div
class="grid grid-cols-1 lg:grid-cols-2 gap-6"
>
<div
class="flex flex-col h-full"
>
<div
class="flex items-center justify-between gap-4 mb-3"
>
<div
class="flex items-center gap-2.5 min-w-0"
>
<span
class="w-5 h-5 rounded-md bg-accent/15 text-accent text-[11px] font-bold flex items-center justify-center flex-shrink-0"
>
6
</span>
<h2
class="text-sm font-semibold text-fg-primary"
>
Model Delegation Flow
</h2>
<button
aria-expanded="false"
aria-label="What this shows"
class="flex items-center justify-center rounded-full p-0.5 -m-0.5 text-fg-muted hover:text-fg-secondary transition-colors focus:outline-none focus:ring-1 focus:ring-accent/40"
type="button"
>
<svg
class="lucide lucide-info w-3.5 h-3.5"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M12 16v-4"
/>
<path
d="M12 8h.01"
/>
</svg>
</button>
</div>
<span
class="hidden lg:block flex-shrink-0 max-w-[20rem] xl:max-w-sm truncate text-right text-[11px] text-fg-muted"
title="How models route through agent hierarchies"
>
How models route through agent hierarchies
</span>
</div>
<div
class="card p-4 flex-1"
>
<div
class="flex flex-col items-center justify-center py-16 gap-3 text-fg-muted"
>
<svg
aria-hidden="true"
fill="none"
height="40"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
width="40"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M12 8v4M12 16h.01"
/>
</svg>
<span
class="text-sm"
>
No model delegation data available
</span>
</div>
</div>
</div>
<div
class="flex flex-col h-full"
>
<div
class="flex items-center justify-between gap-4 mb-3"
>
<div
class="flex items-center gap-2.5 min-w-0"
>
<span
class="w-5 h-5 rounded-md bg-accent/15 text-accent text-[11px] font-bold flex items-center justify-center flex-shrink-0"
>
7
</span>
<h2
class="text-sm font-semibold text-fg-primary"
>
Error Propagation Map
</h2>
<button
aria-expanded="false"
aria-label="What this shows"
class="flex items-center justify-center rounded-full p-0.5 -m-0.5 text-fg-muted hover:text-fg-secondary transition-colors focus:outline-none focus:ring-1 focus:ring-accent/40"
type="button"
>
<svg
class="lucide lucide-info w-3.5 h-3.5"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M12 16v-4"
/>
<path
d="M12 8h.01"
/>
</svg>
</button>
</div>
<span
class="hidden lg:block flex-shrink-0 max-w-[20rem] xl:max-w-sm truncate text-right text-[11px] text-fg-muted"
title="Where errors cluster in agent hierarchy depth"
>
Where errors cluster in agent hierarchy depth
</span>
</div>
<div
class="card p-4 flex-1"
>
<div
class="flex flex-col items-center justify-center py-16 gap-3"
>
<div
class="w-12 h-12 rounded-full bg-status-success/10 border border-status-success/20 flex items-center justify-center"
>
<svg
fill="none"
height="24"
stroke="#10b981"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
>
<path
d="M22 11.08V12a10 10 0 1 1-5.93-9.14"
/>
<polyline
points="22 4 12 14.01 9 11.01"
/>
</svg>
</div>
<span
class="text-sm text-status-success font-medium"
>
No errors recorded
</span>
<span
class="text-xs text-fg-muted"
>
All sessions completed successfully
</span>
</div>
</div>
</div>
</div>
<div
class="grid grid-cols-1 lg:grid-cols-2 gap-6"
>
<div
class="flex flex-col h-full"
>
<div
class="flex items-center justify-between gap-4 mb-3"
>
<div
class="flex items-center gap-2.5 min-w-0"
>
<span
class="w-5 h-5 rounded-md bg-accent/15 text-accent text-[11px] font-bold flex items-center justify-center flex-shrink-0"
>
8
</span>
<h2
class="text-sm font-semibold text-fg-primary"
>
Session Complexity Scatter
</h2>
<button
aria-expanded="false"
aria-label="What this shows"
class="flex items-center justify-center rounded-full p-0.5 -m-0.5 text-fg-muted hover:text-fg-secondary transition-colors focus:outline-none focus:ring-1 focus:ring-accent/40"
type="button"
>
<svg
class="lucide lucide-info w-3.5 h-3.5"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M12 16v-4"
/>
<path
d="M12 8h.01"
/>
</svg>
</button>
</div>
<span
class="hidden lg:block flex-shrink-0 max-w-[20rem] xl:max-w-sm truncate text-right text-[11px] text-fg-muted"
title="Duration vs agent count vs tokens - Bubble size = token usage"
>
Duration vs agent count vs tokens - Bubble size = token usage
</span>
</div>
<div
class="card p-4 flex-1"
>
<div
class="flex flex-col items-center justify-center py-16 text-center"
>
<div
class="w-10 h-10 rounded-xl bg-surface-4 flex items-center justify-center mb-3"
>
<svg
class="w-5 h-5 text-fg-muted"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
>
<circle
cx="7"
cy="12"
r="3"
/>
<circle
cx="17"
cy="8"
r="2"
/>
<circle
cx="14"
cy="17"
r="4"
/>
</svg>
</div>
<p
class="text-sm font-medium text-fg-secondary"
>
No session complexity data
</p>
<p
class="text-xs text-fg-muted mt-1"
>
Sessions will appear here once they complete with agent activity.
</p>
</div>
</div>
</div>
<div
class="flex flex-col h-full"
>
<div
class="flex items-center justify-between gap-4 mb-3"
>
<div
class="flex items-center gap-2.5 min-w-0"
>
<span
class="w-5 h-5 rounded-md bg-accent/15 text-accent text-[11px] font-bold flex items-center justify-center flex-shrink-0"
>
9
</span>
<h2
class="text-sm font-semibold text-fg-primary"
>
Compaction Impact Analysis
</h2>
<button
aria-expanded="false"
aria-label="What this shows"
class="flex items-center justify-center rounded-full p-0.5 -m-0.5 text-fg-muted hover:text-fg-secondary transition-colors focus:outline-none focus:ring-1 focus:ring-accent/40"
type="button"
>
<svg
class="lucide lucide-info w-3.5 h-3.5"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
/>
<path
d="M12 16v-4"
/>
<path
d="M12 8h.01"
/>
</svg>
</button>
</div>
<span
class="hidden lg:block flex-shrink-0 max-w-[20rem] xl:max-w-sm truncate text-right text-[11px] text-fg-muted"
title="How context compression is spread across your sessions"
>
How context compression is spread across your sessions
</span>
</div>
<div
class="card p-4 flex-1"
>
<div
class="flex flex-col items-center justify-center py-16 gap-3 text-fg-muted"
>
<svg
aria-hidden="true"
fill="none"
height="40"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
width="40"
>
<path
d="M9 17H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11a2 2 0 0 1 2 2v3"
/>
<path
d="M13 21l2-2 4 4"
/>
<path
d="M17 21v-6"
/>
<path
d="M21 17h-6"
/>
</svg>
<span
class="text-sm"
>
No compactions recorded
</span>
</div>
</div>
</div>
</div>
<div
class="flex flex-col h-full"
>
<div
class="flex items-center justify-between gap-4 mb-3"
>
<div
class="flex items-center gap-2.5 min-w-0"
>
<span
class="w-5 h-5 rounded-md bg-accent/15 text-accent text-[11px] font-bold flex items-center justify-center flex-shrink-0"
>
10
</span>
<h2
class="text-sm font-semibold text-fg-primary"
@@ -247,6 +247,7 @@ vi.mock("../../lib/api", async (importOriginal) => {
workflows: {
get: r(emptyWorkflow),
session: r({}),
skillUsage: r({ skills: [], plugins: [] }),
runs: r({
runs: [sampleWorkflowRun],
total: 1,
@@ -388,6 +389,7 @@ vi.mock("../../lib/api", async (importOriginal) => {
update: r({ lane: {} }),
stage: r({ lane: {} }),
pipelines: r({ pipelines: [] }),
pipelineStats: r({ pipelines: [] }),
// A realistic shape: `{}` would crash blockingReason on `blocked` if any
// screen ever opened the destructive modal.
preflight: r({