Workflows
The unit is the WORKFLOW, not the tool. — doc-14, BabyAI LoRAs doctrine
Per-tool SPC answers "is this tool stable in isolation." Per-workflow SPC answers "is the *job we use it for* stable" — Monthly close, AR collection run, quarterly tax filing. Those are the units regulators ask about and the units LoRAs train against. Accelerando builds the composition primitive so the dashboard speaks both grains.
The entity
Workflow fields:
name REQUIRED — human-readable label
status = "active" — active / completed / abandoned
started_at REQUIRED
completed_at — stamped on complete/abandon
template_id — back-link to WorkflowTemplate when started from one
notes
The header chip
When a workflow is active, a gold-rimmed chip with a pulsing dot appears in the header showing the name + short id. Click it to open the progress drawer:
- Started + completed + computed duration
- Expected tools (from the template, if any) — ● ok / ● errored / ○ not-yet for each, with a "5 of 8 fired" header
- Recent activity — the last 30 audit_log rows tagged with this composition_id
- Complete / Abandon buttons
When no workflow is active, clicking the chip (which is hidden) is a no-op; ⌘K's "Start workflow…" command opens the picker.
composition_id propagation
The chip writes to localStorage; the api() wrapper reads it and stamps every /tools/* request with the X-Workflow-Id header. The Worker reads the header in the dispatch path and threads it into the audit emitter as composition_id.
That one column is what powers everything downstream:
/audit-log/workflow-statsaggregates by it- The Activity tab's "By workflow" view joins it to Workflow names
- The Quality scorecard can correlate AI-source defects to specific workflow runs via composition_id
Templates
WorkflowTemplate lets the operator define reusable workflow shapes:
name — "Monthly close"
description
expected_tools — JSON array of tool names expected to fire during a run
Admin → Settings has a "Workflow templates" card to author them. Once defined:
- ⌘K → "Start workflow from template…" → pick → run auto-named "{template name} {YYYY-MM-DD}"
- Inline ▶ Start button on the template row in Settings
- The progress drawer scores expected vs fired so the operator sees the run completion at a glance
Workflow history report
The Reports view in Books has a "Workflow history" card. Per-template aggregation over the selected report window:
- Runs count + completed / abandoned / active counts
- Median + average duration
- Last Run column — ⚠ flagged when the most recent run is >50% above this template's median duration. That's the doc-14 SPC drift signal applied at the workflow grain.
Workflows without a template (started ad-hoc) bucket together as "(no template)" so they're still visible.
Reading the dashboard at two grains
Tool grain (Ops → Activity → Tool stats): "is trial_balance stable across all the times anyone called it" Workflow grain (Ops → Activity → By workflow): "is the *Monthly close run* of 2026-06 stable across the 23 tool calls it made"
Same dashboard pattern, both grains. When the BabyAI workflow-LoRA layer lands, the same numbers fall out — the LoRA's tool calls fire inside an active X-Workflow-Id and show up in the by-workflow stats automatically.
What's deferred
The expected_tools list is operator-authored today. A natural next step is to *learn* the expected set from successful past runs — each completed workflow contributes to a per-template canonical tool sequence, and deviations get flagged. That's the LoRA-as-tool-surface doctrine starting to bite.