9c832b0e21
7 cards updated, 4 new cards added. Topics: signal-routing, worker-isolation, storage-layer, adapter-isolation, sense contracts, workflow runtime enforcement, coding conventions details. 小橘 <xiaoju@shazhou.work>
3.8 KiB
3.8 KiB
Workflow Engine
Stateful multi-step execution driven by Roles and a Moderator.
Core Concepts
- Workflow — definition with concurrency strategy
- Thread — one execution instance, unique
runId - Role — executes actions (has side effects).
(start, messages) → { content, meta } - Moderator — pure routing function.
(context) → next role | END
Thread Lifecycle
trigger → queued → started → step_complete ↺ → completed
↓
failed / crashed
Concurrency Config (nerve.yaml)
workflows:
cleanup:
concurrency: 1
overflow: drop # discard if already running
code-review:
concurrency: 3
overflow: queue
max_queue: 20 # queue limit, oldest discarded
createRole Helper
createRole builds a Role<M> from an adapter, prompt, Zod schema, and extract config:
import { createRole } from "@uncaged/nerve-workflow-utils";
import { cursorAdapter } from "@uncaged/nerve-adapter-cursor";
import { z } from "zod";
const coderSchema = z.object({ plan: z.string(), files: z.array(z.string()) });
const coder = createRole(cursorAdapter, coderPrompt, coderSchema, {
provider: { baseUrl: "...", apiKey: "...", model: "qwen-plus" },
});
// Use in WorkflowDefinition
const workflow: WorkflowDefinition<MyMeta> = {
name: "develop",
roles: { coder, reviewer },
moderator,
};
adapter: AgentFn— direct function referenceprompt: string | ((start, messages) => Promise<string>)— static or dynamicmeta: z.ZodType<M>— Zod schema, directly (no wrapper needed)extract: LlmExtractorConfig— provider for structured extraction
Runtime Enforcement Mechanisms
Role Authority & Validation
Role Function Lookup:
- Roles accessed via
def.roles[nextRole]dictionary lookup - Unknown roles trigger immediate workflow error (
Unknown role: ${nextRole}) - No dynamic role registration during execution
Result Validation (validateRoleResult()):
// Required return shape from every role function
{ content: string, meta: Record<string, unknown> }
contentmust be string (non-string → workflow error)metamust be plain object (array/null/primitive → workflow error)- Validation failure terminates thread immediately
Moderator Authority & Routing Control
Next Role Selection:
- Moderator must return role name from
roleskeys ORENDsymbol - Called after every role completion (receives full context)
- No validation of role name until execution attempt
- Pure function constraint: cannot perform side effects
Causal Chain Integrity:
- Moderator receives immutable history:
{ start, steps } - Steps array contains ALL role outputs in chronological order
- No role can modify prior steps or start metadata
- Thread context built from log store on crash recovery
Unauthorized Command Event Prevention
Message Flow Control:
- Role functions have NO direct access to kernel IPC
- All outputs flow through
sendWorkflowMessage()wrapper - Worker process validates messages before kernel transmission
- No direct log store database access from roles
Process Isolation:
- Roles execute in forked worker processes (not kernel)
- File system access limited to user permissions
- No network isolation (roles can make arbitrary HTTP calls)
- Worker has read/write access to workflow workspace only
Concurrent Thread Management
Kill Flag Implementation:
type KillFlag = { value: boolean };
// Checked before role execution and after completion
if (killFlag.value) {
sendThreadEvent(runId, "killed", { exitCode: 137 });
return;
}
Concurrency Enforcement:
- Workflow manager enforces per-workflow limits in kernel
- Excess threads queued/dropped per overflow policy
- No role can spawn additional threads (no access to workflow manager)