kind: WorkflowTemplate
What it is
AWorkflowTemplate defines the state machine an issue, run, or other tracked item moves through inside a workspace. It’s the equivalent of “status options on a board”: you declare an ordered list of stages, each tagged with a type (open, started, completed, or cancelled), and the workspace UI uses that as the column layout for kanban boards plus the legal transition graph for status changes.
WorkflowTemplates are workspace-scoped and idempotent on metadata.slug within a workspace. The workflow_templates DB table has no slug column, so on export the manifest synthesises a kebab-case slug from metadata.name to keep round-trips stable.
Built-in templates seeded by the server (sequential, parallel, dev-test-loop, pipeline) are owned by the server and explicitly excluded from export — re-applying an exported workspace will never overwrite them and never produce drift against them.
YAML schema
Field reference
Stage types
Every stage carries one of fourtype tags. The tag is what the orchestrator and UI key on for behaviour — not the stage name, which is purely human-facing.
Transitions are unrestricted by default: from any stage to any other stage. The state machine is structural (“which columns exist?”), not behavioural (“which arrows are legal?”).
Examples
Minimal example
open stage and one completed stage. No started and no cancelled are fine.
Realistic example with all common fields
ready row above intentionally illustrates a rejected shape — Validate refuses two open stages. Pick one of backlog/ready as the entry state and tag the other started.
Multi-template bundle
CLI reference
crewship apply path is the only one that resolves metadata.slug → server ID for you. The flat crewship workflow get/delete accept a slug for convenience and do the lookup themselves.
REST endpoint mapping
The
template_json column stores the entire stages array as a JSON string (the column is TEXT, not JSON). The handler does not re-parse user input — it passes the marshalled string straight through to the DB, so any future stage-shape extension is forward-compatible.
All paths are workspace-scoped via the JWT/workspace context. RBAC: OWNER, ADMIN, and MANAGER can create / update / delete; every authenticated role can read.
Validation rules
metadata.nameis required (server rejects emptynamewith HTTP 400).metadata.slugis required and must be unique within the workspace.spec.stagesmust be a non-empty array.- Each
spec.stages[].namemust be non-empty and unique within the template. - Each
spec.stages[].positionmust be unique within the template. - Each
spec.stages[].typemust be one ofopen,started,completed,cancelled. - Exactly one stage must have
type=open. - At least one stage must have
type=completed. spec.colorandspec.stages[].color, if set, must match^#[0-9A-Fa-f]{6}$. Three-digit shorthand is not accepted.
Apply behavior
ApplyUpsert (default)
- List the workspace’s templates via
GET /api/v1/workflow-templates. - Match by
metadata.name:- No match →
Action=Create,POST /api/v1/workflow-templateswith{name, description, template_json, icon, color}. - Match with differing fields →
Action=Update,PATCH /api/v1/workflow-templates/{id}carrying the same body. - Match with identical fields →
Action=Unchanged, no HTTP call issued.
- No match →
position, so reordering the stages array in the YAML file without changing positions is a no-op (Unchanged), not a drift.
ApplyStrict
Fails with a slug already exists error if any declared template already exists by name. Useful for new-workspace bootstrapping where overwriting a same-named template would be a bug.
ApplyReplace
For each declared template, emits Action=Delete followed by Action=Create. Use this only when you want a fresh row (e.g. you’ve renamed a stage and want the underlying record reset rather than mutated in place). Apply also deletes any templates in the workspace that the manifest no longer declares — but never the built-in templates, which are protected server-side.
Round-trip via export
crewship export workspace lists every non-builtin template and emits one kind: WorkflowTemplate document per row. The export decodes template_json back into the structured stages array so the output is directly re-applyable:
Engineering Standard → engineering-standard). If two templates in the same workspace share a name (rejected by the DB’s UNIQUE(workspace_id, name) index — so this should never happen in practice), their slugs would collide and crewship apply would reject the bundle.
Built-in templates (sequential, parallel, dev-test-loop, pipeline, plus any others the server seeds in the future) are filtered out. The intent is that re-applying an exported workspace bundle never produces drift against server-owned rows.
See also
- kind: Project — projects don’t reference workflow templates directly today, but a future
default_workflow_slugfield is planned. - kind: TriageRule —
actions.set_statusreferences a stage name; if you wire one up, make sure the stage exists in whichever template the matched issue lives under. - kind: SavedView — views can filter by stage type (
type=open/type=started) to render “active work” boards. - SPEC-2 section 8 — authoritative contract for this kind.