# Migration: schema_version 1.0 → 1.1

> **Type**: MINOR（兼容；老 state 用默认值读不会崩，但跑迁移让字段语义对齐）
>
> **Status**: 这是 v1 第一份正式 migration——主要作为模板和示例。绝大多数早期用户直接从 init 出来就是 1.1，无需迁移。

---

## WHAT changed

### 删除字段

| 字段 | 旧值 | 删除原因 |
|---|---|---|
| `mode` | "cold-start" / "calibration" 二元 | 用 `calibration_samples` 整数替代——硬切换是设计者猜测，连续整数更准确 |
| `prediction_complexity` | "cold-start-simple" / "complete" | 所有预测都用统一 7 组件结构，confidence 派生自 calibration_samples |
| `bucket_scheme` | "ratio" / "absolute" / "absolute_with_ratio" / "percentile" | bucket 边界由单一算法自动派生（有 `baseline_plays` → 倍数；无 → 平台默认） |

### 新增字段

| 字段 | 默认值 | 写入者 |
|---|---|---|
| `typical_duration_seconds` | 240 | cheat-init Q1.5 派生 |
| `target_publish_cadence_days` | null（灵活节奏） | cheat-init Q1.6 派生 |
| `rubric_form_mismatch` | false | cheat-init（content_form ≠ opinion-video 时 true） |
| `benchmark_status` | "none" | cheat-init Phase 2.5 |
| `benchmark_name` | null | cheat-learn-from |
| `benchmark_sample_count` | 0 | cheat-learn-from |
| `baseline_plays` | null | cheat-init / cheat-retro |
| `enabled_perf_adapters` | `[]` | cheat-init |
| `last_published_file` | null | cheat-publish |
| `last_retro_at` | null | cheat-retro |
| `pending_retros` | `[]` | cheat-publish / cheat-retro |
| `shoots` | `[]` | cheat-shoot / cheat-publish |

---

## WHY

v1.0 的 `mode` 是离散二元，强迫"前 5 篇 cold-start vs 后续 calibration"二选一。实际频道是**连续光谱**——第 4 篇和第 6 篇没有质变。用整数 `calibration_samples` 配一张派生表（详见 [shared-references/state-management.md](../shared-references/state-management.md) 的 confidence 派生表），既保留信号又消除假断点。

类似地 `bucket_scheme` 把"bucket 怎么分"硬塞给用户决定，但用户根本没数据决定——交给算法从 baseline_plays 派生才合理。

---

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

按顺序：

1. 读 `.cheat-state.json` → 解析 JSON
2. 校验 `schema_version == "1.0"`，否则报错"当前 state 不是 1.0，无需此迁移"
3. 派生新字段值：
   - `typical_duration_seconds`: 询问用户"你的视频典型时长多少秒？"（默认 240）
   - `target_publish_cadence_days`: 询问"目标发布频率（1=日更/2=隔日/7=周更/null=灵活）"（默认 null）
   - `rubric_form_mismatch`: 检查 content_form 是否 ≠ "opinion-video"（v1.0 字段保留）→ 是则 true
   - `benchmark_status`: 默认 "none"
   - `benchmark_name`: null
   - `benchmark_sample_count`: 0
   - `baseline_plays`: 扫 `predictions/` 找含实绩的 reconstructed predictions → 取播放数中位数；找不到→ null
   - `enabled_perf_adapters`: `[]`
   - `last_published_file`: null（不能从 1.0 字段反推）
   - `last_retro_at`: null（同上）
   - `pending_retros`: 扫 `predictions/` 找有 `published_at` 但无 `## 复盘` 的 → 列表
   - `shoots`: 扫 `videos/` 找有 script.md 但对应 prediction 无 `published_at` 的 → 构造 shoot 条目
4. 删除字段：`mode`, `prediction_complexity`, `bucket_scheme`
5. 设 `schema_version = "1.1"`
6. **原子写**：写到 `.cheat-state.json.tmp` → rename
7. 报告："✅ 迁移到 1.1 完成。新增 N 字段，删除 3 字段。"

---

## Manual fallback

不想跑 `/cheat-migrate` 的用户可以手改 `.cheat-state.json`：

```diff
 {
   "schema_version": "1.0",
+  "schema_version": "1.1",
-  "mode": "cold-start",
-  "prediction_complexity": "cold-start-simple",
-  "bucket_scheme": "ratio",
+  "typical_duration_seconds": 240,
+  "target_publish_cadence_days": null,
+  "rubric_form_mismatch": false,
+  "benchmark_status": "none",
+  "benchmark_name": null,
+  "benchmark_sample_count": 0,
+  "baseline_plays": null,
+  "enabled_perf_adapters": [],
+  "last_published_file": null,
+  "last_retro_at": null,
+  "pending_retros": [],
+  "shoots": [],
   ...
 }
```

> 手改后建议跑 `/cheat-status` 验证 state 文件能被所有 skill 正常读。
