Workflow Engine — Conceptual Deep Dive
Purpose & Mental Model
Agentweaver workflows answer one question: which execution process should move an agent run from intent to a reviewed outcome?
The workflow engine is the policy layer between orchestration and runtime execution. The coordinator decides what the team should accomplish. The workflow engine decides which gates, loops, and terminal paths govern the run that carries out that work.
Conceptually, a workflow engine has five jobs:
- Define reusable process graphs as declarative workflow templates.
- Discover built-in, catalog, and project-authored workflow definitions.
- Track invocation context and automation metadata so runs carry the right operational context without changing workflow validity.
- Select the best process fit when several workflows are available.
- Bind the selected definition to real runtime executors, failing closed if any node or edge cannot run safely.
A useful rebuilding rule is: workflows are declarative policy graphs; binding is the safety boundary that turns policy into execution.
The executor chain shown is the built-in default, not every workflow. Checkpoints use ICheckpointStoreFactory: PostgreSQL-backed shared storage for the PostgreSQL provider, file storage for local/default configuration (apps/Agentweaver.Api/Program.cs:1063–1065; apps/Agentweaver.Api/Runs/RunWorkflowFactory.cs:181–188). Pending human decisions are durable records, not browser-local state.
Workflows are one half of run orchestration. The coordinator and run lifecycle are covered in orchestration.md; the focus here is how workflow definitions are authored, generated, selected, and bound.
Core Design Invariants
These invariants are the backbone of the workflow engine:
- Definitions are data, not code. YAML describes nodes, edges, and metadata. It does not execute directly.
- Discovery is server-side. Clients list, render, and edit workflows, but loading, validation, selection, and binding happen in the API.
- Trigger evaluation and workflow execution are separate concerns. A workflow definition may declare a trigger, but trigger verification/filtering happens before the workflow is selected and bound.
- Overrides cannot bypass safety. A requested workflow id is honored only if it resolves, validates, and binds.
- Selection is bounded model authority. The selector may choose among already-safe candidates; it may not invent ids or bypass availability and validation checks.
- Binding fails closed. A node type, gate, or edge with no known executor mapping aborts the build instead of becoming a no-op.
- Gates belong to workflow definitions. Effective workflow resolution does not inject a configurable project review policy; the binder checks the declared graph.
- A built-in default is always available. The default workflow is embedded in code and serves projects that ship no workflow files of their own, so every project has a valid workflow to run.
Workflow Template as Policy Graph
What a Workflow Template Is
A workflow template is a declarative graph with:
- a stable
id, - a human-readable
nameand optionaldescription/version, - a
startnode, - typed
nodes, - directed
edges, - optional board
stages, - and node metadata used for rendering and execution context.
The key abstraction is that a workflow describes what process should happen, not the hidden plumbing required to execute it. A single logical edge such as rai -> review when review may expand into adapters, state storage, predicates, review ports, and graph outputs when bound to the runtime.
The loader validates the static shape first: required fields, valid node type, unique node ids, known edge endpoints, check branches with matching outgoing edges, and valid references from structured node fields.
The binder then validates runtime bindability. This second phase matters because the schema can represent graph concepts before the live executor graph has executor support for them.
Node Types
Agentweaver's workflow schema models these conceptual node types:
- prompt — an agent turn that produces work or analysis.
- publish — an agent-backed action classified as
NodeKind.Agent, not the deterministic GitHub PR executor (apps/Agentweaver.Api/Workflows/NodeClassifier.cs:78). - peer_review — an AI review turn that can emit approval, change request, decline, pass, or fail verdicts.
- build_test — the platform-owned Build & Test gate. It runs the canonical build/test/preview instruction, emits
approved,request-changes, ordeclined, and should sit after any RAI safety gate and before human review for software workflows. InSandbox:AgentExecutionMode=pod-per-run, assembly Build & Test first launches a dedicated AgentHost pod for the coordinator run and configures it with the detached integration worktree as its working directory, so the gate and anystart_previewserver run from the same assembled tree. - check — a routing gate with declared branches. Known gate kinds include
rai,human-review, andrubberduck. - merge — an action that applies produced changes.
- open_pull_request — a platform-owned deterministic action, not an LLM turn. It invokes the PR client with configured
title,body,base,head, anddraftfields; templates support{run_id},{worktree_branch},{originating_branch}, and{outcome_summary}. It can follow an agent turn or a supported approval/merge transition. Successful publication emits the PR number/url; ordinary validation, credential, and client errors emit a failed step while passing the producedAgentTurnOutputonward unchanged. Cancellation is rethrown rather than swallowed (packages/Agentweaver.AgentRuntime/Workflow/OpenPullRequestTurnExecutor.cs:88–166). Do not equate one client invocation with a guaranteed single network request. - scribe — a recording step that captures the outcome.
- terminal — an explicit sink such as done, declined, or safety failed.
- fan_out, fan_in, coordinator_composed — schema-level extension points for richer topologies.
- serial is not a workflow node type; ordinary directed edges express sequential execution.
Runtime binding supports prompt/publish agent turns, peer-review, build_test, open_pull_request, check gates with known gate kinds, merge, scribe, terminal sinks, and a set of sequential / review / direct-completion topologies. Extension node types remain explicit schema concepts; until executors bind them, the runtime fails closed.
An open_pull_request node binds from a producing prompt node directly, or from a peer_review/build_test gate's approved/pass verdict (mirroring the existing gate → merge transition), and forwards into scribe exactly like an agent turn does:
nodes:
- id: implement
type: prompt
agent: worker
prompt: "Implement the requested change."
- id: build-test
type: build_test
- id: open-pr
type: open_pull_request
title: "Agentweaver: {outcome_summary}"
body: "Automated changes from run `{run_id}` on `{worktree_branch}`."
base: main
- id: record
type: scribe
edges:
- from: implement
to: build-test
- from: build-test
to: open-pr
when: approved
- from: open-pr
to: recordSoftware assembly gate order
Authored assembly gates are ordered using a breadth-first traversal from start over unconditional edges and verdict edges whose when is approved, pass, or review. The resolver selects known check/Build & Test nodes, sorts them by that traversal index (unvisited gates sort last, in declaration order), projects canonical assembly stages (rai, build-test, rubberduck, human-review), and deduplicates by stage. For non-code-producing work, the platform Build & Test gate is omitted (apps/Agentweaver.Api/Coordinator/CoordinatorAssemblyService.cs:1586–1676). This is aggregate gate projection, not execution of every workflow node by the assembly service.
For the built-in software workflows this means RAI runs before Build & Test, even if a YAML author groups node declarations differently. bug-fix follows triage -> fix -> verify -> rai-check -> build-test -> human-review; software-delivery follows plan -> implement -> test-gate -> rai-check -> rubberduck -> code-review -> build-test -> review-gate (packages/Agentweaver.Squad/Catalog/Resources/workflows/bug_fix.yaml, software_delivery.yaml). Copilot workflow and blueprint generation prompts carry the same rule so generated software workflows place build_test after any RAI gate and immediately before human review (apps/Agentweaver.Api/Workflows/WorkflowGatePromptGuidance.cs:7, apps/Agentweaver.Api/Workflows/CopilotWorkflowGenerator.cs:243, apps/Agentweaver.Api/Blueprints/CopilotBlueprintGenerator.cs:152).
Build & Test infrastructure failures are not authored request-changes verdicts. Pod capacity, launch/readiness, endpoint-resolution, and A2A transport failures are raised as typed infrastructure exceptions, then parked as retryable assembly_blocked reasons or failed terminally for non-retryable configuration errors (apps/Agentweaver.Api/Coordinator/CoordinatorAssemblyService.cs:1039–1043, :3559–3632). The emitted event payload includes detail, exceptionMessage, innerExceptionMessage, innerExceptionType, and infrastructureReason, so operators can distinguish a quota park from an AgentHost launch or A2A transport root cause.
The Default Workflow
The default workflow encodes the standard standalone run pipeline. Its canonical source is the code-embedded DefaultWorkflowTemplate (id default), loaded once through the real loader as BuiltInWorkflows.Default. DefaultWorkflowTemplate.TryMaterialize can write an inspectable project copy at .agentweaver/workflows/default.yaml; customization requires a new workflow id because the registry skips a materialized default. The current success path is agent -> rai -> review -> merge -> push-pr -> scribe -> done, with separate safety-failed and declined sinks (apps/Agentweaver.Api/Workflows/DefaultWorkflowTemplate.cs:42–161).
This default produces work, applies Responsible AI safety review, pauses for human review when required, merges if approved, attempts PR publication, and records the outcome. Its RAI routing distinguishes no-change, revision, human-review and safety-failed paths; it must not be confused with collective assembly, where RAI RED opens durable human review. The loops are part of the workflow, not exceptional control flow.
Role Slots, Catalog Roles, and Bespoke Charters
Workflow nodes carry two different kinds of "role" information:
- Workflow role slots describe the node's place in the graph or UI lane:
agent,review,merge,scribe,plumbing, and similar labels. - Catalog or bespoke execution roles identify who should perform a node when a real agent identity is needed.
Do not collapse these into one concept. A node with role: review is in a review lane; it is not automatically a catalog role named review. A peer-review node names a concrete reviewer with agent: qa-engineer when it needs that agent. A generated or project-authored node carries an inline charter when no catalog role fits.
The runtime uses explicit node fields and run context to build the agent prompt. Catalog roles are preferred because their charters are already known to the casting system. Bespoke charters are a controlled escape hatch for generated workflows whose process needs a role outside the catalog.
Execution context is node-specific, not one universal precedence chain. Generic prompt/publish nodes pass charter and prompt into AgentTurnExecutor while retaining the run's assigned identity; this binding does not pass node.Agent. Peer-review nodes pass both agent and charter to RubberduckTurnExecutor; Build & Test passes agent to its platform executor (apps/Agentweaver.Api/Runs/RunWorkflowFactory.cs:935–1006). role and kind remain graph/render metadata and do not select the executing agent.
Discovery, Validation, and Registry
Source Precedence
For a project, WorkflowRegistry.Build assembles a ProjectWorkflowSet from:
- the built-in default workflow (
BuiltInWorkflows.Default, fromDefaultWorkflowTemplate), - conformance-checked catalog library workflows from
CatalogConformanceSnapshot, - project-authored
.yaml/.ymlfiles under.agentweaver/workflows/(WorkflowRegistry.WorkflowsRelativePath).
The result is cached per project in WorkflowRegistry.GetOrLoad. Each cache entry is keyed by a signature of the project's top-level workflow YAML files plus the project's allowed workflow id set, so a replica refreshes its local cache when shared project files or blueprint restrictions change. WorkflowRegistry.Sync still provides the explicit user-facing refresh path and rebuilds from disk; validation errors are cached as registry results for replica coherence. Invalid workflows remain visible in ProjectWorkflowSet.Results with their errors, but ProjectWorkflowSet.Available excludes them.
The built-in default is always available. Catalog workflows are available without project-local files. A blueprint may restrict the allowed workflow ids for a project via Project.AllowedWorkflowIds; WorkflowRegistry.FilterByAllowedSet keeps only allowed ids plus the built-in default, which is always retained so a project never has zero workflows. An empty/absent allowed set means all workflows are returned (backward compatible).
Reserved ids are protected. A loaded project definition with id default is silently skipped as a materialized copy; other reserved catalog ids produce conflicts rather than overrides (WorkflowRegistry.cs:183–203). Duplicate ids are resolved deterministically in WorkflowRegistry.AddResult: among built-in/catalog collisions the higher semantic Version wins (ties keep the first-loaded source); among project files the first valid file wins and later duplicates are invalid load results. Source categories are therefore not an override-precedence hierarchy.
Validation Layers
Validation happens in layers:
- YAML parse —
WorkflowDefinitionLoader.Loadturns malformed YAML into a file-scoped invalid result. - Schema mapping — node type, start node, edge endpoints, branches, and references are checked.
- Bindability dry-run —
WorkflowRegistry.ValidateBindablerunsRunWorkflowGraphBinder.GetBindabilityErrorsto check whether every node and transition can map to real executor wiring. - Runtime resolution and binding — the effective workflow is resolved again and its declared graph is bound before execution.
This layered design lets the UI show useful authoring errors while preserving runtime safety.
Invocation Context
RunOrigin describes how a run began. Backlog pickup records RunOrigin.BacklogPickup; manually started and child runs have their own origin/responsibility context. The selector does not convert this into a separate invocation-kind eligibility filter (apps/Agentweaver.Api/Coordinator/CoordinatorOrchestratorExecutor.cs:297–300). There is no current WorkflowInvocationKind / ResolveInvocationKindAsync API.
Event and schedule automation are implemented upstream producers, not future selector modes. A matching trigger must obtain or recover an authorized automation invocation before the Ready task is published with a workflow pin (apps/Agentweaver.Api/Workflows/WorkflowEventTriggerService.cs:59–116; WorkflowScheduleTriggerService.cs:145–199). Manual starts and heartbeat pickup use the same normally available workflow set.
All valid workflows in the project's available set are candidates. A backlog task can carry a WorkflowOverrideId. The override is honored only if the workflow exists, is valid, and can bind safely. Otherwise the system logs the mismatch and continues with normal selection or safe fallback behavior.
Rebuild guidance: keep origin, trigger authorization, and workflow selection separate. See selection for override precedence and post-decomposition compatibility; do not treat an invalid requested id as executable.
Event Trigger Evaluation Pipeline
Event-triggered workflows add a narrow routing layer in front of the normal backlog → coordinator pickup path. The trigger mechanism is intentionally small:
POST /api/github/webhooks/repo-appbounds the raw body and verification time;- the endpoint verifies the configured Repo App HMAC keys (including rotation) before JSON deserialization, lifecycle routing, or predicate evaluation;
- installation/repository lifecycle processing resolves connected active projects and derives event names:
github.<event>and, when GitHub sent an action,github.<event>.<action>; WorkflowEventTriggerServicescans valid workflows' event declarations, evaluatesif:predicates, and claims or recovers an authorized automation invocation before publishing matching Ready tasks;- the coordinator heartbeat claims those Ready tasks through the same accountable path as schedule triggers and manual backlog work.
That ordering is the trust boundary: unsigned or badly signed deliveries never reach predicate evaluation, backlog creation, or prompt assembly.
Source: apps/Agentweaver.Api/Endpoints/GitHubWebhookEndpoints.cs:14–135 and apps/Agentweaver.Api/Workflows/WorkflowEventTriggerService.cs:59–116.
Curated predicate DSL
The event trigger DSL is deliberately not a general expression language. It supports:
hasLabelisNotLabeledWithbaseBranchreviewStaterefcategorycommentMatchesornot
Sibling entries in trigger.if are ANDed by default. or and not provide the only compound logic. Predicate support is event-specific (reviewState only for pull_request_review, ref only for push, and so on), and invalid event/predicate combinations are rejected when the workflow is loaded rather than silently ignored at runtime.
commentMatches privacy and ReDoS boundary
commentMatches is the only predicate that inspects raw user-authored text, so it has an explicit security boundary:
- the only raw text admitted is the verified
comment.bodystring from the GitHub payload; - the pattern is fixed in saved workflow configuration — not generated dynamically from the incoming comment;
- the pattern is validated against a restricted safe subset before the workflow is accepted;
- runtime matching uses
.NET's non-backtracking regex engine plus a hard 200 ms match timeout; - match failures, compile errors, and timeouts fail closed to “no match”;
- only the boolean match result crosses into workflow firing — Agentweaver does not log, persist, or forward the raw comment body into backlog task text or downstream prompts.
This keeps comment-command workflows possible without widening the rest of the engine into a free-form text processing surface.
Use snake_case in YAML and camelCase in the structured trigger API/UI. For example:
trigger:
type: event
event_name: github.issue_comment.created
if:
- comment_matches: { pattern: "^/agentweaver:triage$" }
- not:
or:
- has_label: { label: "ignore-bot" }
- comment_matches: { pattern: "^/agentweaver:skip$" }Workflow Library and Generation
Catalog Library
The catalog library provides reusable functional processes, with only conformance-checked definitions entering normal selection. A blueprint can restrict workflow ids and set a default.
The library is process-oriented. Workflow selection compares process steps and expected outputs rather than matching names. Inspect the current registry rather than assuming a fixed catalog count or that a retired id remains selectable.
Workflow Generation
Workflow generation turns a natural-language process request into an unsaved YAML draft.
Generation has these rules:
- The prompt is built server-side.
- The user's description is fenced as untrusted data.
- The prompt includes the schema, supported runtime node vocabulary, validation rules, available project roles, and few-shot examples.
- Output is cleaned for accidental Markdown fences.
- If the model omits an id, a kebab-case id is derived from the description.
- The generator validates with the same loader and binder dry-run used by runtime authoring paths.
- Exactly one correction pass is allowed.
- The result is a draft; it is not written to
.agentweaver/workflows/until a save/apply path persists it.
Trigger-aware generation extends that same flow rather than introducing a separate side channel. Both create-mode and edit-mode prompts teach the model:
- the schedule trigger schema (
daily/weekly/monthly, UTCtime_of_day, weeklyday_of_week, monthlyday_of_month); - the curated GitHub event shortlist (
issues,issue_comment,pull_request,pull_request_review,push,release,discussion); - the structured predicate vocabulary and boolean wrappers (
or,not); - a few-shot set of natural-language trigger examples such as label-driven issue triage, weekly schedules, and exact comment commands.
The output still goes through the same loader and binder gate as any other generated workflow. A bad event name, malformed predicate, or unsafe commentMatches pattern triggers the single correction pass; if the corrected draft is still invalid, generation fails closed instead of saving a broken trigger.
Blueprint generation can also invoke workflow generation when no library workflow is a good process fit. Applying that blueprint writes the generated workflow file, syncs the registry, and makes the workflow selectable.
Selection Logic
Workflow selection chooses a process for a task. It runs inside CoordinatorOrchestratorExecutor.SelectWorkflowAsync and is intentionally conservative: deterministic rules narrow the space first (registry ordering, availability, overrides), and WorkflowSelector.SelectAsync only chooses among 2+ available definitions.
The selector prompt asks for process fit:
- Match on the steps the workflow runs and the output it produces.
- Do not choose by name similarity or domain-word overlap.
- Prefer project/custom workflows when they perform the requested process.
- If nothing fits, select the first listed workflow, which is the project default.
The requested model response is JSON with a selected workflow id and short rationale. Candidate matching also accepts normalized ids/display names and supported string/prose forms; unusable responses get one retry before deterministic fallback. The selector prefers available default/standard fallback definitions, while the coordinator's outer exception fallback uses its resolved project default. See workflow selection for these distinct rules and post-decomposition Build & Test compatibility.
Overrides
There are three override channels, checked before automatic singleton handling:
Request/dialog override —
CoordinatorDraftInput.WorkflowOverrideId, checked before the backlog-task pin. An unavailable dialog value continues normal selection without falling back to that pin.Backlog task override —
BacklogTask.WorkflowOverrideId, persisted on the task before it is claimed.CoordinatorPickupServiceprependsuse {id}to the goal at pickup, andSelectWorkflowAsyncalso resolves the override id directly against the registry and available set.Conversational override — revision feedback
use {workflow-id}is parsed before the candidate-count shortcut and used when available. Available request, backlog, and conversational choices emit selection events even with one candidate.
An explicit override wins only inside the candidate safety boundary. It does not let a user or backlog item execute a workflow that the registry cannot resolve or that cannot bind safely.
Selection vs Runtime Resolution
The coordinator persists the selected workflow id on its WorkPlan for decomposition and collective assembly. Standalone run graph construction independently resolves a backlog-task override or the project default and binds that definition; it does not compose a review policy or read the coordinator's selected WorkPlan as its override source (apps/Agentweaver.Api/Runs/RunWorkflowFactory.cs:1495–1558). Child graphs take the separate trimmed path. These are different resolution responsibilities, not a promise that every run replays the selector's exact result.
Binding Declarative Nodes to Runtime Execution
Binding is where a workflow stops being YAML and becomes an executable graph.
The binder:
- classifies each node by
typeand, for gates,gate_kind; - resolves the node to a known executor kind;
- expands every logical edge into concrete executor wiring and predicates;
- wires terminal outputs from incoming edge semantics;
- preserves hidden plumbing such as adapters and stored merge data;
- fails closed when a node or transition has no mapping.
The binder resolves by node type, not by hardcoded ids. A workflow can rename agent, rai, review, merge, and scribe and still bind if the node types and gate kinds describe the same process. This is what lets library and generated workflows use meaningful node ids while preserving the same runtime semantics.
Canonical Transition Families
The default family includes:
- agent work into RAI,
- RAI revision back to agent,
- RAI safety failure to terminal,
- RAI no-change to scribe,
- RAI review path to human review,
- human approval to merge,
- human change request back to agent,
- human decline to terminal,
- merge completion to PR publication and then scribe in the current default (direct merge-to-scribe remains a supported family),
- merge blocked back to review,
- scribe to done.
Catalog-style workflows add supported generic families:
- sequential agent turns,
- agent output into peer review,
- peer-review pass or approval into merge / RAI / a subsequent agent turn,
- peer-review fail or request-changes back to an agent,
- direct agent or review completion through scribe,
- merge blocked back into peer review or an agent.
Anything outside supported transition families is not "best effort." It is a binding error.
Workflow-declared gates, not a project policy overlay
Author required gates in the workflow nodes and edges. Effective resolution returns the validated workflow without a separate policy-composition pass (apps/Agentweaver.Api/Runs/RunWorkflowFactory.cs:1495–1517). Blueprint validation accepts only review_policy: default (apps/Agentweaver.Api/Blueprints/BlueprintService.cs:113–115). There is no configurable project review-policy registry here.
Policy-prefixed adapters retained in RunWorkflowFactory are executor plumbing for declared gate transitions; their names do not establish a live composer. An unsupported declared gate or edge fails binding instead of becoming optional.
Relationship to Runs and Coordinator Work
A workflow is selected at the point where a run needs an execution process. Different run origins use the same concepts but have different responsibility boundaries:
- Manual run — an explicit start.
- Backlog pickup coordinator run — a heartbeat-started run with durable
RunOrigin.BacklogPickup. - Scheduled or event-driven automation — implemented trigger producers publish authorized, workflow-pinned Ready backlog tasks; normal pickup starts the coordinator.
- Coordinator parent run — owns planning, assembly, review, merge, and scribe for coordinated work.
- Coordinator child run — uses a trimmed child pipeline in an isolated working tree: agent work terminating at assemble-ready or a typed failure terminal. It does not perform per-child RAI, human review, merge, or scribe independently. Dependency outputs are merged forward through the coordinator integration branch before dependent children launch.
The important boundary is that workflows govern run gates, while the coordinator owns intent, decomposition, dependency frontiers, and assembly. A child run can produce a safe piece of work; the parent workflow decides how the assembled result is reviewed and merged.
See orchestration.md for the broader run lifecycle and coordinator model.
Extension Points and Gotchas
- Do not execute by id alone. Workflow ids identify definitions; node types and edge semantics determine bindability.
- Unattended pickup is governed outside the workflow YAML. Use project settings and automation rules to decide when a workflow should start automatically.
- Keep generated examples bindable. A generator that teaches unsupported node types will produce attractive but unrunnable YAML.
- Role metadata can be misleading. Distinguish render lanes from concrete catalog agent ids and inline charters.
- Peer review must be verdict-routed. A
peer_reviewnode needs verdict-labeled outgoing edges; otherwise bindability validation rejects it instead of guessing how to route it. - Terminal nodes are resolved by incoming semantics. Renaming
doneis fine; losing the scribe-sourced or verdict-sourced incoming edge is not. - Registry sync matters. Saving a file should be followed by an explicit sync for immediate feedback; other replicas refresh when they observe the changed shared-file signature.
- Workflow gates are explicit. Do not infer a configurable review-policy overlay from legacy adapter names.
- Selection and binding have separate responsibilities. Coordinator topology is persisted on the WorkPlan; standalone graph resolution and trimmed child graphs follow their own paths.
Rebuilding Blueprint
If you were rebuilding the workflow engine from scratch, implement it in this order:
- Define the workflow schema: start, typed nodes, edges, branches, and metadata.
- Write a loader that returns valid and invalid load results without crashing the whole set.
- Embed a built-in default workflow and parse it through the same loader as user files.
- Build a registry that discovers built-in, catalog, and project workflows, caches per project with a shared-file signature, and syncs explicitly.
- Add invocation-context tracking and keep it separate from candidate availability.
- Add bindability validation that rejects unsupported node types and transitions before runtime.
- Implement node classification by type and gate kind, never by fixed ids.
- Implement edge expansion from
(from kind, to kind, when)to concrete executor wiring. - Keep authored review gates explicit and validate every gate transition before binding.
- Add default and override resolution that revalidates availability and bindability.
- Add process-fit selection among available candidates with deterministic fallback.
- Add generation as a draft-only server-side prompt + validation + one correction pass.
- Surface graph descriptors and workflow-selected events for clients, but keep clients out of selection and binding.
- Add recovery tests that prove renamed nodes, invalid edges, invalid overrides, and unsupported types fail safely.
The central design principle is simple: load workflows as data, select among valid available process graphs, bind every edge to real executors, and fail closed whenever policy cannot be proven executable.
Where this lives
apps/Agentweaver.Api/Workflows/apps/Agentweaver.Api/Coordinator/WorkflowSelector.csapps/Agentweaver.Api/Coordinator/CoordinatorOrchestratorExecutor.csapps/Agentweaver.Api/Runs/RunWorkflowFactory.cspackages/Agentweaver.Squad/Catalog/Resources/workflows/docs/workflow-binder.mddocs/workflow-generation.mddocs/workflow-library.mddocs/workflow-selection.md
Diagram details and constraints
| Element | Contract |
|---|---|
| title | Generic default workflow |
| subtitle | Built-in template • merge → PR publication → Scribe |
| returns-heading | SOURCE / RETURN |
| outcomes-heading | OUTCOMES |
| footer | PR action can skip / fail and still reach Scribe. No-changes also reaches Scribe. |
| Agent work | Agent |
| Agent work | Agent task |
| Agent work | agent |
| RAI gate | Rai |
| RAI gate | Verdict routing |
| RAI gate | rai |
| Human review | Review |
| Human review | human-review |
| Merge | Merge |
| Merge | Merge outcome routing |
| Merge | merge |
| Publish / reuse PR | Publish / reuse PR |
| Publish / reuse PR | Create / reuse; not git push |
| Publish / reuse PR | action |
| Scribe | Scribe |
| Scribe | Record the run outcome |
| Scribe | scribe |
| Safety failed | Safety failed |
| Safety failed | Workflow endpoint |
| Declined | Declined |
| Done | Done |
| edge-02-label | revise |
| edge-03-label | safety- failed |
| edge-04-label | no- changes |
| edge-05-label | review |
| edge-06-label | approved |
| edge-07-label | request-changes |
| edge-08-label | declined |
| edge-09-label | merged |
| edge-10-label | blocked |
Diagram details and constraints
| Element | Contract |
|---|---|
| title | How work enters the coordinator |
| takeaway | Origin produces work; it does not filter the set of selectable workflows. |
| group-title-0 | ORIGIN CHANNELS |
| group-title-1 | AUTOMATION ADMISSION AND DURABLE WORK |
| group-title-2 | PICKUP, CONFIRMATION AND SELECTION |
| Manual request | Manual request |
| Manual request | Start coordinator directly |
| Manual request | submitting user |
| Verified Repo App event | Verified Repo App event |
| Verified Repo App event | HMAC before JSON parsing |
| Verified Repo App event | configured signing keys |
| Due schedule | Due schedule |
| Due schedule | Claim scheduled occurrence |
| Due schedule | recover outstanding work |
| Trigger + authorization | Trigger + authorization |
| Trigger + authorization | Match and claim invocation |
| Trigger + authorization | rejected: no work |
| Pinned Ready task | Pinned Ready task |
| Pinned Ready task | Durable workflow choice |
| Pinned Ready task | automation work item |
| Atomic pickup | Atomic pickup |
| Atomic pickup | Claim and reserve a run |
| Atomic pickup | start reserved coordinator |
| Coordinator run | Coordinator run |
| Coordinator run | Manual or backlog origin |
| Coordinator run | RunOrigin recorded |
| Approval policy | Approval policy |
| Approval policy | Autopilot controls unattended |
| Approval policy | not origin alone |
| Available workflows | Available workflows |
| Available workflows | No invocation-kind filter |
| Available workflows | valid supplied candidates |
| e0 | start |
| e1 | match |
| e2 | claim |
| e3 | publish |
| e4 | pickup |
| e6 | policy |
| e7 | select |
| groups | ORIGIN CHANNELS; AUTOMATION ADMISSION AND DURABLE WORK; PICKUP, CONFIRMATION AND SELECTION |
Diagram details and constraints
| Element | Contract |
|---|---|
| title | Workflow selection |
| subtitle | Trigger-agnostic • explicit choices precede singleton |
| returns-heading | SOURCE / RETURN |
| outcomes-heading | OUTCOMES |
| footer | Post-decomposition Build & Test compatibility is a separate check (executor:407–494). |
| Load candidates | Load candidates |
| Load candidates | Project default ordered first |
| Load candidates | registry.Available |
| Explicit override? | Explicit override? |
| Explicit override? | Dialog value, else backlog pin |
| Explicit override? | must be available |
| Conversational choice? | Conversational choice? |
| Conversational choice? | Revision feedback: use {id} |
| Candidate count | Candidate count |
| Candidate count | Only automatic selection |
| Candidate count | 0 / 1 / multiple |
| Ask selection model | Ask selection model |
| Ask selection model | Goal + roles + process fit |
| Ask selection model | maximum 2 attempts |
| Usable candidate? | Usable candidate? |
| Usable candidate? | Parse / normalize / prose match |
| Usable candidate? | reject unknown choices |
| Selected workflow | Selected workflow |
| Selected workflow | Emit selection + rationale |
| Selected workflow | workflow_selected |
| Explicit choice | Explicit choice |
| Explicit choice | Emit selection |
| Explicit choice | not auto-selected |
| Silent choice | Silent choice |
| Silent choice | One: candidate |
| Silent choice | Zero: project default |
| Model fallback | Model fallback |
| Model fallback | default / standard then non-code-review |
| Model fallback | else first candidate |
| Outer fallback | Outer fallback |
| Outer fallback | Project default |
| Outer fallback | when catch permits |
| edge-02-label | available |
| edge-03-label | absent / invalid |
| edge-06-label | 0 or 1 |
| edge-07-label | 2+ |
| edge-08-label | response |
| edge-09-label | exception |
| edge-10-label | accepted |
| edge-11-label | retry once |
| edge-12-label | 2 unusable |
| edge-13-label | emit choice |
| edge-14-label | outer catch |
| fallback | default / standard |
Diagram details and constraints
| Element | Contract |
|---|---|
| title | From workflow definition to execution |
| takeaway | Binding, checkpointed execution and observation are distinct responsibilities. |
| group-title-0 | DECLARATIVE INPUT AND BINDING |
| group-title-1 | EXECUTION AND CHECKPOINTS |
| group-title-2 | DURABLE OBSERVATION |
| Selected definition | Selected definition |
| Selected definition | Concrete authored workflow |
| Selected definition | no policy overlay |
| Node classification | Node classification |
| Node classification | Types and gate contracts |
| Node classification | not fragile node IDs |
| Factory + binder | Factory + binder |
| Factory + binder | Executors and typed edges |
| Factory + binder | fail closed if unsupported |
| Executable MAF graph | Executable MAF graph |
| Executable MAF graph | Run the bound workflow |
| Executable MAF graph | not always default chain |
| Checkpoint store | Checkpoint store |
| Checkpoint store | Provider-aware persistence |
| Checkpoint store | not universally files |
| Default example | Default example |
| Default example | Merge -> push-pr -> Scribe |
| Default example | authored default only |
| Watch loop | Watch loop |
| Watch loop | Consume execution updates |
| Watch loop | supervised observer |
| Pending + run state | Pending + run state |
| Pending + run state | Durable request/status state |
| Pending + run state | typed terminal projection |
| Workflow-step events | Workflow-step events |
| Workflow-step events | Progress for observers |
| Workflow-step events | not runtime control |
| e0 | classify |
| e1 | bind |
| e2 | execute |
| e3 | checkpoint |
| e4 | example |
| e5 | stream |
| e6 | persist |
| e7 | publish |
| groups | DECLARATIVE INPUT AND BINDING; EXECUTION AND CHECKPOINTS; DURABLE OBSERVATION |
Diagram details and constraints
| Element | Contract |
|---|---|
| title | Generate a draft, not a saved workflow |
| takeaway | One correction attempt reuses loader and binder validation; saving is a separate action. |
| group-title-0 | DRAFT REQUEST AND GENERATION |
| group-title-1 | NORMALIZATION AND VALIDATION |
| group-title-2 | BOUNDED CORRECTION OR RESULT |
| User description | User description |
| User description | Request workflow generation |
| User description | not a save command |
| Server prompt + model | Server prompt + model |
| Server prompt + model | Constrained generation request |
| Server prompt + model | authored draft response |
| Clean draft + ID | Clean draft + ID |
| Clean draft + ID | Normalize returned content |
| Clean draft + ID | built-in edit: new ID |
| Loader validation | Loader validation |
| Loader validation | Parse workflow definition |
| Loader validation | same validation pipeline |
| Binder validation | Binder validation |
| Binder validation | Check runtime bindability |
| Binder validation | not syntax alone |
| Valid draft response | Valid draft response |
| Valid draft response | Return YAML to caller |
| Valid draft response | not persisted/applied |
| Error + one retry | Error + one retry |
| Error + one retry | Ask model to correct error |
| Error + one retry | exactly one correction |
| Validate correction | Validate correction |
| Validate correction | Same cleanup/loader/binder |
| Validate correction | second pass only |
| Generation exception | Generation exception |
| Generation exception | Correction still invalid |
| Generation exception | explicit failure |
| e0 | request |
| e1 | clean |
| e2 | load |
| e3 | bind |
| e4 | valid |
| e5 | invalid |
| e7 | retry |
| groups | DRAFT REQUEST AND GENERATION; NORMALIZATION AND VALIDATION; BOUNDED CORRECTION OR RESULT |
Diagram details and constraints
| Element | Contract |
|---|---|
| title | Declarative data is not an executor |
| takeaway | A parsed workflow still needs structural and runtime-bindability validation. |
| group-title-0 | DECLARATIVE SHAPE |
| group-title-1 | STRUCTURE AND SEMANTIC CLASSIFICATION |
| group-title-2 | BINDABILITY AND RESULT |
| Workflow YAML | Workflow YAML |
| Workflow YAML | Authored graph document |
| Workflow YAML | start + nodes + edges |
| Start + typed nodes | Start + typed nodes |
| Start + typed nodes | Stable node identity |
| Start + typed nodes | type / gate contracts |
| Edges + metadata | Edges + metadata |
| Edges + metadata | Transitions and render fields |
| Edges + metadata | role / kind: visual |
| Structural checks | Structural checks |
| Structural checks | References and graph shape |
| Structural checks | loader-valid definition |
| Runtime classifier | Runtime classifier |
| Runtime classifier | Known node execution kinds |
| Runtime classifier | publish -> agent kind |
| Executor bindings | Executor bindings |
| Executor bindings | Concrete execution contracts |
| Executor bindings | not metadata inference |
| Start/edge compatibility | Start/edge compatibility |
| Start/edge compatibility | Typed transitions must fit |
| Start/edge compatibility | dry-run bindability |
| Executable graph | Executable graph |
| Executable graph | Valid concrete MAF graph |
| Executable graph | ready to execute |
| Binding error | Binding error |
| Binding error | Unsupported node/start/edge |
| Binding error | WorkflowBindException |
| e0 | parse |
| e2 | validate |
| e4 | classify |
| e5 | bind |
| e6 | check |
| e7 | valid |
| e8 | invalid |
| groups | DECLARATIVE SHAPE; STRUCTURE AND SEMANTIC CLASSIFICATION; BINDABILITY AND RESULT |
Diagram details and constraints
| Element | Contract |
|---|---|
| title | Visual role versus runtime context |
| takeaway | Node fields feed different executors; role/kind never becomes an executing catalog identity. |
| group-title-0 | AUTHORED FIELD FAMILIES |
| group-title-1 | PRESENTATION AND WORKER CONTEXT |
| group-title-2 | SPECIALIZED EXECUTOR CONTEXT |
| Authored node | Authored node |
| Authored node | Fields are not interchangeable |
| Authored node | node declaration |
| role / kind | role / kind |
| role / kind | Rendering metadata |
| role / kind | not executing identity |
| prompt / charter | prompt / charter |
| prompt / charter | Generic worker context |
| prompt / charter | prompt or publish node |
| Diagram presentation | Diagram presentation |
| Diagram presentation | Visual role and grouping |
| Diagram presentation | no agent assignment |
| AgentTurnExecutor | AgentTurnExecutor |
| AgentTurnExecutor | Receives prompt + charter |
| AgentTurnExecutor | not node.Agent |
| Peer-review fields | Peer-review fields |
| Peer-review fields | agent + charter |
| Peer-review fields | reviewer-specific contract |
| Rubberduck executor | Rubberduck executor |
| Rubberduck executor | Receives agent + charter |
| Rubberduck executor | peer-review context |
| Build & Test field | Build & Test field |
| Build & Test field | agent |
| Build & Test field | platform executor context |
| Build & Test executor | Build & Test executor |
| Build & Test executor | Receives agent context |
| Build & Test executor | not role/kind mapping |
| e0 | visual |
| e1 | worker |
| e2 | render |
| e3 | bind |
| e4 | peer |
| e6 | build |
| groups | AUTHORED FIELD FAMILIES; PRESENTATION AND WORKER CONTEXT; SPECIALIZED EXECUTOR CONTEXT |
Diagram details and constraints
| Element | Contract |
|---|---|
| title | Discover and cache valid workflows |
| takeaway | Registry sources are categories, not a project-policy precedence chain. |
| group-title-0 | DEFINITION SOURCES |
| group-title-1 | VALIDATION AND FILTERING |
| group-title-2 | DIAGNOSTICS, CACHE AND AVAILABILITY |
| Embedded default | Embedded default |
| Embedded default | Platform-provided definition |
| Embedded default | default retained |
| Conforming catalog | Conforming catalog |
| Conforming catalog | Known catalog workflows |
| Conforming catalog | reserved IDs protected |
| Project YAML | Project YAML |
| Project YAML | Project-defined documents |
| Project YAML | not review-policy files |
| Loader + bindability | Loader + bindability |
| Loader + bindability | Validate usable definitions |
| Loader + bindability | invalid entries diagnosed |
| Identity collisions | Identity collisions |
| Identity collisions | Reserved catalog conflicts |
| Identity collisions | materialized default skipped |
| Allowed-set filter | Allowed-set filter |
| Allowed-set filter | Filter available choices |
| Invalid diagnostics | Invalid diagnostics |
| Invalid diagnostics | Keep errors in results |
| Invalid diagnostics | not selectable candidates |
| Signature cache | Signature cache |
| Signature cache | Per-project refresh key |
| Signature cache | sync invalidates/refreshes |
| Available candidates | Available candidates |
| Available candidates | Valid and allowed workflows |
| Available candidates | selection input |
| e0 | load |
| e3 | check IDs |
| e4 | invalid |
| e5 | valid |
| e6 | cache |
| e7 | available |
| groups | DEFINITION SOURCES; VALIDATION AND FILTERING; DIAGNOSTICS, CACHE AND AVAILABILITY |
Diagram details and constraints
| Element | Contract |
|---|---|
| title | Bind contracts, not just node names |
| takeaway | Typed executor, start and transition contracts can reject otherwise parseable YAML. |
| group-title-0 | DEFINITION AND CLASSIFICATION |
| group-title-1 | EXECUTOR AND MESSAGE CONTRACTS |
| group-title-2 | START, EDGE AND TERMINAL VALIDATION |
| Parsed definition | Parsed definition |
| Parsed definition | Loader-valid graph data |
| Parsed definition | not execution proof |
| Type/gate classifier | Type/gate classifier |
| Type/gate classifier | Known node kinds |
| Type/gate classifier | renamed IDs still work |
| Unsupported kind | Unsupported kind |
| Unsupported kind | No concrete runtime contract |
| Unsupported kind | fail closed |
| Executor bindings | Executor bindings |
| Executor bindings | Concrete executor instances |
| Executor bindings | factory integrations |
| Transition adapters | Transition adapters |
| Transition adapters | Typed predicates and messages |
| Transition adapters | not arbitrary arrows |
| Terminal outputs | Terminal outputs |
| Terminal outputs | Known result contracts |
| Terminal outputs | typed completion |
| Start + edge checks | Start + edge checks |
| Start + edge checks | Dry-run bindability |
| Start + edge checks | verdict start may fail |
| MAF graph | MAF graph |
| MAF graph | Validated executable graph |
| MAF graph | start executor resolved |
| WorkflowBindException | WorkflowBindException |
| WorkflowBindException | Unsupported contract reported |
| WorkflowBindException | no silent fallback graph |
| e0 | classify |
| e1 | unknown |
| e2 | known |
| e3 | wire |
| e4 | outputs |
| e5 | check |
| e7 | valid |
| e8 | invalid |
| e9 | raise |
| groups | DEFINITION AND CLASSIFICATION; EXECUTOR AND MESSAGE CONTRACTS; START, EDGE AND TERMINAL VALIDATION |

