Skip to content

80 规范工作流系统 (Canonical Coding Workflow)

版本: v2.0 (Phase 1–5 + ADR Phase A–E) | 最后更新: 2026-04-09 | 灵感来源: oh-my-codex

状态: 全部五阶段实施完成,共 215+ 单元/集成/E2E 测试绿。详见 LIGHTWEIGHT_MULTI_AGENT_ORCHESTRATION_ADR.md

1. 背景

现有 Coding Agent 允许用户直接下达任意指令,agent 可自行决定是否先澄清、是否先规划。实践中暴露两个问题:

  1. 需求不清就开写 — 用户给一句话,agent 直接改代码,改完才发现边界理解错了
  2. 多步骤任务无交接物 — agent 中途出错无法恢复,也无法让另一个 agent 接手

OMX 用 4 个固定入口 ($deep-interview → $ralplan → $ralph/$team) + .omx/ 持久目录解决了这两个问题。本模块把这套思路移植到 ChainlessChain,不替换现有 cowork / skills / coding-agent,而是作为一条 opinionated 默认路径叠加上去。

2. 目标与非目标

目标

  • 为 Coding Agent 提供一条"澄清 → 规划 → 审批 → 执行"的固定默认路径
  • 把阶段交接物(intent / plan / progress)固化成可读可编辑的文件
  • 通过 Gate 强制阶段顺序,防止跳步
  • 复用现有的四层 skill 加载器,0 侵入注册

非目标

  • ❌ 不引入 tmux(Windows 生态差,Electron 集成复杂)
  • ❌ 不替换 cowork / skills / coding-agent
  • ❌ 不做自动审批(approved 必须显式触发)
  • ❌ 不做 MVP 范围外的 lifecycle hooks(留给 Phase 4)

3. 架构

3.1 分层

┌────────────────────────────────────────────────┐
│  用户 / Coding Agent 会话                       │
└────────────────┬───────────────────────────────┘

                 ▼  调用 workflow 技能
┌────────────────────────────────────────────────┐
│  Workflow Skills (4 个 builtin)                 │
│  deep-interview / ralplan / ralph / team        │
└────────────────┬───────────────────────────────┘

                 ▼  读写阶段状态
┌────────────────────────────────────────────────┐
│  SessionStateManager                            │
│  .chainlesschain/sessions/<id>/ 文件读写         │
│  + Gate 校验 (阶段顺序 + approved 标志)          │
└────────────────┬───────────────────────────────┘

                 ▼  fs
┌────────────────────────────────────────────────┐
│  持久层                                          │
│  intent.md / plan.md / progress.log / mode.json │
└────────────────────────────────────────────────┘

3.2 状态机

     ┌──────────┐
     │  (none)  │
     └─────┬────┘
           │ $deep-interview

     ┌──────────┐
     │  INTENT  │ ──── $ralplan ────▶ PLAN (approved=false)
     └──────────┘                         │
                                          │ $ralplan --approve

                                    PLAN (approved=true)

                        ┌─────────────────┼─────────────────┐
                        │                                   │
                        ▼                                   ▼
                    $ralph                              $team
                        │                                   │
                        ▼                                   ▼
                 EXECUTE (single owner)          EXECUTE (parallel)
                        │                                   │
                        └────────────── markDone ───────────┘


                                       DONE

3.3 Gate 规则

Gate位置逻辑
G1: 必须先 intentSessionStateManager.writePlan不存在 intent.md 即 throw
G2: 必须 approvedSessionStateManager.appendProgressplan.md frontmatter approved: false 即 throw
G3: ralph/team 入口handler.execute 开头readPlan + approved 双检查
G4: sessionId 安全SessionStateManager.safeSessionId正则 ^[A-Za-z0-9._-]+$ 防路径穿越

4. 文件结构

4.1 新增代码

desktop-app-vue/src/main/
├── ai-engine/code-agent/
│   └── session-state-manager.js          # 核心状态管理器
└── ai-engine/cowork/skills/builtin/
    ├── deep-interview/
    │   ├── SKILL.md
    │   └── handler.js
    ├── ralplan/
    │   ├── SKILL.md
    │   └── handler.js
    ├── ralph/
    │   ├── SKILL.md
    │   └── handler.js
    └── team/
        ├── SKILL.md
        └── handler.js

desktop-app-vue/src/main/ai-engine/cowork/__tests__/
└── workflow-skills.test.js                # 25 tests

4.2 运行时产物

<project>/.chainlesschain/sessions/<sessionId>/
├── intent.md        # markdown, 手工可读写
├── plan.md          # markdown + YAML frontmatter
├── progress.log     # append-only text
└── mode.json        # { stage, updatedAt }

5. 关键设计决策

5.1 为什么用文件而非数据库?

  • 可读可编辑 — 用户可以手动改 plan.md,对 agent 透明
  • git 友好 — 工作流状态可以纳入版本控制
  • 零依赖 — 不占用 SQLCipher 的表 quota
  • OMX 对标 — 跟 .omx/ 保持同构,降低用户学习成本

5.2 为什么 session 放项目目录而非 appData?

  • OMX 的 .omx/ 就在项目目录(跟代码一起迁移)
  • 项目作用域的状态更直观
  • appData/chainlesschain-desktop-vue/.chainlesschain/config.json 继续管全局配置,不冲突

5.3 为什么 $ralplan --approve 独立而非自动?

  • OMX 的经验:审批是最容易被跳过的步骤,必须显式触发
  • 审批语义保留给未来的权限系统(例如企业版 RBAC 里审批需要特定角色)

5.4 为什么 $team 不真实 spawn 子 agent?

  • 保持单一职责:workflow 技能只负责分派计划
  • 真正的子 agent 生命周期归 cowork runtime 管
  • 降低测试复杂度:handler 是纯函数式,易测

5.5 SessionId 的产生

  • 用户不传:handler 自动生成 session-<timestamp>
  • 用户传:必须通过 safeSessionId 正则校验
  • 恢复:通过 context.sessionIdtask.params.sessionId 复用

6. 与现有系统集成

6.1 Skill 四层加载

无需改 skill-loader.js。新技能放 cowork/skills/builtin/ 即 bundled 层,加载器自动发现。

6.2 Coding Agent Session Service

Phase 2 未改动 coding-agent-session-service.js。Phase 3 会注入 SessionStateManager,把 coding-agent 的 sessionId 与 workflow 的 sessionId 绑定。

6.3 Cowork Runtime

$team 的输出是一个 assignments[] 数组,等 Phase 5 实现 team runtime 时可以直接喂给它。

7. 测试策略

7.1 Unit Test

workflow-skills.test.js — 25 tests:

  • SessionStateManager: 9 tests (gate / CRUD / stage)
  • $deep-interview: 3 tests
  • $ralplan: 4 tests
  • $ralph: 3 tests
  • $team: 6 tests

7.2 测试关键点

  • 真实 tmp 目录 — 不 mock fs,避免 Vitest forks pool + CJS 内联的坑
  • Gate 验证 — 每个 Gate 都有对应的 negative test
  • 安全输入 — sessionId 路径穿越测试
  • 辅助函数独立测试parseSpec / distributeSteps 单独断言

7.3 运行

bash
cd desktop-app-vue
npx vitest run src/main/ai-engine/cowork/__tests__/workflow-skills.test.js

预期:25 pass, ~250ms。

8. 后续 Phase 规划

Phase内容说明
Phase 14 workflow skills已完成
Phase 2SessionStateManager已完成
Phase 3$xxx 解析器 + 分派器 + CLI 检查器已完成
Phase 3.5AIChatPage UI 集成(IPC + 输入拦截)已完成
Phase 4Lifecycle hooks (.chainlesschain/hooks/*.js)已完成
Phase 5Sub-runtime pool — 真 spawn 多个 Electron-main 子 runtime已完成
Phase 6文档站 + 模板cc init --template coding-workflow

Phase 4 详情:生命周期 Hooks

设计目标:在不修改核心工作流代码的前提下,让用户 / 企业注入审计、通知、策略拦截、CI 触发等横切关注点。

文件布局<projectRoot>/.chainlesschain/hooks/<event>.js,每个事件一个独立 JS 文件,存在才执行、缺失即跳过。

8 个事件pre-intent / post-intent / pre-plan / post-plan / pre-execute / post-execute / pre-done / post-done。前 6 个由 4 个 workflow 技能主动调用;done 相关事件为 Phase 6 的 $done 技能预留。

Hook 合约module.exports = async ({ event, sessionId, projectRoot, payload }) => any。也接受 { run } / { default } 形式。

Veto 语义

  • pre-* hook 抛异常或超时 → runner 重新抛出 → 调用方(技能 handler)捕获后返回 { success: false, error } → 状态文件不写入
  • post-* hook 抛异常或超时 → 仅记录日志,不回滚;保证已完成的工作不被 hook bug 破坏

关键实现细节

  • require.cache 每次调用前清除,支持 hook 文件热编辑无需重启 Electron
  • 默认 30 秒超时,Promise.race 实现;可通过 ctx.timeoutMs 覆盖
  • 所有技能 handler 通过 _deps.runHook 注入,测试用例可覆盖 hook 行为而不依赖文件系统
  • Hook 跑在 Electron 主进程,拥有完整 Node 权限 → 用户文档警示"只放自己可信的脚本"

Phase 5 详情:Sub-runtime Pool(真 spawn 多个 Electron-main 子 runtime)

设计目标$team 不再只返回"路由规划",而是真正为每个 member 拉起一个独立的 Electron-main 子进程(via process.execPath + ELECTRON_RUN_AS_NODE=1),在 OS 级别做故障隔离,并把每个 member 的工作限定在自己的目录里以避免并发写冲突。

关键冲突分析(选型前做过的功课):

  • CLI ↔ Desktop 互不冲突:CLI 只有 workflow-state-reader(只读)和 in-memory SubAgentRegistry(WS 用,不碰文件系统),desktop 独占写者身份。
  • Sub-runtime ↔ Sub-runtime 会冲突:若多个子 runtime 共享同一 sessionId 并发写 progress.log / mode.json → 错行、覆盖、YAML 坏掉。
  • 解法 A(采纳)sessionId 分片。每个 member 分配独立的 <parentId>.m<idx>-<role> 子 session,拥有自己独立的目录。
  • 解法 B(弃用):对 progress.log 加文件锁。会把并行退化成串行,失去意义。

文件与职责

文件角色
desktop-app-vue/src/main/sub-runtime/index.js子 runtime 入口(headless,无 BrowserWindow,无 DI,无数据库,无 MCP)。读 stdin JSON-lines,写 stdout JSON-lines,直接调 SessionStateManager
desktop-app-vue/src/main/ai-engine/code-agent/sub-runtime-pool.js父进程 pool:spawn(process.execPath, [entry], { env: { ELECTRON_RUN_AS_NODE: "1" } }) + stdin/stdout 协议 + _deps 注入。
desktop-app-vue/src/main/ai-engine/code-agent/session-state-manager.js新增 memberSessionId / createMemberSession / listMemberSessions / readMemberProgress 四个 API。
desktop-app-vue/src/main/ai-engine/cowork/skills/builtin/team/handler.js改造为 new _deps.SubRuntimePoolCtor(...).dispatch(...);父 session 只作为聚合单写者追加一行 [team] dispatched NxR + 每个 member 的 ok/fail 摘要。

Stdio JSON-lines 协议

# parent → child (stdin, 每行一个 JSON)
{ "cmd": "run", "projectRoot": "...", "sessionId": "parent", "assignment": { "memberIdx": 0, "role": "executor", "steps": ["a", "b"] } }
{ "cmd": "shutdown" }

# child → parent (stdout, 每行一个 JSON)
{ "type": "ready" }
{ "type": "progress", "memberId": "parent.m0-executor", "step": "a", "index": 0, "total": 2 }
{ "type": "done", "memberId": "parent.m0-executor", "success": true }
{ "type": "error", "memberId": "...", "error": "..." }
{ "type": "bye" }

Member session 布局

.chainlesschain/sessions/
├── parent-id/                 ← 父 session(只读 + 聚合写者)
│   ├── intent.md
│   ├── plan.md    (approved)
│   ├── mode.json
│   └── progress.log           ← 父 handler 写(单写者,无竞争)
├── parent-id.m0-executor/     ← 子 runtime 0 独占
│   ├── intent.md
│   ├── plan.md    (approved, steps=自己那份)
│   ├── mode.json
│   └── progress.log           ← 子 runtime 0 自己写
└── parent-id.m1-reviewer/
    └── ...

生命周期与容错

  • 每个 assignment = 一次性子 runtime。run 完成后父发 shutdown,子 process.exit(0);500ms 宽限期后 SIGTERM 兜底。
  • Pool 硬上限 HARD_CAP=6(和 $team size cap 对齐),默认 maxSize=4
  • readyTimeoutMs=10s / runTimeoutMs=60s,超时自动降级为 { success: false, error: "..." } 结果,绝不挂起。
  • 子 runtime 崩溃(exit before done)会被 pool 捕获并映射为失败结果,其他 member 不受影响。
  • Pool 不持有任何状态 —— 没有 idle 重用;换取的是"失败隔离 = 进程边界"的清晰心智模型。

测试矩阵

层级文件数量
Unit — SessionStateManager member APIscode-agent/__tests__/session-state-manager.test.js9
Unit — Sub-runtime dispatchersub-runtime/__tests__/sub-runtime-entry.test.js6
Unit — SubRuntimePool (mocked spawn)code-agent/__tests__/sub-runtime-pool.test.js7
Unit — team handler (fake pool injection)cowork/__tests__/workflow-skills.test.js 更新(已存在)
Integration — 真 spawn 子进程tests/integration/sub-runtime-pool.integration.test.js2

已排除的反模式

  • ❌ 子 runtime 内部再起 CodingAgentBridge → 进程爆炸(N × Electron-main + N × CLI-serve)
  • ❌ 走 WebSocket 通信 → 端口争用 + 认证复杂度 + 启动延迟;stdio 更简单可靠
  • ❌ Worker Threads / fork() → 无法真正隔离 Electron main state,crash 会波及父进程
  • ❌ 共享 sessionId 加文件锁 → 退化成串行

9. 风险与权衡

风险缓解
用户绕过工作流直接让 agent 改代码工作流是可选的,不强制;但 Coding Agent 默认 prompt 会推荐
.chainlesschain/sessions/ 污染仓库加入 .gitignore 模板;进阶用户可自行提交
多项目共用一个 session项目作用域隔离,sessionId 只在当前 projectRoot 有效
plan.md 手工编辑破坏 frontmatterreadPlan 正则容错,解析失败回退为 approved: false
长 progress.log 导致性能问题append-only,不读整个文件;Phase 4 可加 rotate

10. 变更清单

Phase 1+2 新增

  • desktop-app-vue/src/main/ai-engine/code-agent/session-state-manager.js (+220)
  • desktop-app-vue/src/main/ai-engine/cowork/skills/builtin/deep-interview/{SKILL.md,handler.js}
  • desktop-app-vue/src/main/ai-engine/cowork/skills/builtin/ralplan/{SKILL.md,handler.js}
  • desktop-app-vue/src/main/ai-engine/cowork/skills/builtin/ralph/{SKILL.md,handler.js}
  • desktop-app-vue/src/main/ai-engine/cowork/skills/builtin/team/{SKILL.md,handler.js}
  • desktop-app-vue/src/main/ai-engine/cowork/__tests__/workflow-skills.test.js (25 tests)

Phase 3 新增

  • desktop-app-vue/src/main/ai-engine/code-agent/workflow-command-parser.js — 纯函数解析器
  • desktop-app-vue/src/main/ai-engine/code-agent/workflow-command-runner.js — in-process 分派器
  • desktop-app-vue/src/main/ai-engine/code-agent/__tests__/workflow-command-parser.test.js (13 tests)
  • desktop-app-vue/src/main/ai-engine/code-agent/__tests__/workflow-command-runner.test.js (4 tests)
  • packages/cli/src/lib/workflow-state-reader.js — CLI 侧只读 reader
  • packages/cli/src/commands/session.js — 新增 session workflow 子命令
  • packages/cli/__tests__/unit/workflow-state-reader.test.js (5 tests)

Phase 3.5 新增

  • desktop-app-vue/src/main/ai-engine/code-agent/coding-agent-ipc-v3.js — 2 个工作流通道 + handler
  • desktop-app-vue/src/preload/index.jscodingAgent.{check,run}WorkflowCommand
  • desktop-app-vue/src/renderer/types/electron.d.ts — 类型定义
  • desktop-app-vue/src/renderer/pages/AIChatPage.vuehandleSubmitAgentAwareMessage 正则拦截
  • desktop-app-vue/src/main/ai-engine/code-agent/__tests__/coding-agent-ipc-v3.test.js (+3 tests)

Phase 4 新增

  • desktop-app-vue/src/main/ai-engine/code-agent/workflow-hook-runner.js — hook 加载 / 执行 / 超时 / veto
  • desktop-app-vue/src/main/ai-engine/code-agent/__tests__/workflow-hook-runner.test.js (16 tests)
  • deep-interview / ralplan / ralph / team handler 都通过 _deps.runHook 注入 pre-/post- 事件
  • workflow-skills.test.js 新增 4 个 hook 集成测试(post-intent 写入验证 + 3 个 veto 场景)

Phase 5 新增

  • desktop-app-vue/src/main/sub-runtime/index.js — headless Electron-main 入口 + stdio JSON-lines 调度
  • desktop-app-vue/src/main/sub-runtime/__tests__/sub-runtime-entry.test.js (6 tests)
  • desktop-app-vue/src/main/ai-engine/code-agent/sub-runtime-pool.js — 父进程 pool + _deps.spawn 注入
  • desktop-app-vue/src/main/ai-engine/code-agent/__tests__/sub-runtime-pool.test.js (7 tests,全假 child)
  • session-state-manager.js +93 行:memberSessionId / createMemberSession / listMemberSessions / readMemberProgress
  • session-state-manager.test.js (9 tests,新文件)
  • team/handler.js 改造:真调 pool.dispatch,把 dispatchResults 写进父 progress.log
  • tests/integration/sub-runtime-pool.integration.test.js (2 real-spawn tests)

ADR Phase A–D 新增 (2026-04-09):

  • session-state-manager.js 扩展:writeTasks / readTasks / writeVerify / readVerify / setRoutingHint / fix-loop retries 计数
  • $verify / $complete builtin skills + 门控(verify.status 必须为 passed)
  • $team handler 升级:按 tasks.jsonscopePaths 拆分,不再平均分编号步骤
  • workflow-session-ipc.js —— 4 个 read-only IPC 通道 (list / get / list-members / classify-intake)
  • workflow-session.ts —— Pinia store 只读消费 session 状态
  • CanonicalWorkflowPanel.vue —— Workflow Monitor 子面板(session 列表 + stage + task readiness + verify checks + routing hint)

ADR Phase E 新增 (2026-04-09):

  • desktop-app-vue/src/main/ai-engine/code-agent/intake-classifier.js —— 纯函数路由启发式
    • 输入: { request, scopePaths, fileHints, sessionId, tasks, concurrency }
    • 输出: { decision: "ralph"|"team", confidence, complexity, scopeCount, boundaries, testHeavy, signals, reason, recommendedConcurrency, suggestedRoles }
    • 规则: scopeCount>=2 → team | cross-cutting 短语 → team | trivial 短语 → ralph | 单 scope → ralph | 默认 → ralph
  • deep-interview/handler.js 集成:写完 intent.md 后调用分类器,通过 setRoutingHint 合并写入 mode.json(非阻塞、失败降级为 null)
  • workflow-session-ipc.js +workflow-session:classify-intake 通道(sessionId 提供时自动富化 tasks.json)
  • preload/index.js +workflowSession.classifyIntake
  • workflow-session.ts Pinia store +classifyIntake action + lastClassification 状态
  • CanonicalWorkflowPanel.vue 新增 routing hint 展示行(decision tag / complexity / confidence / scopeCount / reason / suggested roles)
  • 跨阶段不变式: _updateMode merge-write 天然保证 routingHint 跨 stage 迁移不丢失

文档

  • docs-site/docs/chainlesschain/coding-workflow.md (用户文档,含 Phase 1–5 + ADR Phase A–E 全量示例)
  • docs/design/modules/80_规范工作流系统.md (本文件)
  • docs/design/modules/81_轻量多Agent编排系统.md (ADR 设计讨论)
  • docs/implementation-plans/LIGHTWEIGHT_MULTI_AGENT_ORCHESTRATION_ADR.md (实施计划,已关闭)

测试总数

  • Phase 1–5: 94 tests
  • ADR Phase A–D: +67 tests (state + IPC + store + UI + verify)
  • ADR Phase E: +54 tests (classifier 20 + IPC 5 + store 3 + handler 4 + integration 5 + E2E 7 + deep-interview 集成 10)
  • 合计 215+ tests 全绿,其中 Phase 5 含 2 个真 spawn 集成,Phase E 含 7 个 handler → fs → IPC → preload shim → Pinia store 全链路 E2E

维护者: 开发团队 相关文档: coding-workflow.md, 79_Coding_Agent系统.md

基于 MIT 许可发布