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 your editor agent
(Copilot, Claude Code, Mistral Vibe, or another)
uses that agent directly and ignores this table.