# Clavue v8.9.2 — Release Notes

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

```bash
npm install -g clavue@8.9.2
# 或一次性:
npx -y clavue@8.9.2
```

---

## TL;DR

这一版做了三件事：

1. **/resume 不再丢失会话内容** — 引入持久 HEAD 指针，rewind 事务化，新增 `/branches` 命令把每次回滚后的"暗分支"显式化。
2. **修复 macOS 上 BashTool 卡 `read-only variable: status`** — 默认改用 `/bin/bash` 而非用户的 `$SHELL=/bin/zsh`。
3. **支持 `claude-opus-4-7` 模型** — 注册到模型表，1M context 启用，未注册模型现在会被 logo 标记 `(unknown — passthrough)`。

11 个新测试，0 个回归。

---

## 1. /resume 数据丢失修复（L1 + L2 + L3）

### 问题

我们把 transcript 当作链表读，但磁盘上它其实是一棵 **DAG**：每次 rewind / retry / parallel-tool-use / 中断都会产生一个新 leaf。在用户的真实数据上跑 audit：

| 会话 | 总消息数 | leaf 数 | `/resume` 实际可见 |
|------|----------|---------|---------------------|
| 88MB session | 5,272 | 1,317 | **17 (0.3%)** |
| 47MB session | 6,250 | 1,297 | 100 (1.6%) |
| 整体 79 个 session 平均 | 98,506 | — | **~16.5%** |

加上多处 `} catch {}` 静默吞错 + `walkChainBeforeParse` 默认开启的"死分支"预剪枝，丢失被完全淹没。

### 修复

**L1 紧急止血**

- `loadTranscriptFile` / `loadFullLog` 移除静默 catch，改为 `logError` + 遥测事件 `tengu_transcript_load_partial`、`tengu_resume_full_log_failed`，partial recovery 替代全丢。
- `walkChainBeforeParse` 默认翻为 opt-in（环境变量 `CLAUDE_CODE_ENABLE_PRECOMPACT_SKIP=1` 才启用）。代价：>5MB 会话多吃 ~30MB 峰值 RSS，换"绝不静默丢分支"。
- `checkResumeConsistency` 改为返回 delta，UI 端可以渲染。

**L2 持久 HEAD 指针**

新增 JSONL entry type：

```jsonl
{"type":"head","sessionId":"...","leafUuid":"...","branchId":"main","reason":"append","timestamp":"..."}
```

- `insertMessageChain` 在每次 commit 后自动写一条 head（append-only，~80 字节）。
- `loadFullLog` 优先消费最近的 head entry，fallback 走旧的 `max(timestamp) leaf` 兼容老 jsonl。
- 老会话**零退化**：没有 head entry 时行为不变。

**L2 事务化 rewind**

`REPL.rewindConversationTo` 从纯内存操作升级为：

```
flushSessionStorage → recordBranch(自动从用户 prompt 截首 60 字命名)
                   → recordHead(reason='rewind')
                   → setMessages(slice)
```

下次 `/resume` 一定走到 rewind 之后的分支，不再撞 timestamp 抽奖。

**L3 显式分支**

新 `/branches` 命令：

```
/branches               # 列表
/branches switch <id>   # 切到某分支（写新 head）
/branches rename <id> <name>
```

每次 rewind 自动建命名分支（e.g. `rw-lzj7m9-x4q2 · "fix the auth middleware bug"`）。

**UI 闭环**

`/resume` 之后会注入一条系统消息：

```
Resume loaded 17 of 5272 messages (-5255). 12 branches available — /branches to inspect.
```

让丢失从此可见。

### 用户操作变更

- `/resume` 行为不变，但更稳更全。
- 新命令：`/branches`。
- 新事件：`tengu_resume_leaf_picked`（source='head'|'fallback'）、`tengu_resume_consistency_delta`。
- 新环境变量：`CLAUDE_CODE_ENABLE_PRECOMPACT_SKIP=1` 用于回到旧的预剪枝优化。

### Audit 工具

仓库内置两个只读脚本：

```bash
node scripts/audit-transcripts.mjs       # 简版
node scripts/audit-transcripts-v2.mjs    # 含 parallel-tool 救援，更准
```

跑过任意时间，对比 `current%` vs `withHEAD%`，看能救回多少。

---

## 2. Bug 修复：macOS BashTool `read-only variable: status`

### 问题

macOS 上 `$SHELL=/bin/zsh`，BashTool 的 shell 选择跟随 `$SHELL`，于是模型写的 bash 命令被 zsh 执行：

```bash
for i in 1 2 3 4 5; do
  sleep 25
  status=$(curl -s ...)   # zsh: $status 是 read-only special parameter
done
# (eval):3: read-only variable: status
# Exit 1
```

### 修复

`src/utils/Shell.ts:findSuitableShell()`：

- `preferBash = true` 无条件优选 bash。
- `$SHELL` 只在指向 bash 时参与排序，指向 zsh 被忽略。
- `CLAUDE_CODE_SHELL=/path/to/zsh` 显式 override 仍尊重。

副作用：相同 zsh 特殊参数（`$LINENO`、`$SECONDS`、`$RANDOM`）也一并不再爆。

---

## 3. 模型支持：claude-opus-4-7 + 未知模型警告

### 背景

某些 Anthropic-compat 网关（如 ttqq.inping.com）在 first-party 发布前就接受 `claude-opus-4-7`。Clavue 此前模型表只到 4-6，导致：

- `isKnownModel('claude-opus-4-7')` → false → logo 显示原始字串
- `modelSupports1M('claude-opus-4-7')` → false → **1M context 静默关闭**
- prompt cache tier 走默认路径

### 修复

- 注册 `CLAUDE_OPUS_4_7_CONFIG`（first-party / bedrock / vertex / foundry）。
- `modelSupports1M` 白名单加 `opus-4-7`。
- `getPublicModelDisplayName` 加 `Opus 4.7` / `Opus 4.7 (1M context)` case。
- `firstPartyNameToCanonical` 识别 `opus-4-7`（specificity-first）。
- `LogoV2` 对未在 `ALL_MODEL_CONFIGS` 中的 model 显示 `(unknown — passthrough)`：

```
Opus 4.7 (1M context) · Usage-based API billing             # 已识别
some-future-model (unknown — passthrough) · Usage-based API billing  # 未识别
```

下一次有 typo 或新版本未注册，logo 会立刻提醒，不再静默降级。

---

## 升级路径

```bash
npm install -g clavue@8.9.2
clavue --version   # 期望: 8.9.2 (Clavue)
```

旧 jsonl 全部继续可用，零破坏性迁移。第一次 `/resume` 之后会写入 head entry，从此进入确定性轨道。

## 已知边界

- L3 `compact` 改非破坏被砍出此版本范围（"历史记录不重要"用户决策）。如果未来要做，路径已铺好。
- `/branches` UI 是文本列表 + 命令切换；尚未做交互式 picker。
- audit-transcripts-v2 的 "truly unreachable" 数字含真实分支损失（rewind 后再不会走的分支），是预期行为，不是 bug。

## 验证摘要

```
npm run check                     PASS (816/819, 3 skipped, 0 fail)
新增测试                           11/11 PASS
zsh status= repro                 重现 (read-only variable)
bash status= repro                成功 (hello)
audit-transcripts-v2              79 sessions, 98506 messages, 0 parse errors
experimental-dist                 8.9.2 (Clavue), CLI smoke OK
```

---

## 文件改动统计

| 文件 | 角色 |
|------|------|
| `src/types/logs.ts` | 新 `HeadEntry` / `BranchEntry`，扩 `LogOption.branchCount` |
| `src/utils/sessionStorage.ts` | L1 catch 暴露、HEAD 读写、`recordHead`/`recordBranch`/`loadBranchesForSession`/`setActiveBranch` 公开 API |
| `src/utils/conversationRecovery.ts` | 透出 `consistencyDelta` + `branchCount` |
| `src/utils/sessionRestore.ts` | 注入系统消息显示 delta + branch hint |
| `src/screens/REPL.tsx` | rewind 事务化（写 branch+head+flush 再改内存） |
| `src/commands/branches/{index.ts,branches.tsx}` | 新 `/branches` 命令 |
| `src/commands.ts` | 注册 `/branches` |
| `src/utils/Shell.ts` | preferBash=true |
| `src/utils/model/configs.ts` | 注册 `CLAUDE_OPUS_4_7_CONFIG` |
| `src/utils/model/model.ts` | 加 opus-4-7 识别 + `isKnownModel` |
| `src/utils/context.ts` | `modelSupports1M` 加 opus-4-7 |
| `src/components/LogoV2/LogoV2.tsx` | 未知 model 警告 |
| `scripts/audit-transcripts*.mjs` | 只读 audit 工具 |
| `tests/resume-loss-fix.test.mjs` | 4 invariant tests |
| `tests/bash-tool-shell-selection.test.mjs` | 3 shell-selection tests |
| `tests/known-model-detection.test.mjs` | 4 model-registry tests |

总：18 文件，+1559/−32。

## 致谢

本次修复基于用户提供的 1.7GB 真实 transcript audit 数据，根因诊断没有这份证据是凑不齐的。
