Spec: Workflows
- Status: Draft
- Last amended: 2026-05-26 (ADR-0022 — tool intersection operates against root agent's tool surface)
- Constrained by: ADR-0006, ADR-0011, ADR-0012, ADR-0015, ADR-0019, ADR-0022
- Implements:
packages/dsl/,packages/harness/,packages/daemon/
Purpose
This spec defines the workflows system: operator-authored, parameterised agent recipes declared in the project DSL. Workflows provide a constrained, prompt-bound way for operators and guests to execute specific tasks with structured inputs, strictly limited tools, and a defined invocation lifecycle.
This document is normative for:
- The
workflows:block schema in the project DSL. - The input parameter types and validation rules.
- The two-stage file upload protocol for workflow inputs.
- The invocation lifecycle from selection to audit.
- The composition of system prompts and tool allowlists for workflow runs.
It is not normative for:
- Guest principal management (that's
guests.md). - The low-level agent execution details (that's
agent.md). - The PTY-based task runner (that's
task-runner.md).
Constraints (from ADRs)
| Constraint | Source |
|---|---|
| Workflows are declared as a named-object map in the YAML DSL | ADR-0006, ADR-0019 |
| Portable across hosts; paths use project-relative URI prefixes | ADR-0011, ADR-0015 |
Compiled into Mastra Agent (v1) or Workflow (v1.x) primitives |
ADR-0012, ADR-0019 |
| Supports federated configuration: local overrides and nullification | ADR-0015 |
| Operator-authored recipes; guests invoke but do not define | ADR-0019 |
| Tool allowlist must be a strict subset of the root agent's tool surface | ADR-0019, ADR-0022 |
WorkflowDefinition Schema
The workflows: block is a named-object map where each entry is a WorkflowDefinition.
| Field | Type | Required | Meaning |
|---|---|---|---|
description |
string | yes | Human-readable description. Max 280 chars. |
system_prompt |
path | yes | Project-relative path to the workflow's system prompt fragment. |
inputs |
object | yes | Named-object map of WorkflowInput schemas. |
tools.allow |
string[] | yes | Tool names the workflow is permitted to use. |
tools.deny |
string[] | no | Explicit denials to narrow allowlist patterns. |
confirm_required |
bool | no | Show confirmation step to guests. Default false. |
invokable_by |
enum[] | no | ["operator", "guest"]. Default ["operator"]. |
timeout_seconds |
int | no | Max wall-clock for invocation. Default 600. |
cage_overrides |
object | no | Reserved for v1.x cage tightening. |
Input Schema Reference
Each input parameter follows the WorkflowInput schema.
| Field | Type | Required | Meaning |
|---|---|---|---|
type |
enum | yes | string, integer, number, boolean, file, url. |
required |
bool | no | Default true. |
description |
string | no | Field label in the invocation form. |
max_length / min_length |
int | no | For string. |
min / max |
number | no | For integer / number. |
pattern |
string | no | Regex (re2) for string and url. |
accept |
string[] | no | MIME types for file. |
max_size_kb |
int | no | Size limit for file. |
enum |
string[] | no | Restricted set of allowed values. |
default |
any | no | Default value when not supplied. |
File Upload Protocol
Workflows with file inputs use a two-stage protocol:
- Upload: Client
POSTs file to/api/v1/projects/:id/workflows/:name/upload. - Staging: Daemon validates MIME/size and streams to a temporary staging area.
- Token: Daemon returns a JSON response with an
upload_token. - Invoke: Client includes the
upload_tokenin theinvokepayload. - Cleanup:
- Abandoned: Files deleted after a 1-hour TTL if no invocation occurs.
- Completion: Files deleted immediately after the workflow run completes.
Invocation Lifecycle
An invocation follows these eight steps:
- Select: Invoker (operator or guest) selects a workflow.
- Form: UI renders an input form from the workflow's
inputsschema. - Submit & Validate: Client submits inputs; daemon validates against schema.
- Create Workflow Run: Daemon creates a session record with
kind: "workflow". - Harness Compose: Harness composes instructions and intersects tool sets.
- Primary Runs: Primary agent executes the recipe; output streams to UI.
- Completion: Run ends as
succeededorfailed. - Audit: AuditProcessor logs the run with the invoker's identity.
Prompt Composition
The effective system prompt is composed by the harness:
[Project Primary System Prompt]
---
### Workflow: <name>
[Workflow System Prompt Fragment]
### Workflow inputs
<name>: <value>
...
The composition is fully visible in the run's debug view and recorded in audit logs.
Federated Config Composition
project.local.yaml follows ADR-0015 for workflows:
- Override: Merging fields (e.g., narrowing tools or changing
timeout_seconds). - Nullification: Setting a workflow to
nullto disable it on a specific host.
Mastra Integration
- Constrained Agent Path (v1): Compiled into a Mastra
Agentwith composed instructions and tool intersection. - Mastra Workflow (v1.x): Reserved for complex control flow (suspend/resume).
Run Record Schema
Stored as a session with kind: "workflow" and extended metadata:
| Field | Type | Meaning |
|---|---|---|
workflow_name |
string | Name of the invoked workflow. |
inputs |
object | Frozen input values used for the run. |
invoking_principal |
object | Type (operator |
Tool Intersection
- The effective tool set is
(Root Agent Tools ∩ Workflow Allowlist) - Workflow Denylist. - Per ADR-0022, tools are per-agent — there is no project-level
tools:block. Workflows compose against the root agent's resolved tool surface (built-in defaults → role-based defaults → root agent'stools:override → cage filter). The root agent is the agent atProjectDsl.primary. - Validation occurs at session-start; workflows cannot expand the root agent's tool set.
kaged.issue.*andkaged.workflow.*tools are available to workflows by default (they are root-agent defaults), but can be excluded viatools.denyif the workflow should not interact with issues or trigger other workflows.
Validation Timing
- DSL Parse: Schema validation (Zod).
- Project Load: Prompt-file existence check.
- Session Start: Tool intersection validation.
- Invocation: Input value validation against schema.
Audit Events
workflow.invokedworkflow.completedworkflow.failedworkflow.uploadworkflow.upload_expired
API Endpoints
GET /api/v1/projects/:id/workflows— List available workflows.POST /api/v1/projects/:id/workflows/:name/invoke— Invoke with{ inputs }.POST /api/v1/projects/:id/workflows/:name/upload— Upload file forfileinputs.GET /api/v1/projects/:id/workflows/:name/runs— List historical runs.GET /api/v1/projects/:id/workflows/:name/runs/:rid— Get run detail/logs.
Zod-style Type Definitions
const WorkflowInput = z.object({
type: z.enum(["string", "integer", "number", "boolean", "file", "url"]),
required: z.boolean().default(true),
description: z.string().optional(),
max_length: z.number().int().optional(),
min_length: z.number().int().optional(),
min: z.number().optional(),
max: z.number().optional(),
pattern: z.string().optional(),
accept: z.array(z.string()).optional(),
max_size_kb: z.number().int().optional(),
enum: z.array(z.string()).optional(),
default: z.any().optional(),
});
const WorkflowDefinition = z.object({
description: z.string().max(280),
system_prompt: z.string(),
inputs: z.record(WorkflowInput),
tools: z.object({
allow: z.array(z.string()),
deny: z.array(z.string()).optional(),
}),
confirm_required: z.boolean().default(false),
invokable_by: z.array(z.enum(["operator", "guest"])).default(["operator"]),
timeout_seconds: z.number().int().default(600),
cage_overrides: z.record(z.any()).optional(), // Reserved
});
const WorkflowRun = z.object({
kind: z.literal("workflow"),
workflow_name: z.string(),
inputs: z.record(z.any()),
invoking_principal: z.object({
type: z.enum(["operator", "guest"]),
id: z.string(),
}),
});
Failure Modes
- Input Validation Error: Daemon rejects invocation (422).
- Tool Intersection Failure: Logged as critical error; invocation blocked.
- Upload Timeout: Staged files purged after 1 hour.
- Agent Timeout: Run aborted after
timeout_seconds.
Testing Notes
- Verify Zod schema enforces all constraints (max_length, pattern, etc.).
- Test tool intersection with both glob and explicit tools.
- Verify file staging cleanup after successful and failed runs.
- Test federated merge (nullification and deep merge).
Open Questions
- Should workflows declare a
result_schemafor v1.x? - Mapping to
Mastra.Workflowfor branching logic in v1.x.
References
- ADR-0019 — Workflows DSL
- ADR-0022 — Recursive agents; tool intersection target is root agent
project-dsl.md— DSL Referenceagent-tooling.md— Tool resolution chain andkaged.workflow.*toolsagent.md— Agent Harnesshttp-api.md— API Surface
Amendments
2026-05-26 — ADR-0022: tool intersection operates against root agent's tool surface
ADR-0022 removes the project-level tools: block and moves tool configuration to each AgentSpec. This changes the workflow tool intersection logic:
- Intersection target changed. The effective tool set formula is now
(Root Agent Tools ∩ Workflow Allowlist) - Workflow Denylist, where "Root Agent Tools" is the resolved tool surface of the agent atProjectDsl.primary. Previously the intersection was against the project-leveltools:block, which no longer exists. - Root-agent defaults available. The root agent gets
kaged.issue.*andkaged.workflow.*by default (ADR-0022 rule 5). These are available to workflows unless explicitly denied. A workflow that should not interact with issues can add"kaged.issue.*"totools.deny. - Constraint table updated. The "Tool allowlist must be a strict subset" constraint now references ADR-0022 alongside ADR-0019.
- Validation timing unchanged. Tool intersection still occurs at session-start (step 3 of validation timing).