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 Copilot or Claude Code uses that assistant directly and ignores this table.