docs: update wf-stateless-design.md for new/resume $START semantics #103
@@ -200,7 +200,7 @@ payload:
|
|||||||
|
|
||||||
- `roles` — 内联定义,每个 role 的 `meta` 是独立的 ocas_ref(指向 ocas 内置 JSON Schema 节点)
|
- `roles` — 内联定义,每个 role 的 `meta` 是独立的 ocas_ref(指向 ocas 内置 JSON Schema 节点)
|
||||||
- `graph` — `Record<Role | "$START", Record<Status, Target>>`,每个 Target = `{ role, prompt }`
|
- `graph` — `Record<Role | "$START", Record<Status, Target>>`,每个 Target = `{ role, prompt }`
|
||||||
- Status 来自上一个 role 输出的 `status` 字段,`$START` 用 `_` 作为初始 status
|
- Status 来自上一个 role 输出的 `$status` 字段,`$START` 使用 `new`(首次启动)和 `resume`(恢复已完成的 thread)作为 status
|
||||||
- Prompt 模板使用 Mustache 渲染,变量来自 lastOutput
|
- Prompt 模板使用 Mustache 渲染,变量来自 lastOutput
|
||||||
- 不含 agent binding — agent 配置在 `~/.uwf/config.yaml` 中管理
|
- 不含 agent binding — agent 配置在 `~/.uwf/config.yaml` 中管理
|
||||||
|
|
||||||
@@ -208,7 +208,7 @@ Moderator 的求值逻辑:
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
evaluate(graph, lastRole, lastOutput) → { role, prompt }
|
evaluate(graph, lastRole, lastOutput) → { role, prompt }
|
||||||
// 1. status = lastRole === "$START" ? "_" : lastOutput.status
|
// 1. status = lastOutput.$status (e.g. "new" for $START first run, "resume" for completed thread resume)
|
||||||
// 2. target = graph[lastRole][status]
|
// 2. target = graph[lastRole][status]
|
||||||
// 3. prompt = mustache.render(target.prompt, lastOutput)
|
// 3. prompt = mustache.render(target.prompt, lastOutput)
|
||||||
```
|
```
|
||||||
@@ -422,8 +422,8 @@ type StepNodePayload = StepRecord & {
|
|||||||
Moderator 使用 `evaluate(graph, lastRole, lastOutput)` 进行同步 status-based routing:
|
Moderator 使用 `evaluate(graph, lastRole, lastOutput)` 进行同步 status-based routing:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// graph[lastRole][lastOutput.status] → Target { role, prompt }
|
// graph[lastRole][lastOutput.$status] → Target { role, prompt }
|
||||||
// $START 角色使用 "_" 作为初始 status
|
// $START 使用 "new"(首次启动)和 "resume"(恢复已完成 thread)作为 status
|
||||||
// prompt 通过 Mustache 模板渲染,变量来自 lastOutput
|
// prompt 通过 Mustache 模板渲染,变量来自 lastOutput
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export {
|
|||||||
validateFrontmatter,
|
validateFrontmatter,
|
||||||
} from "./frontmatter-markdown/index.js";
|
} from "./frontmatter-markdown/index.js";
|
||||||
export { createLogger } from "./logger.js";
|
export { createLogger } from "./logger.js";
|
||||||
export { generateModeratorReference } from "./moderator-reference.js";
|
|
||||||
export type {
|
export type {
|
||||||
CreateProcessLoggerOptions,
|
CreateProcessLoggerOptions,
|
||||||
ProcessLogFn,
|
ProcessLogFn,
|
||||||
@@ -35,4 +35,3 @@ export { extractUlidTimestamp, generateUlid } from "./ulid.js";
|
|||||||
export { generateUsageReference } from "./usage-reference.js";
|
export { generateUsageReference } from "./usage-reference.js";
|
||||||
export { VERSION } from "./version.js";
|
export { VERSION } from "./version.js";
|
||||||
export { generateWorkflowAuthoringReference } from "./workflow-authoring-reference.js";
|
export { generateWorkflowAuthoringReference } from "./workflow-authoring-reference.js";
|
||||||
export { generateYamlReference } from "./yaml-reference.js";
|
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
export function generateModeratorReference(): string {
|
|
||||||
return `# Moderator Reference
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The moderator is the workflow engine's routing component. It evaluates the directed graph defined in the workflow YAML to determine the next role (or \`$END\`) after each step — with zero LLM cost.
|
|
||||||
|
|
||||||
## Status-Based Routing
|
|
||||||
|
|
||||||
The moderator uses **status-based routing**: it inspects the previous step's extracted output (specifically the \`$status\` field) and looks up the corresponding edge in the graph.
|
|
||||||
|
|
||||||
### Graph Structure
|
|
||||||
|
|
||||||
The graph is a nested map: \`Record<Role | "$START", Record<Status, Target>>\`. Each role maps its possible \`$status\` values to a target with a \`role\` and \`prompt\`:
|
|
||||||
|
|
||||||
\`\`\`yaml
|
|
||||||
graph:
|
|
||||||
$START:
|
|
||||||
new: { role: planner, prompt: "Analyze the issue." }
|
|
||||||
resume: { role: planner, prompt: "Review the previous run output and continue." }
|
|
||||||
planner:
|
|
||||||
ready: { role: developer, prompt: "Implement the plan (CAS hash: {{{plan}}})." }
|
|
||||||
insufficient_info: { role: $END, prompt: "Not enough info." }
|
|
||||||
developer:
|
|
||||||
done: { role: reviewer, prompt: "Review branch {{{branch}}} at {{{worktree}}}." }
|
|
||||||
failed: { role: $END, prompt: "Developer failed: {{{reason}}}." }
|
|
||||||
reviewer:
|
|
||||||
approved: { role: tester, prompt: "Run tests on {{{branch}}} at {{{worktree}}}." }
|
|
||||||
rejected: { role: developer, prompt: "Fix issues: {{{comments}}}." }
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
### Routing Algorithm
|
|
||||||
|
|
||||||
1. Look up \`graph[lastRole]\` to get the status map for the current role
|
|
||||||
2. Look up \`statusMap[lastOutput.$status]\` to get the target
|
|
||||||
3. If target role is \`$END\`, mark thread as completed
|
|
||||||
4. Otherwise, render the edge prompt (Mustache templates with \`{{{field}}}\` from output) and spawn the next agent
|
|
||||||
|
|
||||||
### Edge Prompts and Mustache Templates
|
|
||||||
|
|
||||||
Edge prompts use triple-brace Mustache syntax (\`{{{field}}}\`) to interpolate values from the previous step's output into the next agent's task prompt. This passes structured data (branch names, file paths, CAS hashes) between roles without manual wiring.
|
|
||||||
|
|
||||||
## Special Nodes
|
|
||||||
|
|
||||||
- \`$START\` — entry point; uses status keys \`new\` (first start) and \`resume\` (resuming a completed thread)
|
|
||||||
- \`$END\` — terminal node; thread completes when reached and is moved to history
|
|
||||||
|
|
||||||
## Integration with Steps
|
|
||||||
|
|
||||||
Each \`uwf thread exec\` cycle:
|
|
||||||
1. Moderator reads the thread's head step output
|
|
||||||
2. Looks up \`graph[lastRole][output.$status]\` to pick the next role
|
|
||||||
3. If next is \`$END\`, marks thread as completed
|
|
||||||
4. Otherwise, renders the edge prompt and spawns the agent for the selected role
|
|
||||||
5. Extract pipeline parses agent output → new step node → append to CAS chain
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
export function generateYamlReference(): string {
|
|
||||||
return `# Workflow YAML Schema Reference
|
|
||||||
|
|
||||||
## Top-Level Structure
|
|
||||||
|
|
||||||
A workflow YAML file defines the complete workflow specification:
|
|
||||||
|
|
||||||
\`\`\`yaml
|
|
||||||
name: solve-issue # verb-first kebab-case identifier
|
|
||||||
description: "..." # human-readable description
|
|
||||||
|
|
||||||
roles: # named actors in the workflow
|
|
||||||
planner:
|
|
||||||
description: "Analyzes issue and outputs a plan"
|
|
||||||
goal: "You are a planning agent."
|
|
||||||
capabilities:
|
|
||||||
- issue-analysis
|
|
||||||
- planning
|
|
||||||
procedure: |
|
|
||||||
1. Read the issue
|
|
||||||
2. Produce a test spec
|
|
||||||
output: "Output the plan summary. Set $status to ready or insufficient_info."
|
|
||||||
frontmatter: # JSON Schema for structured output (drives routing)
|
|
||||||
oneOf:
|
|
||||||
- properties:
|
|
||||||
$status: { const: ready }
|
|
||||||
plan: { type: string }
|
|
||||||
required: [$status, plan]
|
|
||||||
- properties:
|
|
||||||
$status: { const: insufficient_info }
|
|
||||||
required: [$status]
|
|
||||||
|
|
||||||
graph: # status-based routing (nested map)
|
|
||||||
$START:
|
|
||||||
new: { role: planner, prompt: "Analyze the issue." }
|
|
||||||
resume: { role: planner, prompt: "Review the previous run output and continue." }
|
|
||||||
planner:
|
|
||||||
ready: { role: developer, prompt: "Implement plan {{{plan}}}." }
|
|
||||||
insufficient_info: { role: $END, prompt: "Not enough info." }
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## roles
|
|
||||||
|
|
||||||
Each role defines an actor in the workflow:
|
|
||||||
|
|
||||||
| Field | Type | Description |
|
|
||||||
|-------|------|-------------|
|
|
||||||
| \`description\` | string | Short description of the role's purpose |
|
|
||||||
| \`goal\` | string | System-level goal statement for the agent |
|
|
||||||
| \`capabilities\` | string[] | Tags describing what the role can do |
|
|
||||||
| \`procedure\` | string | Step-by-step instructions for the agent |
|
|
||||||
| \`output\` | string | Description of expected output format |
|
|
||||||
| \`frontmatter\` | JSON Schema | Defines the structured output the agent must produce |
|
|
||||||
|
|
||||||
### frontmatter
|
|
||||||
|
|
||||||
The \`frontmatter\` field is a standard JSON Schema object. The extract pipeline validates agent output against it. Key conventions:
|
|
||||||
- \`$status\` field drives routing decisions in the graph
|
|
||||||
- Use \`const\` or \`enum\` to constrain status values
|
|
||||||
- Use \`oneOf\` to define multiple valid output shapes (one per status)
|
|
||||||
- All \`required\` fields must appear in the agent's frontmatter output
|
|
||||||
|
|
||||||
## graph
|
|
||||||
|
|
||||||
The graph is a nested map defining status-based routing:
|
|
||||||
|
|
||||||
\`\`\`
|
|
||||||
Record<Role | "$START", Record<Status, { role: string, prompt: string }>>
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
| Level | Key | Value |
|
|
||||||
|-------|-----|-------|
|
|
||||||
| Outer | Role name or \`$START\` | Status map for that role |
|
|
||||||
| Inner | \`$status\` value | Target: \`{ role, prompt }\` |
|
|
||||||
|
|
||||||
### Special Nodes
|
|
||||||
- \`$START\` — entry point; uses status keys \`new\` (first start) and \`resume\` (resuming a completed thread)
|
|
||||||
- \`$END\` — terminal node; thread completes when reached
|
|
||||||
|
|
||||||
### Edge Prompts
|
|
||||||
Prompts use triple-brace Mustache templates (\`{{{field}}}\`) to interpolate values from the previous step's output. Example: \`"Implement plan {{{plan}}} in repo {{{repoPath}}}."\`
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user