Workflow

Added in version 0.31.0.

The [workflow] section declares a project’s process model — the ordered stages a requirement passes through, the traceability each stage must satisfy, and the lifecycle states a need may take. It is consumed by the ubc agent workflow engine, which evaluates the declared gates against your needs and reports where the process is incomplete.

Caution

The [workflow] section drives the ubc agent engine, which is an evolving feature. The keys below are stable enough to author against (they are validated by the schema and rejected on a typo), but new keys may be added as the engine grows.

See also

This page is the key-by-key reference for [workflow] and the sibling [agent.runner] table. For the concepts, a quick start, the ubc agent command, and the VS Code surfaces, see the Pharaoh Agentic Engineer guide — in particular its configuration walkthrough.

Unlike the other sections, [workflow] is read only by ubc agent — it does not affect ubc build or ubc check, and it is not part of a project’s reproducible build hash. It still inherits through extend and responds to -c overrides like every other section.

Every key is validated: an unknown key, or an invalid value for one of the closed-set fields (such as a stage route scope or a trace direction), is a hard error rather than a silently-ignored setting.

Minimal example

[workflow]
exempt_status = ["deprecated"]

[[workflow.stages]]
id = "requirements"
produces = "requirement"

[workflow.stages.route]
scope = "stream"
granularity = "per-root"
path = "specs/{stream}/requirements.rst"

[[workflow.stages.trace]]
up = "user_story"
link = "implements"

Top-level options

exempt_status

Type: array of string (default: [])

Statuses whose needs are dropped before coverage is computed. A need in one of these states is ignored by the process gates, so a deprecated or descoped item does not count as an open gap.

release_disallow_status

Type: array of string (default: [])

Statuses that are forbidden at release time. A need left in one of these states blocks a release check.

orphan_exempt_types

Type: array of string (default: [])

Need types excluded from the orphan check only. A type listed here may exist without an incoming trace without being reported as orphaned.

Lifecycle

The [workflow.lifecycle] sub-table declares the legal status values a need may take, and (optionally) the legal transitions between them.

[workflow.lifecycle]
states = ["open", "in_progress", "done"]
transitions = [
    { from = "open", to = "in_progress" },
    { from = "in_progress", to = "done" },
]
states

Type: array of string (default: [])

The declared lifecycle states. When non-empty, a need whose status is not one of these values is reported by the status_undeclared gate. An absent table, or an empty list, keeps that gate off.

transitions

Type: array of table or pair (default: [])

The legal state transitions, carried for config fidelity. Each entry may be written either as a table with from and to keys, or as a positional two-element [from, to] pair, so you can use whichever form reads better:

[workflow.lifecycle]
states = ["open", "done"]
transitions = [
    { from = "open", to = "done" },   # table form
    ["open", "done"],                 # positional form
]

Diagram gate

The [workflow.diagram] sub-table configures the diagram gate, which validates diagram blocks in authored needs.

[workflow.diagram]
enabled = true
engine = "mermaid"
enabled

Type: boolean (default: true when the table is present)

Whether the diagram gate is enabled. A present [workflow.diagram] table is opt-in, so enabled defaults to true; set enabled = false to keep the table parsed but the gate off.

engine

Type: string (default: "mermaid")

Which diagram engine the gate validates against. One of:

  • mermaid — validate Mermaid blocks.

  • plantuml — validate PlantUML blocks.

  • both — validate both engines.

format

Type: string (default: none)

The diagram syntax the agent should author any diagram as (for example mermaid or plantuml). This is independent of enabled and engine: it is an authoring hint, so a project can pin a diagram format for authoring even when the diagram gate itself is off.

Stages

Each [[workflow.stages]] entry is one step of the process, in declaration order. A stage produces a need type and declares the gates that step must satisfy.

id

Type: string (required)

The stage identifier, referenced by other stages’ depends_on.

produces

Type: string (default: none)

The need type this stage produces. A stage with no produces is a code stage — it authors no need and is gated solely by its require_code gate (which must then carry an explicit type).

depends_on

Type: array of string (default: [])

The ids of stages this stage depends on. A stage whose dependencies are unmet is reported as blocked.

author_skill

Type: string (default: none)

The name of the agent skill used to author this stage.

review_skill

Type: string (default: none)

The name of the agent skill used to review this stage.

authoring

Type: string (default: none)

A project-specific authoring directive for this stage (free text). It is injected into the agent’s authoring prompt, so process policy — for example “decompose into the full set of requirements” — lives in config rather than in the generic skill prose.

optional

Type: boolean (default: false)

Whether this stage is informational-only, never a required blocking step in the dependency chain. An optional stage is never forced as blocked or as the next step; it surfaces only when the agent catches it during the process.

Stage route

The [workflow.stages.route] sub-table declares where a stage’s needs are authored.

[workflow.stages.route]
scope = "stream"
granularity = "per-root"
path = "specs/{stream}/requirements.rst"
scope

Type: string (required)

The authoring scope. One of:

  • stream — authored under a per-stream subtree.

  • global — authored at one global location.

granularity

Type: string (required)

The authoring granularity. One of:

  • global — one authoring run for the whole graph.

  • per-need — one authoring run per produced need.

  • per-root — one authoring run per trace-chain root and its subtree.

  • single — a single authoring run.

path

Type: string (required)

The authoring path template. Supports the {stream} and {id} placeholders.

group_field

Type: string (default: none)

An optional grouping field.

Stage code gate

The [workflow.stages.require_code] sub-table declares a field-presence gate: every need of the gated type must carry at least min non-empty values in field.

[workflow.stages.require_code]
field = "code_url"
min = 1
type

Type: string (default: the stage’s produces)

The gated need type. For a need-producing stage it defaults to the produced type; for a code stage (a stage with no produces) it must be set explicitly.

field

Type: string (default: "code_url")

The need field the gate ranges over.

min

Type: integer (default: 1)

The minimum number of non-empty values required in field.

Note

The gated field (by default code_url) is populated by the codelinks need-id reference mechanism — an @need-ids: marker in source that back-attaches a URL onto the existing need it names. One-line need markers do not set this field; they materialise a new need carrying a trace link instead. To gate coverage against one-line-materialised impl / test needs — as the vmodel profile does — use a bidirectional trace edge on the produced type rather than require_code.

Stage trace edges

Each [[workflow.stages.trace]] entry declares a traceability edge: the produced type must trace up to up via link.

[[workflow.stages.trace]]
up = "user_story"
link = "implements"
direction = "both"
min = 1
up

Type: string (required)

The upstream need type the produced type traces up to.

link

Type: string (required)

The link name the trace edge follows.

direction

Type: string (default: "both")

The edge direction. One of:

  • both — bidirectional: the child must trace up via link and the parent must be covered down.

  • incoming — the parent type must carry the incoming link.

  • outgoing — the produced child must carry the outgoing link.

min

Type: integer (default: 1)

The minimum required link count.

Icons

The [workflow.icons] table maps a need type name to the icon it renders with in the workflow view. A type not listed here uses a default icon, chosen by matching its name against a built-in alias table — for example an impl or implementation type renders with the code icon (not a distinct one), and a config or configuration type with the settings icon.

[workflow.icons]
requirement = "req"
architecture = "arch"
verification = "test"

The value must be one of the following presentation-neutral icon names. The name is intentionally decoupled from your own type names, so you can — for example — render a software_requirement type with the req icon. The complete set, each shown with the icon it currently renders as:

Icon

Name

Depicts

feature

A user-facing feature, user story, or epic.

req

A requirement (system, software, or component level).

arch

An architecture or design element.

code

A source-code artefact.

test

A test or verification case.

spec

A specification document.

risk

A risk or FMEA entry.

decision

A decision record.

task

A task or work item.

fsr

A functional safety requirement.

hazard

A hazard or HARA entry.

safety_goal

A safety goal.

release

A release or milestone.

people

A person, team, or role.

interface

An interface, API, or port.

component

A component, module, or block.

settings

A settings or configuration element.

Note

The glyphs are the VS Code codicons the workflow view currently maps each name to. The names are the stable configuration contract; the concrete glyph behind a name may be refined between releases.

Complete example

[workflow]
exempt_status = ["deprecated"]
release_disallow_status = ["open"]
orphan_exempt_types = ["release"]

[workflow.lifecycle]
states = ["open", "in_progress", "done"]
transitions = [
    { from = "open", to = "in_progress" },
    { from = "in_progress", to = "done" },
]

[workflow.icons]
requirement = "req"
architecture = "arch"

[[workflow.stages]]
id = "requirements"
produces = "requirement"
author_skill = "author-requirement"

[workflow.stages.route]
scope = "stream"
granularity = "per-root"
path = "specs/{stream}/requirements.rst"

[[workflow.stages.trace]]
up = "user_story"
link = "implements"

[[workflow.stages]]
id = "architecture"
produces = "architecture"
depends_on = ["requirements"]

[workflow.stages.route]
scope = "stream"
granularity = "per-root"
path = "specs/{stream}/architecture.rst"

[[workflow.stages.trace]]
up = "requirement"
link = "implements"

The [agent.runner] table

ubc agent run executes a workflow stage through a headless AI process, configured by the [agent.runner] table — a sibling of [workflow] in ubproject.toml:

[agent.runner]
command = "claude -p --permission-mode acceptEdits --output-format stream-json --verbose"
model = "sonnet"
timeout_secs = 600
command

Type: string (default: "claude -p")

The runner invocation. The assembled prompt (recommended skill + context briefing + resolved route) is passed on the process’s stdin.

model

Type: string (optional)

A model to pin for the runner (for example sonnet for routine authoring).

timeout_secs

Type: integer (default: 600)

The wall-clock budget for one authoring pass, in seconds. A runner that does not exit within the budget is killed; 0 disables the bound.

Each key can be overridden per invocation with the UBC_AGENT_RUNNER, UBC_AGENT_RUNNER_MODEL, and UBC_AGENT_RUNNER_TIMEOUT environment variables, which take precedence over the table.

Note

[agent.runner] only affects the headless ubc agent run path. Driving the loop interactively from your editor agent (Copilot, Claude Code, Mistral Vibe, or another) uses that agent directly and ignores this table.