The host validates every compiled definition against [WorkflowProductionCapabilities] before
persistence or start. This production contract is intentionally narrower than the in-process SDK
surface. The current production host accepts Agent, Branch, Repeat, wait-all and fail-fast
Parallel, PluginBlock, Input, and Approval. Closure-backed Task is explicitly
in-process-only; Retry, FanOut, and retry edges are rejected until their complete durable
implementations ship. Fail-fast persists a generation-scoped decision and exact sibling
cancellation intents before signaling runtime owners; unsignalled intents survive restart and
terminal sibling outcomes return through ordinary attempt observation. Explicit operator retry of a terminal failed activation remains a
separate bounded store operation; ambiguous mutation requires explicit repair before any later
attempt and is never automatic retry. Deterministic branch and repeat predicates use the current explicit versioned
contract and are bounded and validated during production admission. Versioned bounded edge
transforms are supported, including immutable run-state and canonical parallel join.left/join.right
sources.
Registration and start both rerun production admission. Plugin block nodes must resolve to one byte-equivalent enabled manifest declaration, so a definition cannot be persisted or started with an ownerless or stale block contract.
Durable prompt nodes use the versioned WorkflowPromptConfiguration contract. Skills are not
selected, required, or resolved by workflow contracts. Prompt text may ask the ordinary Bcode agent
to load a named skill through the normal skill catalog and tool boundary; missing or disabled skills
therefore remain ordinary prompt/tool outcomes and never invalidate a workflow definition or widen
authority. The prompt's explicit model, tool, timeout, context-target, and structured-output policy
remain authoritative.
Use typed workflow composition for domain behavior and let the host own durable registration, execution, discovery, and lifecycle state.
let workflow = WorkflowBuilder::new(
"review",
Step::map("review", |input: ReviewInput| review(input)),
)
.build()?;
let spec = WorkflowSpec::new("code-review.review", &workflow)?;
let session_id = /* active persisted session */;
let binding = PluginWorkflowBinding {
owner_plugin_id: "bcode.code-review".into(),
workflow_kind: "code-review.review".into(),
scope_key: session_id.to_string(),
display_label: Some("Code review".into()),
single_active: true,
};
let request = PluginWorkflowStartRequest::typed(
&spec,
&input,
session_id,
binding.clone(),
Some(stable_retry_id),
)?;
host.start_workflow(request).await?;Rules:
- Carry evolving/original context explicitly with
WorkflowStateEnvelope<State, Value>when a node accepts or returns a narrower value. - Put large retained values in the envelope's typed
artifactsreferences rather than copying bytes inline. - Keep per-run values in typed input.
- Let
WorkflowSpecderive the exact content-addressed definition identity. - Use the logical workflow kind for product vocabulary and durable binding.
- Use a caller-stable run ID when retrying after an uncertain start response.
- Use
single_activeonly when the owner/kind/scope permits one non-terminal run. - Find, inspect, pause, resume, or cancel through
binding.lookup()and generic host methods. - Do not persist workflow attempts, scheduling state, run correlation, or recovery journals in plugins.
- Keep product UI and commands plugin-owned; keep execution and reconciliation host-owned.