Skip to main content

Workflow Schema Reference

Builder-grade reference for authoring your own workflow: one table per YAML file — every field with type, default, and meaning — plus the wiring map that shows how the files reference each other and with what cardinality. Matches the engine's Pydantic models field-for-field; the models are the final authority.

Conventions: ... = required (missing file/value fails) · = value = default · list[X] = YAML list · dict[k, X] = YAML mapping. Kind IDs and assistant IDs are DICT KEYS (no id field inside). Enums reject anything unstated. Plain-words companion: Modular Workflows.

The builder's wiring map

How the files interact. Every arrow below is a reference the loader enforces — a dangling ID fails the load and names the missing reference. Cardinality on each edge: 1 = exactly one · 1..* = one or more · 0..* = zero or more · 0..1 = zero or one.

Read it as a builder:

  • stages.yaml is the hub. It is the only file that fans out to modes, skills, node types, appendages, kinds, and intents — get stages right and most resolution errors disappear.
  • node_types.yaml is the graph. Types reference each other (parent / child / depends_on) and must stay acyclic; their transitions pull in gates; their rules: pull in governance.
  • gates reach back into skills (score_skill), and governance reaches out through connections. Those are the only cross-file chains besides stages.
  • A session binds it all at runtime: one stage, one mode, one or more skills, one intent, one node instance, at most one assistant — checked against checklists and proven by tools.

File index (22 YAML files + governance)

#FileRequired?Role
1workflow.yamlrequiredmeta + defaults
2modes.yamlrequired (≥1)rules per station
3intents.yamldefault []goals of a sitting
4node_types.yamlrequired (≥1)graph model + lifecycles
5stages.yamlrequired (≥1)stations
6skills.yamlrequired (≥1)trades
7gates.yamldefault []transition checks
8guardrails.yamldefaultedper-mode limits
9structure.yamldefaultedfile-routing kinds
10tools.yamldefault []appendage: commands
11mcps.yamldefault []appendage: MCP servers
12apis.yamldefault []appendage: HTTP APIs
13clis.yamldefault []appendage: CLIs
14sdks.yamldefault []appendage: SDKs
15hooks.yamldefaulted7 lifecycle hooks
16traceability.yamldefaultedTTL, snapshots, metrics
17connections.yamldefaultedpack transports
18assistants.yamldefault {}declared workers
19checklists.yamllenientscoring rubrics
20constraints.yamldefault nullsize limits
21verification.yamldefault []proof tools
22git.yamldefaultedbranch policy (.vyasa/git.yaml)
+governance.yamlscaffolded per projectyour standing laws (user-owned)

Rule of thumb: the five required files make a workflow LOAD; gates, governance, and verification make it GOVERN. Missing defaulted files fall through to safe defaults.

1. workflow.yaml — meta + defaults

FieldTypeDefaultMeaning
idstr...unique workflow identifier
labelstr...human-readable name
descriptionstr""what this workflow does
enabledbooltruefalse blocks EVERY session start
versionstr"1.0"semantic version
tierlite|standard|fullstandarddetermines which features are active
structure_packstr?nulldefault kind pack
default_flowsimple|scoringsimplesession flow unless --flow overrides
work_stylestr"conservative"suggestion only — sessions still require an explicit style
work_stylesdict[str, str]{}behavioral prose per style ID
context_budget_tokensint ≥1?nullagent-context cap; null = unlimited
session_modecontent|worktreecontentwhere output lands (staging mirror vs detached worktree)

Connects: root — nothing outbound; everything reads its defaults.

2. modes.yaml — rules per station

FieldTypeDefaultMeaning
idstr...unique mode identifier
labelstr...human-readable name
descriptionstr""what this mode does
default_ttl_hoursint 1–242session expiry → lock expiry
is_advisoryboolfalseprompt-level read-only
read_onlyboolfalsehook/guard-ENFORCED read-only (strictly stronger)
token_budgetint ≥1000100000per-session token cap

Connects: referenced by stages (mode: — exactly 1); selects guardrail patterns + scope generation.

3. intents.yaml — goals of a sitting

FieldTypeDefaultMeaning
idstr...unique intent identifier
labelstr...human-readable name
descriptionstr""what this intent achieves
approachstr""how — method / strategy

Connects: sessions declare 1 intent (--intent, validated); stages suggest 0..1 (default_intent).

4. node_types.yaml — the graph model

FieldTypeDefaultMeaning
idstr...unique type identifier
labelstr...human-readable name
descriptionstr""what this type represents
enabledbooltruefalse = instances cannot be created
id_patternregex str".*"instance-ID gate (^US-\d+$ style)
headersstr[][]required markdown sections (default level 2)
header_levelsdict[str, 1–6]{}per-header overrides; keys MUST be in headers
versionableboolfalsearchive old versions
relationsparent? / child[] / depends_on[]{}type IDs; must stay acyclic (DAG)
lifecyclestatuses[] ≥1 + transitions[]...statuses + moves
sub_nodesenabled (false) / decomposition_strategy[] / max_sessions_per_node (d.5) / max_tokens_per_session (d.50000) / auto_generatedisableddecomposition
templatestr""starting markdown
rulesstr[][]governance rule IDs for this type ONLY; empty = none; unknown ID fails closed
output_schemaOutputFieldConfig[][]id + type (text|number|bool|list|object, d.text) + required (d.false) + description
max_iterationsint ≥13validator retry bound
on_max_iterationsaccept_with_warning|block|escalateescalateretry exhaustion behavior

StatusTransition (inside lifecycle):

FieldTypeDefaultMeaning
from_statusstr...move origin
to_statusstr...move destination
gatesstr[][]gate IDs — ALL must pass
on_fail_statusstr?nullunset = hold position

Connects: types ↔ types (relations, DAG-enforced); transitions → gates (0..* each, ALL evaluated); rules: → governance IDs; stages produce/consume types; sessions target instances.

5. stages.yaml — stations

FieldTypeDefaultMeaning
idstr...unique stage identifier
labelstr...human-readable name
descriptionstr""purpose
enabledbooltruefalse hides from session start
orderint ≥1...feed order (guides, never exclusive)
modestr...mode ID — exactly 1, must resolve
skillsstr[]... (≥1)skill IDs — ALL must resolve
default_intentstr?nullsuggested intent
produces / consumesstr[][]node-type IDs created / required
tools / mcps / apis / clisstr[][]appendage IDs available per session
sdkstr[][]SDK IDs per session
allowed_kindsstr[][]producible kinds (empty = all)

Connects: the hub — mode (1), skills (1..), types (0..), appendages (0..), kinds (0..), intent (0..1). Every ID validated at load.

6. skills.yaml — trades

FieldTypeDefaultMeaning
idstr...unique skill identifier
labelstr...human-readable name
typeproductivity|governance|domain"productivity"descriptive category
enabledbooltruefalse = unselectable
descriptionstr""what it does
capabilitiesstr[][]capability tags
is_advisoryboolfalsereview-only skill
instructionsmarkdown?nullrendered to .vyasa/skills/{id}.md at scaffold; referenced, never inlined

Connects: stages require 1..* skills; sessions pick 1..*; gates (score_skill) reference exactly 1 validator skill.

7. gates.yaml — transition checks

FieldTypeDefaultMeaning
idstr...unique gate identifier
labelstr...human-readable name
descriptionstr""what this gate verifies
enabledbooltruefalse = skipped (passes)
typeschema|dependency|governance|approval|score"schema"descriptive category
conditionConditionConfig...structured condition (below)
messagestr"Gate condition not met"failure message
on_failblock|warn|notify|auto"block"failure behavior
notificationsNotificationConfig[][]platform (slack|teams|discord|email|jira|linear|webhook|pagerduty) + channel? + on (fail|pass|always, d.fail) + template?

The 12 condition operators — anything else fails at parse; each needs its companions or the load fails:

OperatorRequired companions
ALL_HAVE_SECTION / ANY_HAVE_SECTIONsection
FIELD_EXISTS / FIELD_EQUALSfield (+value)
COUNT_AT_LEASTmin_count (≥1)
NO_VIOLATIONSseverity
DEPENDENCY_APPROVED
SCORE_ABOVEscore_min (0–100) + score_skill
PARENT_COMPLETE
FILES_EXIST
FILES_MATCHfield (glob) + value (regex)
SECRET_SCAN

Optional on conditions: source (stage_outputs|previous_stage|all) and node_type filter.

Connects: transitions reference 0..* gates; SCORE_ABOVE → exactly 1 validator skill; NO_VIOLATIONS reads the violations store; SECRET_SCAN reads secret_patterns from governance.

8. guardrails.yaml — per-mode limits

FieldTypeDefaultMeaning
patternsdict[mode-id, write[] / read[] / hidden[] globs]{}EMPTY = no restrictions (permissive, visible)
headersdict[type-id, str[]]{}required headers per node type
extractionmode + node_type + strategy (full|headers-only|none|[headers])[]content-pull portion control

Connects: keyed by modes (patterns) and node types (headers, extraction); generates each session's scope card.

9. structure.yaml — kinds (file-routing truth)

Kind ID is the DICT KEY (no id field inside):

FieldTypeDefaultMeaning
label / descriptionstr""identity
enabledbooltruefalse = not a valid output target
pathstr template...final path pattern (src/{module}/controllers/{name}.py)
naming_conventionstr"{name}"{node_id}.md, {name}.py style
templatestr""initial file content
variablesdict[str, str]{}default values (session kind_variables override)
headers / header_levelsstr[] / dict[] / {}required sections + 1–6 overrides
validationValidationRule[][]required_section|required_field|min_length|pattern_match — unknown types fail closed

Connects: stages scope via allowed_kinds (0..*); sessions record kind + variables; apply routes by kinds.

10–14. Appendages — tools.yaml / mcps.yaml / apis.yaml / clis.yaml / sdks.yaml

Worker-facing capabilities, scoped per session by when. All five share the shape:

FieldTypeDefaultMeaning
idstr...unique appendage identifier
enabledbooltruefalse = unavailable
descriptionstr""what it provides
whenscoping[]which sessions/stages may use it
transport (below)per file...how the worker reaches it

Per-file transport:

FileTransport fields
tools.yamlcommand + args[]
mcps.yamlendpoint
apis.yamlendpoint + method + auth?
clis.yamlcommand
sdks.yamllanguage + package

Connects: stages scope 0..* of each; every referenced ID must resolve. These EXTERNAL capabilities are not Vyasa's own CLI/API/ MCP surfaces — separate vocabularies, kept separate on purpose.

15. hooks.yaml — 7 lifecycle hooks

HookFiresPurpose
pre_tool_usebefore every worker tool callreal-time interdiction
pre_sessionbefore session startsetup
post_sessionafter session endteardown / archive
pre_applybefore promotionlast check
post_applyafter promotionnotify / record
pre_gatebefore gate evaluationsetup
post_gateafter gate decisionnotify

Each is a script path (null = unhooked). Rules live in .vyasa/hooks/rules.yaml — missing/malformed rules file FAILS CLOSED (every call denied).

16. traceability.yaml — memory policy

FieldTypeDefaultMeaning
session_ttl_hoursintdefaultedsession expiry → lock expiry
snapshot_enabledbooldefaultedrollback snapshots on/off
metrics_enabledbooldefaultedtoken/duration stats on/off
retentiondict (sessions, violations, trace, snapshots)defaultedper-record purge counts

Connects: consumed by session lifecycle, apply, end-metrics, cleanup.

17. governance.yaml — your standing laws (scaffolded per project, user-owned)

Scaffolded into your project (OWASP Agentic Top 10 starter); the runtime never reads templates for it.

BlockFields
rules[]id (req) / name (d."") / severity (error blocks, warn advises; d.error) / operator (gate DSL) / field? (glob) / value? (regex) / description (d."") / enabled (d.true — false = kept, not enforced)
secret_patterns[]id + regex — feeds SECRET_SCAN
linked_rules[]source (connection id) + local (path under .vyasa/third-party/governance/)
packid / version metadata dict

Connects: node types opt in by rule ID (empty = none; unknown ID fails closed); linked packs arrive through connections.

18. connections.yaml — transports that fetch packs

FieldTypeDefaultMeaning
idstr...unique connection identifier
directioninbound|outbound...packs in vs telemetry out
protocol6-valued...git|file SHIPPED; sftp|ftp|ems|http are declared stubs (fail loud)
locationstr...endpoint (git URL, dir, URI…)
refstr?nullPINNED git ref (tag/commit — never main)
fingerprint_sha256str?nullexpected payload hash — mismatch fails closed
verify_signaturebooltruepayload signature check
verify_key_idstr?nullkeyring entry
encryptiontls|ssh|none"tls"transport policy
enforcementstrict|advisory"strict"strict blocks gates on violation; advisory warns
parsestr?nullpayload schema tag (e.g. rule-pack-v1)
descriptionstr""what this connection carries

Connects: governance linked_rules.source → exactly 1 connection per pack; managed via connections list/add/remove and governance use/fetch/update/remove.

19. assistants.yaml — declared workers

DICT keyed by assistant id (NOT a list — a - id: list fails to parse):

FieldTypeDefaultMeaning
command / args / modelstr / str[] / str?launch line + model injection
config_filesstr[][]editor configs generated at scaffold
envdict[str, str]{}${VAR} resolved from .env at dispatch (secrets never in YAML)
hook_script_pathstr?nullpre-tool-use hook
sandbox_modeadvisory|hooks|containerenforcement level
system_prompt_file_flag / system_prompt_envstr? / str?nullworkspace-context injection (flag + path, env var, or CWD discovery)
name_session_flagstr?nullworker-session naming
resume_commandstr{assistant_session_name} resume template
session_id_query / session_list_*str?nulllive-session tracking
activeboolfalseat most 1 project default (assistant set)

Connects: sessions bind 0..1 assistant; assistant use launches; resume re-engages. Built-in fixture entries: claude-code, pi, aider, open-interpreter, openclaw, goose, opencode, codex.

20. checklists.yaml — scoring rubrics

A checklists: list of dicts (no Pydantic class — dict convention; unloadable fails loud):

FieldTypeDefaultMeaning
idstr...checklist identifier
namestr...human-readable name
descriptionstr""what it grades
typestr...category
criterialist[]per-item rules — each becomes a score item

Connects: scoring consumes 0..* checklists per session.

21. constraints.yaml — size limits on the definition

FieldTypeDefaultMeaning
node_typesintnullmax node types
max_stagesintnullmax stages
max_modesintnullmax modes

Connects: nothing references it; the engine reads it directly. null = unlimited.

22. verification.yaml — proof tools

FieldTypeDefaultMeaning
idstr...tool identifier
enabledbooltruefalse = the authoring lever for inapplicable tools
categorystr...grouping (lint / type / test…)
parsestr?nulloutput parser tag
descriptionstr""what it proves
commandstr...shell command; {target} → project root; 120s timeout

Connects: sessions run 0..* tools for the Proof Pack; failures feed gates and scoring.

23. git.yaml — branch policy (.vyasa/git.yaml, project-level)

FieldTypeDefaultMeaning
base_branchstr"main"worktree base
work_branchstr"dev"integration branch
commit_targetcurrent_branch|work_branch"current_branch"where applies commit
state_commitslegacy flagsuperseded; retained so old files parse
state_trackingbooltruetimeline kill-switch (support/rollback only)

Sandbox (engine model — no file of its own): image, hooks_enabled, enforce, timeout_seconds, network, memory_limit_mb, heartbeat_timeout_seconds (d.120 → lock preemption).

Load-time rules (exact)

Every reference must resolve or the load fails and names it:

FromToCardinality
stagemodeexactly 1
stageskills1..* (each)
stagenode types (produces + consumes)0..* (each)
stagetool/mcp/api/cli/sdk0..* (each)
stagekinds (allowed_kinds)0..* (each)
stagedefault_intent0..1
node typeparent / child / depends_on0..* each (DAG — cycles fail)
transitiongates0..*, ALL evaluated
gate (SCORE_ABOVE)score_skillexactly 1
node type rules:governance rule IDs0..*; unknown ID fails CLOSED
governance linked_rulesconnectionexactly 1 per pack

Plus fail-closed model validators: SCORE_ABOVE needs score_min + score_skill · COUNT_AT_LEAST needs min_count · section operators need section · FIELD_EQUALS needs field + value · NO_VIOLATIONS needs severity · FILES_MATCH needs field + value · header_levels keys ⊆ headers · unknown operator strings rejected at parse.