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:
arrayofstring(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:
arrayofstring(default:[])Statuses that are forbidden at release time. A need left in one of these states blocks a release check.
- orphan_exempt_types
Type:
arrayofstring(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:
arrayofstring(default:[])The declared lifecycle states. When non-empty, a need whose status is not one of these values is reported by the
status_undeclaredgate. An absent table, or an empty list, keeps that gate off.- transitions
Type:
arrayoftableorpair(default:[])The legal state transitions, carried for config fidelity. Each entry may be written either as a table with
fromandtokeys, 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:truewhen the table is present)Whether the diagram gate is enabled. A present
[workflow.diagram]table is opt-in, soenableddefaults totrue; setenabled = falseto 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
mermaidorplantuml). This is independent ofenabledandengine: 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
producesis a code stage — it authors no need and is gated solely by itsrequire_codegate (which must then carry an explicittype).- depends_on
Type:
arrayofstring(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’sproduces)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 vialinkand the parent must be covered down.incoming— the parent type must carry the incominglink.outgoing— the produced child must carry the outgoinglink.
- 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 |
|---|---|---|
|
A user-facing feature, user story, or epic. |
|
|
A requirement (system, software, or component level). |
|
|
An architecture or design element. |
|
|
A source-code artefact. |
|
|
A test or verification case. |
|
|
A specification document. |
|
|
A risk or FMEA entry. |
|
|
A decision record. |
|
|
A task or work item. |
|
|
A functional safety requirement. |
|
|
A hazard or HARA entry. |
|
|
A safety goal. |
|
|
A release or milestone. |
|
|
A person, team, or role. |
|
|
An interface, API, or port. |
|
|
A component, module, or block. |
|
|
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
sonnetfor 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;
0disables 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.