# Clavue Roadmap v11 — 守护城河 + 补 Claude Code 短板

> 接力 `clavue-roadmap-v10.md`。v10 兑现了 ARCHITECTURE.md 的 `RuntimeDecision` 承诺；v11 在此基础上**对标真正的对手 Claude Code 上游**（不是 Cursor CLI / Aider，那是降维参照）。
>
> Generated 2026-05-17, baseline `clavue@8.10.18`, upstream `claude-code@2.1.143`,
> 测试基线 1372 pass / 0 fail / 3 skipped。

---

## 0. 重新校准的对标

v10 的隐含基线是"超过 Claude Code"，但 v9/v10 plan 文档里没有把这个对比量化。
v11 把它落表（详细推导见会话 transcript 2026-05-17 圆桌评分）：

| 维度 | Claude Code 上游 | clavue 当前 | 差 |
|---|:-:|:-:|:-:|
| 单 agent 主循环 | 9.0 | 8.5 | -0.5 |
| Provider 多样性 | 5.0 | **9.0** | **+4.0** |
| Plan/Verify 工程化 | 7.0 | **9.0** | **+2.0** |
| 多 agent 协同 (Mao) | 6.0 | **8.5** | **+2.5** |
| Memory 体系 | 7.5 | **9.0** | **+1.5** |
| 安全/权限 | 8.5 | **9.0** | **+0.5** |
| 工具数量/深度 | 7.5 | **8.5** | **+1.0** |
| Compact / 长 horizon | 8.5 | 8.5 | 0 |
| Eval CI 化 | 7.0 | 6.5 | -0.5 |
| 主循环可读性 | 7.0 | 6.5 | -0.5 |
| 启动延迟 | 8.0 | 6.5 | -1.5 |
| Native binary | 8.0 | 5.0 | -3.0 |
| **加权综合** | **8.0** | **8.4** | **+0.4** |

结论：**clavue 已经在 7 个核心维度领先 Claude Code**。
v11 任务分两类：

- **守城**（S 系列）— 把领先维度的护城河锁死，防止退化。
- **补短**（C 系列）— 追赶 Claude Code 仍领先的工程形状问题。

---

## 1. 北极星（v10 继承，不变）

每条改动走三问，任一为否就 defer：

1. **产品身份**：是否让 "execution-first AI coding CLI" 更清晰？(PRODUCT.md:3)
2. **运行时契约**：是否符合 "one runtime decision per turn"？(ARCHITECTURE.md:19)
3. **可逆性**：失败时能否 `git revert <slice>` 干净回滚？

---

## 2. v11 任务清单（按优先级）

### 守城 S 系列（保住领先维度）

#### **S1. Mao Supervisor 红队 eval**（P0，1.5 天）

护城河：multi-agent supervisor lane 是 Claude Code 不会做的事。
现有 7 个 mao 测试都是 unit/契约级别，缺**对抗式真实场景**的 eval。

- 新增 `tests/mao-supervisor-redteam.test.mjs`，覆盖：
  1. 多 agent 同文件写冲突 → supervisor 必须检测
  2. release/publish/push claim → 必须经过 final gate
  3. agent scope 漂移（worker 写了 contract 外的文件）→ 必须 flag
  4. worker 谎报 "tests pass" 但无实际 verify → supervisor 拒签
- ledger 必须能从 `.clavue/coordination/<session>/mao-ledger.json` 读出每条 finding。
- **验证**：4 场景全过；任一场景 supervisor 静默接受 → fail。
- **Blast**：低（新测试 only，不改 supervisor 实现）。

#### **S2. Provider Routing 矩阵进 CI**（P0，2 天）

护城河：9 路 provider routing。`docs/evals/world-class/provider-route-matrix.json`
已有 9 行契约，`scripts/run-provider-matrix.mjs` 已存在。**缺 CI gate**。

- 新增 `npm run check:provider-matrix` 把现有脚本接入 release-preflight。
- 每个 ready 路由 × 5 个真任务（write file / read file / grep / plan-verify / compact）跑 smoke。
- baseline JSON 落 `docs/evals/world-class/provider-route-baseline-v11.json`，回归 >5% 拒 release。
- **验证**：`npm run release:preflight` 在 baseline 退步时 exit 非 0。
- **Blast**：中（新 gate 进 release 流，需 staging 跑通后再硬接）。

#### **S3. "无 verify 不算完" hard gate**（P1，1 天）

护城河：Plan/Verify 工程化。`verifyGapReminder` 当前是 reminder，不是 gate。

- 升级：assistant 输出 "完成/done/finished" 等终结词但 `markVerified` 未触发 → 自动 inject 一轮 verify 提示，不放过 turn。
- 配置开关：`CLAVUE_VERIFY_HARD_GATE=0` 退到 reminder 模式（保留 escape hatch）。
- **验证**：新测试 `tests/verify-hard-gate.test.mjs`，pin 行为；现有 `verify-gap-reminder.test.mjs` 不退化。
- **Blast**：中（行为变更，需 escape hatch）。

### 补短 C 系列（追赶 Claude Code）

#### **C1. 主循环教科书化**（P1，3 天）

`query.ts` 1943 行，`QueryEngine.ts` 1101 行。新人 24h 读不懂。

- 抽 `src/runtime/turn-loop.ts` ≤300 行 orchestrator，调度 6 个子步骤模块（`pre-turn` / `route` / `dispatch` / `tool-loop` / `post-turn` / `recover`）。
- **不改语义**，只动形状（参考 v10 A1 的成功模式：alias type + 不动 callsite）。
- 写 `docs/runtime-turn-state-machine.md` ASCII 图，CI 校验图与 `turn-loop.ts` 函数名 1:1（grep gate）。
- **验证**：1372 测试不动；新增 `tests/turn-loop-state-machine-pin.test.mjs` 锁状态机形状。
- **Blast**：中-高（动主路径，但语义不变，可 revert）。

#### **C2. Eval baseline 进 CI**（P0，2 天）

把 `docs/evals/world-class/world-class-eval-report.json` 从档案升 gate。

- 新 gate `npm run check:eval-baseline`：跑 top-K 任务，对比 baseline，回归 >5% 拒 release。
- 接入 `npm run check`（与 S2 同一文件结构，但跑 world-class evals 而非 provider matrix）。
- regression bisect helper：`scripts/eval-bisect.mjs <baseline> <head>` 在退步时找罪魁 commit。
- **验证**：人为引入 prompt 退化 → eval gate fail；正常 PR → 全绿。
- **Blast**：中。

#### **C3. Native binary launcher**（P2，4–5 天，单独 PR）

冷启动 ~600ms → 目标 <200ms。v10 标 P3 单独。

- 选型：`pkg` / `bun build --compile` / `nexe`，先做 PoC 确定。
- `bin/clavue` 二进制独立 release artifact，npm 包仍发 `dist/cli.js` 兼容路径。
- `reexecWithLargerHeapIfNeeded` 改为 in-process IPC 或一次性 spawn，禁掉二次 reexec。
- **验证**：`tests/cold-start-benchmark.test.mjs` baseline + 退步 >20% fail。
- **Blast**：高（release 流变化），所以单独 PR / 单独 RC。

---

## 3. 显式不做的（防 v9 cargo-cult 重启）

- ❌ **不抄 Claude Code 上游 changelog**——v9 教训（`clavue-upgrade-plan-v9.md` 一半 slice 因此 defer）。
- ❌ **不拆 REPL.tsx / main.tsx**——v10 决策不变。
- ❌ **不上图形化 dashboard**——CLI 产品边界。
- ❌ **不引入新 LLM framework 依赖**（LangChain/LlamaIndex/DSPy）——execution-first 不是 framework-shell。
- ❌ **不为了对标分加新功能**——所有改动都要锚点在 PRODUCT.md / ARCHITECTURE.md。

---

## 4. 执行顺序

ROI × Blast 排序：

| # | Slice | Blast | 天 | 验证 |
|--|--|--|--|--|
| 1 | **S2** Provider matrix CI | 中 | 2 | preflight 跑通 |
| 2 | **C2** Eval baseline CI | 中 | 2 | check:eval-baseline 进 check |
| 3 | **S1** Mao 红队 eval | 低 | 1.5 | 4 场景测试通过 |
| 4 | **S3** Verify hard gate | 中 | 1 | new+old 测试都过 |
| 5 | **C1** turn-loop 抽取 | 中-高 | 3 | 1372 测试不变 |
| 6 | **C3** Native binary | 高 | 4-5 | 单独 PR / RC |

每 slice 独立 commit + RC。任一步 regression：`git revert`。

**v11 完成定义**：
- 测试 ≥ 1390 pass / 0 fail。
- `npm run check` + `npm run release:preflight` 全绿。
- Provider matrix + world-class evals 在 CI 中作为 gate 跑。
- Mao supervisor 红队 4 场景 anchored。
- `query.ts` 主循环可在 ≤300 行 orchestrator 中读完。

---

## 5. v12 候选池（v11 不做）

- 异构模型编排（per-step model routing：plan→Opus / edit→Sonnet / verify→Haiku）—— Claude Code 做不了的开拓方向。
- 跨 session 知识图谱（autoDream + extractMemories + teamMemorySync 三栈结构化）。
- Mao 对抗式 pair programming（不只 release gate，写代码时主动质疑）。
- /recap engine + /focus（v10 defer）。
- Vertex mTLS、PR URL paste、EnterWorktree path（v10 defer 沿用）。

---

## 北极星检查（最后一次）

| v11 任务 | 产品身份 | 运行时契约 | 可逆性 |
|---|:-:|:-:|:-:|
| S1 Mao 红队 | ✓ 守 supervisor 这条护城河 | ✓ 锁定 supervisor 决策 | ✓ 测试 only |
| S2 Provider matrix CI | ✓ 锁 routing 多样性 | ✓ RuntimeDecision 矩阵化 | ✓ gate 可关 |
| S3 Verify hard gate | ✓ "verify before claim" 兑现 | ✓ verification discipline 升级 | ✓ env 开关 |
| C1 turn-loop | — | ✓ "one decision per turn" 可读化 | ✓ pure refactor |
| C2 Eval CI | ✓ 防身份漂移 | ✓ 防能力退化 | ✓ gate 可关 |
| C3 Native binary | ✓ "start fast" 兑现 | — | ✓ 单独 PR |

v11 = **把 v10 的契约转成 CI gate** + **补 Claude Code 仍领先的工程形状**。

---

## 实施记录

### 2026-05-17 — S2a + C2 落地

| Slice | 状态 | 证据 |
|---|---|---|
| **S2a** Provider matrix gate | done | `scripts/check-provider-matrix.mjs` + `scripts/lib/provider-matrix.mjs` (共享生成逻辑) + `docs/evals/world-class/provider-route-baseline-v11.json` (9 行 v11 零点) + `tests/check-provider-matrix.test.mjs` (8 测试) + `release-preflight.mjs` `checkProviderRouteMatrix()` + `--skip-provider-matrix` escape hatch |
| **C2** Eval gate-set baseline | done | `scripts/check-world-class-eval-baseline.mjs` + `scripts/lib/world-class-eval-gates.mjs` (共享 GATES 定义) + `docs/evals/world-class/world-class-eval-baseline-v11.json` (7 gate 名单) + `tests/check-world-class-eval-baseline.test.mjs` (6 测试) + `release-preflight.mjs` `checkWorldClassEvalBaseline()` + `--skip-eval-baseline` escape hatch |
| **S1** Mao supervisor 红队 eval | done | `tests/mao-supervisor-redteam.test.mjs` (6 adversarial 场景: liar worker / hidden scope violation / chained conflict / release claim with blocking / adversarial ledger end-to-end / missing-key bypass attempt) — 现有 supervisor 实现在所有 6 场景下都正确 block delivery |
| **S3** Verify hard gate | done | `src/services/plan/verifyHardGate.ts` `computeVerifyHardGate()` 纯函数 — 检测 EN/ZH 完成短语 (done/finished/ready to ship/完成/搞定/收工…) ∩ pending verify-bearing steps ∩ NOT 同 turn `VerifyPlan`，返回 `{reason, pendingIndices, matchedPhrase}` 或 null；`tests/verify-hard-gate.test.mjs` (19 测试) pin 函数边界 + `CLAVUE_VERIFY_HARD_GATE=0/false/off/no` escape hatch 匹配器。Wiring：`src/utils/attachments.ts` 加 `verify_hard_gate` attachment type + `getVerifyHardGateAttachments()` builder（注册在 `verify_finish_line_reminder` 之后；带同样的"上一条 attachment 已是 hard gate 且无新 assistant turn"冷却）+ `isVerifyHardGateDisabled()` env 闸；`src/utils/messages.ts` 加 `verify_hard_gate` system-reminder renderer |
| **C1 D1** Turn-loop 状态机文档 + grep pin | done | `docs/runtime-turn-state-machine.md` (六阶段 PRE/ROUTE/DISPATCH/TOOL-LOOP/POST-TURN/RECOVER + 8 个 `queryCheckpoint` 锚点表 + 12 个 `Terminal reason` 列表) + `tests/turn-loop-state-machine-pin.test.mjs` (5 测试，双向 grep gate：源→文档、文档→源；phase 名称表)。**未抽** `src/runtime/turn-loop.ts` orchestrator —— D1 只锁契约，D2/D3 在契约保护下做形状抽取，避免动主路径前 hidden semantic drift |
| **C1 D2** Turn-loop 类型契约 | done | `src/runtime/turn-loop.ts` (纯 type + sentinel 数组：`TurnPhase` × 6 / `TerminalReason` × 12 / `RetryReason` × 7 / `PhaseOutcome<T>` / `PhaseFn<T>` / `TurnLoopState`) — 没有运行时副作用，esbuild erase；`tests/turn-loop-type-contract.test.mjs` (4 测试) lock 三方契约：types ↔ docs ↔ src/query.ts 的 continue 站点数。D3 抽 orchestrator 时按这些 type name 来命名，避免 mid-refactor 现编名字 |
| **C3 prep** Cold-start baseline + gate | done | `tests/cold-start-benchmark.test.mjs` (3 测试，**默认 skipped** 避免与 1400+ test worker pool 抢 CPU 导致 flake；`CLAVUE_RUN_COLD_START=1` 显式 opt-in) 量化当前 startup：`--version` median **316ms**（heap reexec fast path），`--help` median **447ms**（含 reexec roundtrip + 完整 module graph）；`docs/perf/cold-start-baseline.json` 落零点（Node v22.22.0 / macOS Darwin 24.6.0）。回归 gate `REGRESSION_CEILING_RATIO=1.5` 自动阻止 startup 退步 50% 以上；escape hatch `CLAVUE_SKIP_COLD_START_GATE=1` / `CLAVUE_UPDATE_COLD_START_BASELINE=1`。C3 native binary PR 落地后必须打这个 baseline 至少 20% 改进 → <357ms（help 路径，对应 v11 path roadmap 的 <200ms 目标） |
| **C1 D3 step 0** Phase banner 标记 | done | `src/query.ts` 在 `queryLoop` 体内加入 5 条 phase banner 注释（PRE/ROUTE/DISPATCH/TOOL_LOOP/POST_TURN，RECOVER 通过"早期 return/continue 即 RECOVER 决策"约定）— **零语义改动**，只是给 1943 行函数加入视觉断点。`tests/turn-loop-state-machine-pin.test.mjs` 增第 6 个测试 lock banners 不丢。D3 真抽 orchestrator 时按 banner 找断点逐 phase 迁移 |
| **C1 D3 step 1** PRE phase pure helpers | done | `src/runtime/turn-loop-pre.ts` (新文件，零 blast) 抽出两个最纯的 PRE 阶段计算：`shouldPersistContentReplacements(querySource)` (`agent:` / `repl_main_thread` 前缀 → persist 开关) + `selectUnboundedResultToolNames(tools)` (`!Number.isFinite(maxResultSizeChars)` filter → 进 `applyToolResultBudget` skip-set)。`src/query.ts` 头加 import，line 405-423 旧内联 filter+map+startsWith 改成 helper 调用，**零 yield、零异步、零状态变更**。`tests/turn-loop-pre.test.mjs` (10 测试) pin 两个 helper 边界：repl_main_thread / agent: 前缀 / 5 种 ephemeral 源 / NaN+Infinity 都视作 unbounded / 空入空出 / 返回 Set 实例。`npm test` 1421 → 1431 pass / 0 fail |
| **C1 D3 step 2** Autocompact tracking transition | done | `src/runtime/turn-loop-pre.ts` 加第三个 helper `nextAutoCompactTracking({compactionResult, consecutiveFailures, prevTracking, mintTurnId})` — 三分支纯函数：(a) compactionResult present → 全新 tracking + 新 turnId + 清失败计数；(b) consecutiveFailures !== undefined → 复用 prior shape，只更新 failure counter；(c) 都没有 → 透传。**关键**：mintTurnId 注入而非 import，helper 保持 pure，副作用留在调用方。`src/query.ts` line 558-580 两个 tracking 写入分支换成 helper 调用，**保留 yield 流和 messagesForQuery 赋值不动**。`tests/turn-loop-pre.test.mjs` 加 7 测试 pin：fresh 分支调 mintTurnId 一次 / compactionResult 优先级压过 failure / failure 分支不调 mintTurnId / 无 prior 时 fallback 到干净 baseline / 透传同 reference / 全 undefined → undefined / `consecutiveFailures: 0` 视为信号而非 no-op。`npm test` 1431 → 1438 pass / 0 fail |
| **C1 D3 step 3** Task budget formula dedup | done | `src/runtime/turn-loop-pre.ts` 加 `nextTaskBudgetRemaining({prev, total, finalContextTokens})` — `Math.max(0, (prev ?? total) - finalContextTokens)` 单行公式抽出。**消除一对重复**：原本 PRE phase (line 545-552) 和 TOOL_LOOP reactive-compact (line 1340-1348) 各内联一份相同逻辑，两站点同时改成 helper 调用，未来公式调整不再可能漂移。`tests/turn-loop-pre.test.mjs` 加 5 测试 pin：首次 fallback 到 total / 后续用 prev / 永不为负（clamp 0）/ 0 token 入是 no-op / 两调用同输入同输出（PRE/TOOL_LOOP 一致性契约）。`npm test` 1438 → 1443 pass / 0 fail |
| **C1 D3 step 4** Query chain tracking transition | done | `src/runtime/turn-loop-pre.ts` 加 `nextQueryChainTracking({prev, mintChainId})` — 二分支纯函数：(a) prev undefined → 新 chainId + depth 0；(b) prev present → 同 chainId + depth+1。`mintChainId` 注入保 pure。`src/query.ts` line 381-390 旧 ternary 换成 helper 调用。`tests/turn-loop-pre.test.mjs` 加 3 测试 pin：fresh 调 mintChainId 一次 / continuation 不调 mintChainId / `depth: 0` prev 仍走 continuation 分支（pin 旧 ternary 的真值语义）。`npm test` 1443 → 1446 pass / 0 fail |
| **C1 D3 step 5** Auto-compact analytics payload builder | done | `src/runtime/turn-loop-pre.ts` 加 `buildAutoCompactSucceededEvent({originalMessageCount, compactionResult, queryChainId, queryDepth})` + `AutoCompactSucceededEvent` 类型 — 把 25 行内联 payload 构造（消息计数 + token 累加 + cache_* coalesce）抽成纯函数。`logEvent` 副作用留在 `src/query.ts` 调用点。**关键边界**：原 `compactionUsage ? sum : 0` 三元和 `?? 0` coalesce 全部入 helper，`undefined` token snapshot 透传不被强转 0（保住 analytics 的"未测量 vs 测得为 0"语义）。`src/query.ts` line 507-538 内联 payload + 死局部解构同时清掉。`tests/turn-loop-pre.test.mjs` 加 5 测试 pin：happy path 总和 / 缺 compactionUsage → total=0 / 缺 cache_* 子字段 → 0 / 空数组 → count=0 / 缺 snapshot → 透传 undefined。`npm test` 1446 → 1451 pass / 0 fail |
| **C1 D3 step 6** PRE helper surface 契约表 + 双向 grep gate | done | `docs/runtime-turn-state-machine.md` 新增 "PRE phase pure helpers (D3 step 1–5)" 章节 — 6 个 helper × 调用点 × 用途三列表，作为 D3 真抽 orchestrator 时的"已契约冻结的纯函数清单"。`tests/turn-loop-pre.test.mjs` 加 2 个双向 grep gate 测试：(a) 文档表每个 helper 名必须在 `turn-loop-pre.ts` 真有 `export`；(b) `turn-loop-pre.ts` 每个 `export function` 必须在文档表里被列出。任一方向漂移 → 测试失败 + 错误消息直接指出"加一行到 docs/runtime-turn-state-machine.md"。`npm test` 1451 → 1453 pass / 0 fail |

**验证**：`npm test` 1380 → 1453 pass / 0 fail / 6 skipped (S2a/C2/S1 + S3 + C1 D1/D2/D3-step0 + C3 prep + C1 D3 step 1+2+3+4+5+6 共 +73 active 新测试 + 3 default-skipped cold-start)；
`npm run check:provider-matrix`、`npm run check:eval-baseline` 各自 exit 0；
`npm run release:preflight --skip-local-launcher` 全绿，新增两行
`OK provider route matrix gate` / `OK world-class eval baseline gate`。

**顺手发现**（佐证 gate 价值）：
- 旧 `provider-route-matrix.json`（2026-05-01 写）有 3 行已与 generator 漂移
  （chat-only / degraded / missing-credentials），无 gate 时无人发现。
- 旧 `world-class-eval-report.json` 仍记 `totalGates: 6`，但 runner 已加到
  7 个 gate（platform-autoresearch 后加入）。同样无 gate 时无人发现。

**escape hatches**：
- `npm run check:provider-matrix -- --update-baseline` 显式刷新 baseline。
- `npm run check:eval-baseline -- --update-baseline` 同上。
- `release-preflight --skip-provider-matrix` / `--skip-eval-baseline` 紧急
  跳过单条 gate（非默认）。

**两个 gate 都按"contract-only"原则**：只检测 shape drift，不打真实 API、不评估
模型输出质量。这与 v11 路线图 S2a 显式承诺一致；S2b（per-provider online
smoke）和 C2 的输出质量评估（model-graded eval）留到 v11.1。
