feat(workflows): M6 — dashboard run-history + DAG view

A per-app "Workflows" tab: list definitions, browse run history, and inspect a
run's per-step progress with a static layered DAG.

- API: the run-detail endpoint now returns each step's `depends_on` (loaded
  from the definition via a new `get_workflow_by_id` reader) so the dashboard
  can draw the graph edges — the run's step rows don't carry them.
- dashboard `$lib/api`: a `workflows` namespace (list / runs / start / run) +
  the matching types.
- `apps/[slug]/workflows/+page.svelte`: workflow table → drill into runs →
  drill into a run. The run view renders a step table plus an SVG DAG (nodes =
  steps colored by status, laid out by longest-path level; edges = depends_on,
  arrowed) and a "Start run" button. Nested/parked steps show their child run.
- AppTabBar + the per-app layout gain the Workflows tab (admin-only).

Tests: `npm run check` clean (0 errors); two Playwright smoke tests
(navigation tabs — workflows loads cleanly + renders its empty state) pass
against the full stack.

With M6 the v1.2 Workflows track (M1 schema/validation → M2 durable
orchestrator → M3 when/templating → M4 nested → M5 SDK/API/CLI → M6 dashboard)
is COMPLETE.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-12 17:57:56 +02:00
parent 5a630e1d9f
commit bd9c3fa4f0
7 changed files with 576 additions and 5 deletions

View File

@@ -444,6 +444,57 @@ export interface CreateQueueTriggerInput {
retry_base_ms?: number;
}
// v1.2 Workflows — read/run types (admin workflows API).
export interface WorkflowSummary {
name: string;
enabled: boolean;
steps: number;
}
export type WorkflowRunStatus =
| 'pending'
| 'running'
| 'succeeded'
| 'failed'
| 'canceled';
export interface WorkflowRun {
id: string;
workflow_id?: string;
status: WorkflowRunStatus;
error?: string | null;
workflow_depth: number;
created_at: string;
input?: unknown;
output?: unknown;
started_at?: string | null;
finished_at?: string | null;
}
export type WorkflowStepStatus =
| 'pending'
| 'ready'
| 'running'
| 'succeeded'
| 'failed'
| 'skipped';
export interface WorkflowRunStep {
name: string;
status: WorkflowStepStatus;
attempt: number;
max_attempts: number;
error?: string | null;
child_run_id?: string | null;
depends_on: string[];
output?: unknown;
}
export interface WorkflowRunDetail {
run: WorkflowRun;
steps: WorkflowRunStep[];
}
// v1.1.9 — read-only queue inspection types (admin queues API).
export interface QueueSummary {
queue_name: string;
@@ -1214,6 +1265,26 @@ export const api = {
)
},
workflows: {
list: (idOrSlug: string) =>
adminRequest<WorkflowSummary[]>(
`/api/v1/admin/apps/${encodeURIComponent(idOrSlug)}/workflows`
),
runs: (idOrSlug: string, name: string) =>
adminRequest<WorkflowRun[]>(
`/api/v1/admin/apps/${encodeURIComponent(idOrSlug)}/workflows/${encodeURIComponent(name)}/runs`
),
start: (idOrSlug: string, name: string, input: unknown) =>
adminRequest<WorkflowRun>(
`/api/v1/admin/apps/${encodeURIComponent(idOrSlug)}/workflows/${encodeURIComponent(name)}/runs`,
{ method: 'POST', body: JSON.stringify({ input }) }
),
run: (idOrSlug: string, runId: string) =>
adminRequest<WorkflowRunDetail>(
`/api/v1/admin/apps/${encodeURIComponent(idOrSlug)}/workflow-runs/${encodeURIComponent(runId)}`
)
},
topics: {
list: (idOrSlug: string) =>
adminRequest<{ topics: Topic[] }>(