# Migration: schema_version 1.1 → 1.2

> **Type**: MINOR（兼容；老 state 用 `state.get(field, default)` 兜底不会崩，跑迁移让 shoots[] 字段对齐 schema 文档）

---

## WHAT changed

### `shoots[]` 项新增 5 个字段

| 字段 | 类型 | 默认 | 含义 |
|---|---|---|---|
| `scripts_path` | string | 从 `prediction_file` 路径派生 | pre-shoot 草稿路径 |
| `script_consistency` | enum | `"consistent"` | `"consistent"` / `"modified"` / `"lost"` — 拍时是否改稿 |
| `script_diff_pct` | int / null | `null` | 改动行占比 0-100；老 shoot 没算过则 null |
| `v2_prediction_written` | bool | `false` | prediction 文件是否已含 `## 预测 v2` 段 |
| `script_hash_at_shoot` | string / null | `null` | `videos/<id>/script.md` 的 sha256 前 12 位 |

### 新概念（不在 state 文件，在 prediction 文件 header）

`Prediction Basis` 字段：`pre_shoot`（v1 默认）/ `post_shoot_pre_publish`（v2）

---

## WHY

v1.2 引入"拍后改稿触发 v2 预测重判"——cheat-shoot 检测稿子 diff ≥30% 时调用 `/cheat-predict — mode: v2`，在原 prediction 文件 append `## 预测 v2` 段。

为了让 cheat-retro 知道"哪条预测进校准计算"，shoots[] 项必须记录是否有 v2，以及 diff 强度（用于 rubric 升级时分析"用户改稿模式"）。

---

## HOW (Claude steps for /cheat-migrate)

按顺序：

1. 读 `.cheat-state.json` → 解析 JSON
2. 校验 `schema_version == "1.1"`，否则报错"当前 state 不是 1.1，无需此迁移"
3. 对 `state["shoots"]` 列表里的每一项：
   - 若 `scripts_path` 缺失 → 从 `prediction_file` 派生（把 `predictions/` 替换为 `scripts/`）
   - 若 `script_consistency` 缺失 → 默认 `"consistent"`
   - 若 `script_diff_pct` 缺失 → 默认 `null`
   - 若 `v2_prediction_written` 缺失 → **扫对应 prediction 文件**：
     - `grep -q '^## 预测 v2' <prediction_file>` → true 则写 `true`，否则 `false`
   - 若 `script_hash_at_shoot` 缺失 → 默认 `null`
4. 设 `schema_version = "1.2"`
5. **原子写**：写到 `.cheat-state.json.tmp` → rename
6. 报告："✅ 迁移到 1.2 完成。处理了 N 个 shoots[] 项。"

---

## Manual fallback

不想跑 `/cheat-migrate` 的用户可以**完全不动 state**——所有 skills 都用 `state.get(field, default)` 兜底，新字段会自动用默认值。但 `schema_version` 字段会一直显示 `"1.1"`，SessionStart hook 会持续提示 mismatch warning。

如果想消除 warning 又不想跑 skill，手改：

```diff
 {
-  "schema_version": "1.1",
+  "schema_version": "1.2",
   ...
 }
```

shoots[] 里的字段会在下次 cheat-shoot / cheat-publish 触发时按需补全。
