附录 VII:测试基础设施与数据治理

# 附录 VII:测试基础设施与数据治理

> 可重复性是工程的基础——能被录制的行为,才能被可靠地测试和审计。

---

## 附录 VII-A:VCR 录制回放(API Fixture 系统)

### 功能描述

VCR(Video Cassette Recorder)是 Claude Code 的**测试 API 录制回放系统**:在录制模式下捕获真实 API 调用并存储为 fixture 文件;在回放模式下(CI/测试环境)从 fixture 中还原响应,无需真实 API 调用。这使得测试既可离线运行,又能与真实 API 输出完全一致。

### 核心文件

| 文件 | 说明 |
|------|------|
| `src/services/vcr.ts` | 完整 VCR 实现:脱水/水化、Fixture 读写、hash 生成 |

### 工作机制

**启用条件**(`shouldUseVCR()`):
```typescript
// 自动启用:测试环境
process.env.NODE_ENV === 'test'

// 手动强制(仅 Anthropic 内部用户)
process.env.USER_TYPE === 'ant' && process.env.FORCE_VCR === '1'
```

**核心数据流**:

```
录制模式(VCR_RECORD=1):
  输入消息 → dehydrateValue() → SHA1 hash → fixtures/<hash>.json(不存在则调用 API 并写入)

回放模式(默认/CI):
  输入消息 → dehydrateValue() → SHA1 hash → 读取 fixtures/<hash>.json → hydrateValue() → 返回缓存响应
```

**两种 Fixture 类型**:

| Fixture 类型 | 入口函数 | 文件名格式 | 用途 |
|-------------|---------|-----------|------|
| LLM 对话 | `withVCR()` | `fixtures/<hash1>-<hash2>-....json` | 捕获 API 对话响应 |
| 通用数据 | `withFixture()` | `fixtures/<fixtureName>-<hash>.json` | 捕获 token 计数等其他 API |

**脱水(Dehydrate)**:`dehydrateValue()` 在存储/哈希前将 fixture 中的动态值(文件路径、cwd、临时目录、时间戳等)替换为占位符,确保同一逻辑输入在不同机器/目录上产生**相同 hash**。

**哈希策略**:LLM fixture 使用每条消息的 SHA1 前 6 位拼接(`hash1-hash2-...`),保留消息粒度以便定位变更位置。

**CI 保护机制**:
```typescript
// CI 环境下缺少 fixture → 立即报错,而不是静默跳过
if (env.isCI && !isEnvTruthy(process.env.VCR_RECORD)) {
  throw new Error(`Fixture missing: ${filename}. Re-run tests with VCR_RECORD=1...`)
}
```

### 关键常量/配置

```bash
# 录制新 fixture(会调用真实 API)
VCR_RECORD=1

# 指定 fixture 存储根目录(默认为 cwd)
CLAUDE_CODE_TEST_FIXTURES_ROOT=/path/to/fixtures

# Anthropic 内部强制启用 VCR(非测试环境)
FORCE_VCR=1
```

---

## 附录 VII-B:Grove 数据治理

### 功能描述

Grove 是 Claude Code 的**数据使用同意管理**系统。它控制用户是否同意将对话数据用于模型训练改进,并在用户首次使用时展示知情通知(带有宽限期机制)。

### 核心文件

| 文件 | 说明 |
|------|------|
| `src/services/api/grove.ts` | Grove 设置读取/更新 API,`getGroveSettings()`、`markGroveNoticeViewed()` |

### 数据结构

```typescript
type AccountSettings = {
  grove_enabled: boolean | null       // null = 未设置(首次使用)
  grove_notice_viewed_at: string | null  // ISO 时间戳,首次查看通知时间
}

type GroveConfig = {
  grove_enabled: boolean
  domain_excluded: boolean            // 该域名/组织是否豁免
  notice_is_grace_period: boolean     // 当前是否在宽限期内
  notice_reminder_frequency: number | null  // 提醒频率(天)
}
```

### 工作机制

1. **启动时检查**:`getGroveSettings()` 从 API 拉取当前账户的 Grove 设置(带 24 小时 session 级内存缓存)。
2. **宽限期(Grace Period)**:新用户首次看到 Grove 通知时,有一段时间不会被反复打扰;`notice_is_grace_period=true` 时仅展示一次通知,不阻塞使用。
3. **查看标记**:`markGroveNoticeViewed()` 向 API 发送 POST 请求,记录用户已查看通知的时间戳。
4. **缓存失效**:`updateGroveSettings()` 调用后立即清除内存缓存(`getGroveSettings.cache.clear()`),确保设置更新后立即生效。

### 隐私优先设计

```typescript
// 隐私优先模式下跳过 Grove 请求(确保最小必要流量)
if (isEssentialTrafficOnly()) {
  return { success: false }
}
```

API 失败时**不缓存失败结果**——避免网络抖动导致用户在整个 session 中无法访问隐私设置(死锁保护)。

### 关键 API 端点

```
GET  /api/oauth/account/settings                    → 获取 grove_enabled 状态
POST /api/oauth/account/grove                       → 更新 grove_enabled 开关
POST /api/oauth/account/grove_notice_viewed         → 标记通知已查看
```

---

## 附录 VII-C:Referral 邀请系统(Guest Pass)

### 功能描述

Referral 系统让 Claude Code max 订阅用户可以向他人发送 **Guest Pass**(访客通行证),被邀请者可以免费试用 Claude Code 有限次数。系统包含资格检查、兑换跟踪和本地缓存三层机制。

### 核心文件

| 文件 | 说明 |
|------|------|
| `src/services/api/referral.ts` | 资格检查、兑换查询、缓存管理 |

### 资格门控

```typescript
// 仅 max 订阅 + Claude.ai 订阅者 + 有组织 UUID 的用户可发 Guest Pass
function shouldCheckForPasses(): boolean {
  return !!(
    getOauthAccountInfo()?.organizationUuid &&
    isClaudeAISubscriber() &&
    getSubscriptionType() === 'max'
  )
}
```

### 核心 API

```typescript
// 检查是否有资格发送邀请(默认活动:claude_code_guest_pass)
fetchReferralEligibility(campaign?: ReferralCampaign): Promise<ReferralEligibilityResponse>

// 查询已发送邀请的兑换情况
fetchReferralRedemptions(campaign?: string): Promise<ReferralRedemptionsResponse>

// 获取邀请者奖励信息(如有)
type ReferrerRewardInfo = { ... }
```

### 本地缓存策略

- 资格结果缓存于 `~/.claude/settings.json` 的 `passesEligibilityCache` 字段,以组织 UUID 为 key
- 缓存有效期:**24 小时**(资格仅在订阅变化时才改变,高频检查意义不大)
- `checkCachedPassesEligibility()` 返回三元状态:`{ eligible, needsRefresh, hasCache }`

### API 请求超时

```typescript
// 资格检查:5 秒超时(后台 fetch,可容忍)
timeout: 5000

// 兑换查询:10 秒超时(用户可见,允许稍长)
timeout: 10000
```

---

## 附录 VII-D:Agent Summary(子 Agent 进度摘要)

### 功能描述

在 Coordinator 模式下,每个子 Agent 每隔 30 秒自动生成一句话的**实时进度摘要**(如 `"Reading runAgent.ts"`),供父 Agent / 用户界面展示当前正在做什么,而无需查看完整的对话流。

### 核心文件

| 文件 | 说明 |
|------|------|
| `src/services/AgentSummary/agentSummary.ts` | 定时器、forked Agent 查询、摘要更新 |
| `src/utils/forkedAgent.ts` | `runForkedAgent()`:共享 prompt cache 的轻量 Agent fork |

### 工作机制

```
startAgentSummarization(taskId, agentId, cacheSafeParams)
    │
    ├── 每 30 秒触发一次 runSummary()
    │       │
    │       ├── getAgentTranscript(agentId)  ← 读取子 Agent 当前消息历史
    │       ├── filterIncompleteToolCalls()   ← 过滤不完整的 tool call
    │       ├── runForkedAgent(params, summaryPrompt)  ← fork 出轻量查询
    │       │       └── 复用父 Agent 的 prompt cache(零额外 cache fill 成本)
    │       └── updateAgentSummary(taskId, summaryText)  ← 写入 UI 状态
    │
    └── stop() → 取消定时器,中止进行中的 fork
```

**摘要 Prompt 规范**:
```
Describe your most recent action in 3-5 words using present tense (-ing).
Name the file or function, not the branch. Do not use tools.

Good: "Reading runAgent.ts"
Good: "Fixing null check in validate.ts"
Bad (past tense): "Analyzed the branch diff"
Bad (too vague): "Investigating the issue"
```

**最小消息数保护**:消息数 < 3 时跳过摘要(上下文太少,摘要无意义)。

**Cache 共享**:`runForkedAgent()` 使用与父 Agent 相同的 `CacheSafeParams`,摘要查询复用已有 prompt cache,额外成本极低。

### 关键常量

```typescript
const SUMMARY_INTERVAL_MS = 30_000  // 30 秒摘要间隔
```

---

## 附录 VII-E:Release Notes(更新日志展示)

### 功能描述

Claude Code 在版本更新后会在下次启动时展示更新日志,采用**后台预取 + 文件缓存**策略,确保启动时日志立即可用(不阻塞 UI 渲染)。

### 核心文件

| 文件 | 说明 |
|------|------|
| `src/utils/releaseNotes.ts` | 日志获取、缓存管理、展示条件判断 |

### 工作机制

```
版本更新检测 → 后台 fetch GitHub 原始 CHANGELOG.md
    │
    ├── 写入 ~/.claude/cache/changelog.md(文件缓存)
    │
下次启动时:
    ├── 同步读取内存缓存 changelogMemoryCache
    └── 展示最近 MAX_RELEASE_NOTES_SHOWN(5)条更新
```

**缓存迁移**:历史版本将 changelog 存储于 `settings.json` 中,现已迁移到独立文件(`~/.claude/cache/changelog.md`),`migrateChangelogFromConfig()` 处理一次性迁移。

### 关键常量/配置

```typescript
const MAX_RELEASE_NOTES_SHOWN = 5  // 最多展示 5 条更新记录

// Changelog 源地址
const RAW_CHANGELOG_URL =
  'https://raw.githubusercontent.com/anthropics/claude-code/refs/heads/main/CHANGELOG.md'

// 本地缓存路径
~/.claude/cache/changelog.md
```

---

## 附录 VII-F:Session Backfill

### 功能描述

`/backfill-sessions` 命令将历史本地会话(存储于 `~/.claude/projects/` 的 JSONL 文件)批量上传到 Claude.ai 账户的云端会话历史中,实现本地旧会话与云端同步。

### 核心文件

| 文件 | 说明 |
|------|------|
| `src/commands/backfill-sessions/index.js` | 命令入口(编译产物) |

---

## 附录 VII 源码快速导航

```
测试基础设施与数据治理
│
├── VCR 录制回放
│   └── src/services/vcr.ts           ← dehydrate/hydrate + SHA1 fixture hash
│       ├── withVCR()                 ← LLM API 对话级录制
│       └── withFixture()             ← 通用 API 调用录制(token count 等)
│
├── Grove 数据治理
│   └── src/services/api/grove.ts     ← grove_enabled 同意开关,宽限期通知
│
├── Referral 邀请系统
│   └── src/services/api/referral.ts  ← Guest Pass 资格检查 + 24h 本地缓存
│
├── Agent Summary
│   └── src/services/AgentSummary/    ← 30s 定时 fork,1 句话进度摘要
│       └── agentSummary.ts
│
├── Release Notes
│   └── src/utils/releaseNotes.ts     ← 后台预取 + 文件缓存,最多展示 5 条
│
└── Session Backfill
    └── src/commands/backfill-sessions/  ← 历史本地会话上传到云端
```

---

*附录系列到此结束。*

本系列文档覆盖了 Claude Code 源码的全部主要模块:
- **第 0–16 章**:从系统概览到后台 Agent,逐层拆解核心执行链路
- **附录 I**:基础设施(Bridge/IDE、Computer Use、Worktree、Swarm)
- **附录 II**:后台智能(Magic Docs、Away Summary、投机预加载、设置同步)
- **附录 III**:安全与合规(LSP、OAuth、MDM、自动更新)
- **附录 IV**:自动化扩展(Cron、Todo、Voice、Vim)
- **附录 V**:工作区模式(Plan V2、Sandbox、Rewind、ultraplan、Teleport、Asciicast)
- **附录 VI**:用户体验(Buddy、Effort、Chrome 集成、/btw、彩蛋)
- **附录 VII**:测试与数据治理(VCR、Grove、Referral、Agent Summary)