# Clavue v8.10.5 — Release Notes

**发布日期**：2026-05-11
**npm**：`clavue@8.10.5`
**GitHub**：https://github.com/mycode699/clavue/releases/tag/v8.10.5
**升级命令**：

```bash
npm install -g clavue@8.10.5
# 或一次性：
npx -y clavue@8.10.5
```

---

## TL;DR

8.10.5 是一次**质量基础设施**升级版。用户行为零变化，没有新功能，没有破坏性改动；面向开发与维护：

1. **新增 OTel 路由决策 span helper** — 每轮 per-turn 路由决策可以 lazy 接进 query loop 的现有 trace 树，便于未来排查"为什么这个 turn 选了 chat_only？"。
2. **三个核心纯函数加上 PBT（Property-Based Testing）网兜** — `coding-route-policy`、`agent-budget`、`token-budget`（之前零覆盖）。500 次随机种子 × 多个不变量，专门捕捉 off-by-one、边界、单调性回归。
3. **静态架构边界 checker** — `scripts/check-architecture-boundaries.mjs` 跑 4 条规则：tools 不能 import commands 实现、tools 不能 import TUI 框架、route policy core 必须保持纯净、memdir 不能 import TUI/工具实现。CI 友好，零误报。
4. **knip baseline ratchet** — `npx knip` 的真信号被锁进 `scripts/knip-baseline.json`（11 个 grandfathered orphan 文件），`scripts/check-knip-baseline.mjs` 在新 unreachable 文件出现时 fail。

升级路径：从 8.10.4 直接升级，运行时行为完全一致。

测试：1046 / 1043 pass / 0 fail / 3 skipped（`npm run check`，~60s）。

---

## 1. OTel 路由决策 span（`src/utils/telemetry/routeDecisionTracing.ts`）

### 背景

每个 turn 的"路由决策"（quality_tier、workflow_mode、transport、native vs prompt-only 工具集）以前只活在内存里：从 `routePolicyRuntimeCore.ts` 算出来 → query 循环消费 → 丢弃。当用户报告"为什么这个 turn 没出 EnterPlanMode？"或"为什么 SDK adapter 路径走了？"时，定位只能靠日志反推。

### 实现

新模块 `routeDecisionTracing.ts` 暴露一个 lazy helper：

```ts
import { recordRouteDecision } from './utils/telemetry/routeDecisionTracing.js'

// 在 query loop 拿到 decision 后：
await recordRouteDecision(decision)
```

设计要点：

- **Lazy dynamic import + try/catch**：不主动拉 `sessionTracing.ts` / GrowthBook / OTel SDK 依赖链。即使 telemetry 没启用、span 没在线、依赖加载失败，`recordRouteDecision` 也是 no-op，不抛错。
- **稳定事件名 `route.decision`**：监控/告警可以稳定订阅。改名是 breaking change。
- **零侵入纯模块边界**：`routeDecisionTracing.ts` 自己 hermetic，单元测试不需要拉整条 telemetry 栈即可验证。

### 测试

`tests/route-decision-tracing.test.mjs` 覆盖：no-op 路径、span 缺失路径、attributes shape。

### 接入状态

helper 文件已交付，但**未接入** `src/query.ts`（该文件在本轮独立工作中处于 dirty 状态，不做 surgical 范围外修改）。下个版本一行接入：

```ts
// in src/query.ts after computing routePolicyDecision
await recordRouteDecision(routePolicyDecision)
```

---

## 2. 三组纯函数 PBT（property-based testing）

### 为什么是 PBT 而不是 example tests

Example test 验证你**能想到的**输入；PBT 让 PRNG 帮你想到边界情况。三个被覆盖的函数共同特征：纯函数、决定路由/工具门控/turn 终止、错了**用户看不见**直到下一张账单。

PBT 全部使用本地 `mulberry32` seeded PRNG，**零新依赖**（不引入 fast-check）。500 次迭代 × 多个不变量。

### A. `tests/coding-route-policy-properties.test.mjs`（约 13 properties）

`resolveCodingRoutePolicyCore` 决定每个 turn 的 quality tier、workflow mode、transport。覆盖：

- 输入合法时输出符合 schema。
- "未知 capability" 必降级到安全默认（chat_only / blocked）。
- 模型家族识别 idempotent。
- 1m suffix / GrowthBook override 相互作用边界。

### B. `tests/agent-budget-properties.test.mjs`（约 15 properties）

`checkAgentBudget` / `recordIteration` / `formatBudgetViolationMessage` 决定 sub-agent 何时停。覆盖：

- 三个维度（iterations / tokens / duration）`>=` 边界（exactly-at-limit 必触发）。
- iteration 违例先于 token 违例上报。
- `recordIteration` 单调；非有限/负数 token 被忽略。
- `extractAssistantOutputTokens` 对各种 malformed payload 返回 0。

### C. `tests/token-budget-properties.test.mjs`（**之前 0 覆盖**）

`checkTokenBudget` 决定长 turn 是收到"continue nudge"还是因为 diminishing returns 终止。覆盖：

- agentId / null budget / 非正 budget 的强制 stop。
- < 90% 时 continuation 单调累加。
- 第一次就 ≥90% → stop without event；前置 continuation 后 ≥90% → stop with completionEvent。
- diminishing returns 检测的 4 条精确边界（continuationCount ≥3、当前 delta < 500、上次 delta < 500）。

注意：`src/query/tokenBudget.ts:1` 有运行时 `.js` 后缀 import，Node `--test` 的 strip-types 不重写后缀，因此该测试文件用同 47 个其他测试一样的 esbuild bundle 模式（`mkdtemp` + `entry.mjs` + `pathToFileURL`）加载目标模块。

---

## 3. 静态架构边界 checker（`scripts/check-architecture-boundaries.mjs`）

### 为什么需要

`clavue.md` 和 `ARCHITECTURE.md` 描述了 import 方向规则——但仅靠 code review 守卫。一次失误的 PR 就能破坏分层。

### 4 条规则

| 规则 ID | 范围 | 禁止 |
|---|---|---|
| `tools-cannot-import-specific-commands` | `src/tools/*` | 直接 import `src/commands/*` 实现（registry 可以） |
| `tools-cannot-import-top-level-ui` | `src/tools/*` | import `src/screens/*`、`replLauncher.tsx`、`main.tsx`（`src/ink.ts` wrapper 仍允许） |
| `route-policy-core-stays-pure` | `codingRoutePolicyCore.ts` / `routePolicyRuntimeCore.ts` | import `services/`、`tools/`、`commands/` 任何路径 |
| `memdir-cannot-import-tui-or-tool-impls` | `src/memdir/*` | import TUI（screens/components/ink）或 tool 实现（`*Tool.ts`、`UI.tsx`、`runAgent.ts`） |

### 测试模式

`tests/check-architecture-boundaries.test.mjs` 同时验证 green-on-clean（当前树通过）和 red-when-bad（在 sandbox 副本里制造违例并确认 checker exit 1）。一个总是 pass 的 checker 跟没有 checker 没区别。

### 接入

```bash
npm run check:boundaries
```

未接入 `npm run check`（保留为独立 gate），不增加默认 release gate 时间。

---

## 4. knip baseline ratchet

### 问题

`npx knip` 在无配置时报 1588 issues（绝大多数是 noise：dist sidecars、esbuild 动态加载、tsconfig path alias）。直接 fail CI 不可行；但完全不跑就放任 dead code 漂移。

### 方案

三件套：

1. **`knip.json`**：手工调好的 entry 拓扑（cli、SDK、scripts、memoryAutoresearchEval esbuild target、tests）+ `ignoreDependencies` 白名单（AWS / Anthropic SDK / opentelemetry exporters / 等等运行时按需加载的 deps）。配完后真信号 = 11 个 unreachable 文件。
2. **`scripts/knip-baseline.json`**：grandfather 这 11 个文件作为已知欠债。
3. **`scripts/check-knip-baseline.mjs`**：双向对比 ——
   - 出现新 unreachable 文件（**新增 dead code**）→ exit 1
   - baseline 列表里某文件不再 unreachable（已删除/已重连）→ exit 1，提示更新 baseline

### 测试

`tests/knip-baseline-gate.test.mjs` 在同一次 perturbation 里同时制造新 orphan + stale baseline 条目，验证两个错误都被报告。

### 接入

```bash
npm run check:knip
```

也保留为独立 gate（每次跑 ~5s × 一次 spawn）。

### 11 个 grandfathered 文件（可作为后续清理候选）

```
src/buddy/handoff.ts
src/buddy/observer.ts
src/commands/mao/index.ts
src/commands/mao/mao.ts
src/components/ProviderSetupWizard.tsx
src/components/providerSetupWizardHelpers.ts
src/components/ui/OrderedList.tsx
src/components/ui/OrderedListItem.tsx
src/hooks/useTimeout.ts
src/state/createAppStateStore.ts
src/utils/preflightChecks.tsx
```

`ProviderSetupWizard.tsx` + `providerSetupWizardHelpers.ts` + `OrderedList.tsx` + `OrderedListItem.tsx` 形成一个互引死簇。`mao/index.ts` + `mao/mao.ts` 也是死簇（mao 现已通过 sidecar `dist/mao-command.js` 暴露）。

---

## 5. 已携带的运行时改动

8.10.5 release 同时打包了之前轮次的脏文件改动（每个文件 git blame 可定位作者/动机）：

| 文件 | 类型 |
|---|---|
| `src/query.ts` | OOM 防护链路细化 |
| `src/services/compact/autoCompact.ts` | 紧急截断接入点对齐 |
| `src/services/compact/emergencyTruncate.ts` + `emergencyTruncateWiring.ts` | L3 紧急截断（已在 8.10.4 引入，此轮微调） |
| `src/tools/FileReadTool/FileReadTool.ts` | malware reminder 已在 8.10.4 移除，此轮无功能变化 |
| `src/utils/heapPressure.ts` / `nodeHeapBootstrap.ts` / `attachments.ts` / `messages.ts` / `sessionStoragePortable.ts` | 长会话存储与堆压辅助微调 |
| `src/components/ModelPicker.tsx` / `ProviderConfigManager.tsx` | UI 微调 |
| `scripts/verify-dist.mjs` | dist 校验加 8.10.5 banner 校验 |

测试覆盖：`tests/node-heap-bootstrap.test.mjs`、`tests/provider-config-manager.test.mjs`、`tests/emergency-truncate.test.mjs`、`tests/heap-soft-block-contract.test.mjs`、`tests/sanitize-path-collision.test.mjs`。

---

## 6. 升级路径

```bash
# 全局升级
npm install -g clavue@8.10.5

# 或一次性
npx -y clavue@8.10.5

# 验证
clavue --version  # 输出: 8.10.5 (Clavue)
```

- 用户终端从 8.10.4 直接升上来即可，配置文件、jsonl 会话、`~/.clavue/`、`~/.ccjk/` 全部保持兼容。
- 没有数据库迁移，没有 schema 变化。
- 环境变量逃生开关全保留（`CLAVUE_DISABLE_EMERGENCY_TRUNCATE=1`、`CLAVUE_DISABLE_HEAP_SOFT_BLOCK=1`）。

---

## 7. 文件变更摘要

| 文件 | 变更 | 说明 |
|---|---|---|
| `package.json` | `8.10.4 → 8.10.5` + `check:knip` script | |
| `package-lock.json` | 版本号 bump | |
| `src/constants/branding.ts` | `APP_VERSION_FALLBACK → 8.10.5` | |
| `README.md` / `clavue.md` / 上级 workspace `clavue.md` | 版本字符串 | |
| `src/utils/telemetry/routeDecisionTracing.ts` | 新增 | OTel route span helper |
| `tests/route-decision-tracing.test.mjs` | 新增 | helper 单测 |
| `tests/coding-route-policy-properties.test.mjs` | 新增 | route policy core PBT |
| `tests/agent-budget-properties.test.mjs` | 新增 | agent budget PBT |
| `tests/token-budget-properties.test.mjs` | 新增 | token budget PBT（之前 0 覆盖） |
| `scripts/check-architecture-boundaries.mjs` | 新增 | 4 规则静态边界 checker |
| `tests/check-architecture-boundaries.test.mjs` | 新增 | green-on-clean + red-when-bad |
| `scripts/check-knip-baseline.mjs` | 新增 | knip ratchet gate |
| `scripts/knip-baseline.json` | 新增 | 11 个 grandfathered orphan |
| `tests/knip-baseline-gate.test.mjs` | 新增 | ratchet 测试 |
| `knip.json` | 新增 | knip entry / ignore 配置 |

无运行时新功能，无破坏性变更。
