{"owner":"XBuilderLAB","repo":"cheat-on-content","hasSkills":true,"totalSkillsCount":16,"totalTokensCount":84762,"categories":["anthropic-skill"],"hasMcp":false,"mcpConfig":null,"found":["SKILL.md","skills/cheat-bump/SKILL.md","skills/cheat-init/SKILL.md","skills/cheat-learn-from/SKILL.md","skills/cheat-migrate/SKILL.md","skills/cheat-persona/SKILL.md","skills/cheat-predict/SKILL.md","skills/cheat-publish/SKILL.md","skills/cheat-recommend/SKILL.md","skills/cheat-retro/SKILL.md","skills/cheat-score-blind/SKILL.md","skills/cheat-score/SKILL.md","skills/cheat-seed/SKILL.md","skills/cheat-shoot/SKILL.md","skills/cheat-status/SKILL.md","skills/cheat-trends/SKILL.md"],"skills":{"SKILL.md":"---\nname: cheat-on-content\ndescription: 给所有想把\"感觉\"变成可校准预测的内容创作者。**方法论通用**——打分 → 盲预测 → T+3d 复盘 → 进化 rubric 的循环适用任何能被量化（播放 / 阅读 / 收听 / 点击）的内容。**rubric 是循环的内容，不是循环本身**——当前内置一份观点视频 rubric（参考博主 25+ 视频拟合），其他形态可借这套起步并 bump 调权重。**强烈建议导入对标账号**作为初始信号源（/cheat-learn-from）。触发词：\"初始化\"/\"打分这篇\"/\"启动预测\"/\"已发布\"/\"复盘\"/\"升级 rubric\"/\"推荐选题\"/\"抓热点\"/\"状态\"/\"找对标\"/\"learn from\"。**首次使用必须先跑 /cheat-init。**\nargument-hint: \"[draft-path] [— mode: cold-start|calibration]\"\nallowed-tools: Bash(*), Read, Write, Edit, Grep, Glob, Skill, mcp__llm-chat__chat\n---\n\n# 网红作弊器 / Cheat on Content\n\n> 🎯 **方法论通用，rubric 当前内置观点视频版**\n>\n> **方法论**（5 阶段闭环）：任何能被量化的内容形态都适用——视频 / 文章 / 播客 / Newsletter / 短文 thread。\n>\n> **当前内置 rubric**：观点类视频（评论 / 时评 / 论说 / 议题讨论 / 个人观点表达），7 个维度由参考博主 25+ 已发样本拟合而来。如果你做其他形态，需要：\n> - 自己写一份 rubric（参照 [starter-rubrics/opinion-video-zero.md](starter-rubrics/opinion-video-zero.md) 的格式）\n> - 或等内置扩展（长文 / 短文 / 播客 starter 在批次 3 路线图）\n>\n> 默认假设：**用户是从零开始的新人**（一条视频都没发过）——cold-start 期的预测会**简化**，只要 7 维打分 + 一句话 bet，不强求 bucket 数字（避免 false precision）。已有 5+ 篇数据的老手走 calibration 模式解锁完整 7 组件预测。\n\n把内容创作变成可校准预测循环：**打分 → 预测 → 发布 → 复盘 → 进化 rubric**。\n\n本文件是**总协议 + 路由器**。具体每个阶段的工作流在 `skills/cheat-*/SKILL.md` 各子 skill 里。\n\n## Codex compatibility\n\nCodex 没有 Claude Code 的 slash-command harness。安装到 Codex 后，按自然语言触发同一套路由即可：\n\n- `初始化 cheat-on-content` → 读取并执行 `skills/cheat-init/SKILL.md`\n- `打分这篇 scripts/foo.md` → 读取并执行 `skills/cheat-score/SKILL.md`\n- `启动预测 scripts/foo.md` → 读取并执行 `skills/cheat-predict/SKILL.md`\n- `拍了 ...` / `已发布 ...` / `复盘 ...` / `升级 rubric` / `状态` → 分别读取对应 `skills/cheat-*/SKILL.md`\n\n执行时遵循本文件的三条原则和路由表；不要依赖 `/cheat-*` 命令是否存在。Claude Code 专用 hook（`.claude/settings.json`）仍只在 Claude Code 里自动触发；Codex 中需要用户主动说 `状态` 查看 buffer、待复盘和候选池。\n\n---\n\n## 三条不可妥协原则\n\n任何一条被违反，整个校准循环退化为\"凭直觉的自我安慰\"。如果用户要求打破其中任何一条，**拒绝执行并说明原因**。\n\n1. **盲预测（Blind prediction）**：预测必须在看到任何实际数据**之前**写完。一旦写完，`## 预测` 段是 immutable——只能往 `## 复盘` 段追加。完整规范：[shared-references/blind-prediction-protocol.md](shared-references/blind-prediction-protocol.md)。**hooks/prediction-immutability.sh 在 harness 层强制执行**。\n\n2. **升级 = 全量重打（Bump = full re-score）**：rubric 升级时，校准池所有有实绩数据的样本必须用新公式重打分；新排序与实际表现排序若在 ≥4/5 样本上不一致，升级被拒；升级必须经跨模型独立审核。完整规范：[shared-references/bump-validation-protocol.md](shared-references/bump-validation-protocol.md)。\n\n3. **rubric 是工作台，不是博物馆**：被新数据推翻或被吸收为正式维度的观察，**删掉**。绝不留\"我曾经以为 X，但其实...\"的考古层。git history 才是档案。完整规范：[shared-references/observation-lifecycle.md](shared-references/observation-lifecycle.md)。\n\n---\n\n## 路由表（触发词 → 子 skill）\n\n| 用户说 | 调用 | 前置条件 |\n|---|---|---|\n| \"初始化\" / \"init\" / \"首次使用\" | `/cheat-init` | 无（这是入口） |\n| \"找对标\" / \"学这个账号\" / \"拆这几个对标视频\" / \"learn from\" / \"导入对标账号\" | `/cheat-learn-from` | 已 init；cold-start 强烈建议；后续可随时 --append / --replace |\n| \"找选题\" / \"我不知道拍什么\" / \"seed\" / \"找前 5 个选题\" | `/cheat-seed` | 已 init（cold-start 用户专用一次性种子动作） |\n| \"打分这篇 [path]\" / \"score this [path]\" | `/cheat-score` | rubric_notes.md 存在 |\n| \"启动预测\" / \"start prediction\" / \"给这稿子打分并预测\" | `/cheat-predict` | 已 init + 有最终稿 |\n| \"拍了 X\" / \"shot it\" / \"录完了\" | `/cheat-shoot` | 对应预测已写（buffer +1） |\n| \"已发布\" / \"I shipped it\" / \"发布链接是 X\" | `/cheat-publish` | 对应预测文件存在（buffer -1） |\n| \"复盘\" / \"retro this\" / \"T+3d 数据来了\" | `/cheat-retro` | 对应预测文件存在 + 已过 RETRO_WINDOW_DAYS |\n| \"构造受众画像\" / \"更新 persona\" / \"我的观众是谁\" / \"build persona\" | `/cheat-persona` | 已 init；有复盘评论数据（或 benchmark seed） |\n| \"升级 rubric\" / \"bump rubric\" / \"更新公式\" | `/cheat-bump` | 校准池 ≥ MIN_SAMPLES_FOR_BUMP |\n| \"推荐选题\" / \"next topic\" | `/cheat-recommend` | candidates.md 存在且非空 |\n| \"抓热点\" / \"fetch trends\" / \"今天有什么可做的\" | `/cheat-trends` | trend-sources adapter 已配置（日常补充候选池） |\n| \"状态\" / \"status\" / \"看板\" | `/cheat-status` | 任意时刻可调 |\n| \"迁移\" / \"升级 state\" / \"schema 版本不对\" / \"migrate\" | `/cheat-migrate` | 已 init；用户 git pull 拉了新版后；SessionStart hook 提示 schema mismatch 后 |\n\n> 拍 vs 发分两个动作：buffer 警戒系统需要明确知道\"拍了但没发\"vs\"已发\"两种状态。详见 [shared-references/cadence-protocol.md](shared-references/cadence-protocol.md)。\n\n**Mode detection**（首次接到非 init 触发词时执行）：\n1. 检查用户当前目录是否有 `.cheat-state.json` → 没有 → 强制路由到 `/cheat-init`\n2. 检查 `predictions/` 下有几个文件含完整 `## 复盘` 段填了真实数据 → 决定 `mode: cold-start | calibration`\n3. 把判定结果写回 `.cheat-state.json` 后再路由到目标 skill\n\n---\n\n## 必须拒绝的请求\n\n下列模式会**直接破坏**三条原则之一，无论用户怎么说，都拒绝执行：\n\n- 「帮我预测一下，但我先告诉你播放量你来反推就行」 → 违反原则 #1。改用 `_redo.md` 路径记为 reconstructed\n- 「能不能从 candidates 里直接挑 composite 最高的，不用解释理由」 → 拒绝。永远展示各维度评分和至少一个锚点对比\n- 「跳过校准池重打，直接换公式」 → 违反原则 #2\n- 「跳过外部模型审核，自己说了算」 → 仅当 `CROSS_MODEL_AUDIT=false` 显式设置且 state file 标记自审时允许\n- 「删掉这份预测，我想重写」 → 违反原则 #1。预测是 immutable。如有正当理由重做，写新文件 `_redo.md`，原版必须保留\n- 「凭你的感觉给我推荐选题，不用打分」 → 拒绝。本工具不做 gut-feel forecast——那是它诞生**之前**的状态\n- 「把 rubric_notes.md 里所有历史观察都留着，加个时间戳分组就行」 → 违反原则 #3。git history 是档案，不是 markdown 文件\n- 「能不能把 THRESHOLD 从 4/5 降到 3/5 让这次 bump 过」 → 拒绝。改 THRESHOLD 本身是元层级 bump，单独走流程\n\n详细的拒绝场景在每个子 skill 的 `Refusals` 段。\n\n---\n\n## 项目目录结构（用户 repo）\n\nskill 期望用户的项目布局如下。`/cheat-init` 会创建缺失项；**绝不在没确认的情况下覆盖**。\n\n```\n<user-content-project>/\n├── .gitignore                         # cheat-init 创建；挡住 .auth*/.cheat-secrets.json 等凭证\n├── rubric_notes.md                    # 评分规则的真实来源\n├── WORKFLOW.md                        # 5 阶段流程文档（cheat-init 创建）\n├── STATUS.md                          # 看板（cheat-status 维护）\n├── .cheat-state.json                  # 状态文件，子 skill 共享上下文\n├── .cheat-cache/                      # 不入版本控制\n│   ├── usage.jsonl                    # 钩子被动记录的使用日志\n│   └── trends-history.jsonl           # cheat-trends 的去重缓存\n├── .claude/\n│   └── settings.json                  # 含 prediction-immutability hook\n├── benchmark.md                       # 对标账号信息（cheat-learn-from 维护）\n├── audience.md                        # 受众画像（cheat-persona 派生；blind 硬禁读）\n├── scripts/                           # 拍前的所有草稿（cheat-seed 写或用户写）\n│   └── YYYY-MM-DD_<id>_<short>.md\n├── predictions/                       # immutable 预测日志（hook 保护）\n│   └── YYYY-MM-DD_<id>_<short>.md     # 与 scripts/ 同 id\n├── videos/                            # 拍后才建（cheat-shoot 创建）\n│   └── YYYY-MM-DD_<id>_<short>/\n│       ├── script.md                  # 用户提供的最终拍摄稿（cheat-shoot 时询问\"和 scripts/ 一致吗\"）\n│       └── report.md                  # T+3d 抓的数据 + 评论（cheat-retro 写）\n├── samples/                           # 对标账号视频 / 转录（cheat-learn-from 创建）\n│   └── <账号名>/<video-id>/{source.mp4 (可选), transcript.md, meta.md}\n├── candidates.md                      # 选题池（可选）\n└── content.db                         # 可选 SQLite，校准池规模化后启用\n```\n\n---\n\n## 文件清单\n\n### 本 skill 包\n\n```\ncheat-on-content/\n├── SKILL.md                           # 本文件（总协议 + 路由）\n├── README.md                          # 营销门面\n├── skills/                            # 子 skill 集\n│   ├── cheat-init/SKILL.md            # ✅ 入口：onboarding 与脚手架\n│   ├── cheat-learn-from/SKILL.md      # ✅ 对标账号导入（拆 pattern + 派生 base rubric 信号）\n│   ├── cheat-seed/SKILL.md            # ✅ Cold-start 选题启动器（brainstorm + 可选 draft）\n│   ├── cheat-score/SKILL.md           # ✅ 单稿打分（不写文件）\n│   ├── cheat-predict/SKILL.md         # ✅ 盲预测 + immutable 日志\n│   ├── cheat-shoot/SKILL.md           # ✅ 登记拍摄（buffer +1）\n│   ├── cheat-publish/SKILL.md         # ✅ 发布元数据登记（buffer -1）\n│   ├── cheat-retro/SKILL.md           # ✅ 数据回收 + 复盘\n│   ├── cheat-persona/SKILL.md         # ✅ 受众画像派生（从复盘评论聚类）\n│   ├── cheat-bump/SKILL.md            # ✅ rubric 升级（含跨模型审）\n│   ├── cheat-recommend/SKILL.md       # ✅ 候选池排序推荐（按 buffer 颜色 + 1 稳 + 1 实验）\n│   ├── cheat-trends/SKILL.md          # ✅ 热点抓取（日常补充候选池，多 adapter）\n│   ├── cheat-status/SKILL.md          # ✅ 状态看板（含 buffer 警戒）\n│   ├── cheat-migrate/SKILL.md         # ✅ schema 升级（老用户 git pull 后用）\n│   └── cheat-score-blind/SKILL.md     # ✅ Channel B 隔离打分 sub-agent（仅 Task tool 调用）\n├── migrations/                        # schema 演进单一来源\n│   ├── registry.md                    # ✅ LATEST_SCHEMA + 版本链表\n│   └── <from>-to-<to>.md              # ✅ 每步迁移的 WHAT/WHY/HOW/Manual fallback\n├── shared-references/                 # 跨 skill 共享协议\n│   ├── blind-prediction-protocol.md   # ✅ 原则 #1\n│   ├── bump-validation-protocol.md    # ✅ 原则 #2\n│   ├── observation-lifecycle.md       # ✅ 原则 #3\n│   ├── prediction-anatomy.md          # ✅ 一份合格预测的 7 个组件\n│   ├── candidate-schema.md            # ✅ 候选项统一 schema\n│   ├── cadence-protocol.md            # ✅ 节奏协议（buffer 警戒 + 选题策略）\n│   ├── state-management.md            # ✅ .cheat-state.json 读写约定\n│   └── migration-protocol.md          # ✅ schema 演进哲学 + maintainer checklist\n├── starter-rubrics/                   # 各内容形态的先验 rubric\n│   ├── opinion-video.md               # ✅ 观点视频（中文，已校准 25+ 样本）\n│   ├── opinion-video-zero.md          # ✅ v0 等权占位（cold-start）\n│   ├── long-form-essay.md             # ⬜ 公众号 / Substack\n│   └── short-form-text.md             # ⬜ X thread / 微博长文\n├── templates/                         # skill 写进用户 repo 的文件骨架\n│   ├── gitignore.template             # ✅ 用户项目 .gitignore（护凭证，保留 predictions/ 入库）\n│   ├── rubric_notes.template.md       # ✅\n│   ├── prediction.template.md         # ✅ 统一版（所有阶段，含 confidence header）\n│   ├── retro.template.md              # ✅\n│   ├── candidates.template.md         # ✅\n│   ├── candidates.template.json       # ✅\n│   ├── script_patterns.template.md    # ✅ 写作 pattern 沉淀（含 benchmark 借鉴段说明）\n│   ├── benchmark.template.md          # ✅ 对标账号 reference\n│   ├── audience.template.md           # ✅ 受众画像骨架\n│   ├── workflow.template.md           # ✅\n│   ├── status.template.md             # ✅\n│   └── content.db.schema.sql          # ✅\n├── hooks/                             # harness 强制层\n│   ├── prediction-immutability.json   # ✅ 阻塞型钩子（拦预测段编辑）\n│   ├── prediction-immutability.sh     # ✅ 拦截脚本\n│   ├── session-start.json             # ✅ SessionStart 自动报告 hook\n│   ├── session-start.sh               # ✅ 状态报告渲染脚本\n│   ├── meta-logging.json              # ✅ 被动记录配置\n│   └── log-event.sh                   # ✅ meta-logging 脚本\n├── tools/                             # 独立 CLI 脚本\n│   ├── score-curve.py                 # ⬜ 预测精度收敛曲线\n│   ├── md-to-sqlite.py                # ⬜ markdown → content.db 升级（批次 3）\n│   └── validate-bump.py               # ⬜ 校准池全量重打（批次 3）\n├── adapters/                          # 数据源适配\n│   ├── perf-data/                     # 复盘数据源（含 douyin-session）\n│   ├── candidate-pool/                # 候选池数据源\n│   ├── trend-sources/                 # 热点抓取源\n│   └── script-extraction/             # 视频/音频转 script（含 whisper for cheat-learn-from）\n└── examples/\n    ├── reference-implementation/      # 视频分析脱敏快照（待）\n    └── script_patterns.example.md     # script_patterns 全填示例（参考用，不复制）\n```\n\n✅ = 当前批次（v1 骨架）已完成 / ⬜ = 后续批次\n\n---\n\n## Tone & voice\n\n写面向用户的文案（commit message / 复盘小结等）时，匹配项目的 **直白克制（reflective-irreverent）** voice：\n\n- 直接说出失败：「composite 8.47 但实际只有 16.8w——rubric 高估了 SR」\n- **不要**用模糊措辞软化：「这或许可能在某种程度上暗示...」——别这么写\n- Cluely 风格的反叛 hook 只在 README 出现——**不要写进** `rubric_notes.md` 或预测日志\n\n---\n\n## 给开发者：扩展本 skill\n\n- 新增内容形态 → 加 `starter-rubrics/<form>.md`\n- 新增热点抓取源 → 加 `adapters/trend-sources/<name>.md`，符合 [candidate-schema.md](shared-references/candidate-schema.md) 输出契约\n- 修改原则 → 改 `shared-references/<protocol>.md`，所有引用它的 skill 自动跟进\n- 修改路由 → 改本文件的\"路由表\"段\n- 子 skill 内部细节 → 直接改对应 `skills/cheat-*/SKILL.md`\n\n完整开发指南见 README.md。\n","skills/cheat-bump/SKILL.md":"---\nname: cheat-bump\ndescription: 提议并执行 rubric 或 bucket 升级。两种模式：**完整 rubric bump**（最高风险动作，5 步强制 + 跨模型审核）和 **--bucket-only 轻量重校**（只换 bucket 边界，不动 rubric 公式）。**Phase 2 强制走 cheat-score-blind sub-agent 给校准池重打分**——不接受 self-scored fallback。触发词：\"升级 rubric\"/\"bump rubric\"/\"更新公式\"/\"我想加一个维度\"/\"调整权重\"/\"重校桶\"/\"recalibrate bucket\"。\nargument-hint: --propose \"<...>\" | --bucket-only [--scheme ratio|absolute|percentile]\nallowed-tools: Bash(*), Read, Write, Edit, Glob, Grep, Skill, Task, mcp__llm-chat__chat\n---\n\n# /cheat-bump — Rubric / Bucket 升级\n\n两种模式：\n\n| 模式 | 触发 | 做什么 | 验证强度 |\n|---|---|---|---|\n| **完整 rubric bump** | `--propose \"<新公式>\"` | 改公式 / 维度 / 权重 | 5 步 + 跨模型审核（强制） |\n| **bucket-only 重校** | `--bucket-only` | 只重新派生 bucket 边界 | 数据自动派生，无审核 |\n\n完整 rubric bump 严格遵守 [shared-references/bump-validation-protocol.md](../../shared-references/bump-validation-protocol.md) 的 5 步。bucket-only 走轻量路径——见下方 Phase B。\n\n## Overview\n\n```\n入口：用户触发 /cheat-bump\n  ↓\n[Phase A0: 检测调用模式]\n  ↓\n  ├─ --bucket-only  →  [Phase B: 轻量 bucket 重校]\n  └─ --propose      →  [Phase 0~6: 完整 rubric bump]\n```\n\n## Phase A0: 调用模式分流（先做）\n\n读用户参数：\n- 含 `--bucket-only` → 走 **Phase B**（轻量重校）\n- 含 `--propose \"<...>\"` → 走 **Phase 0~8**（完整 rubric bump）\n- 都没有 → 询问用户：\"你想做什么？1) 调 rubric 公式 / 加减维度 → --propose；2) 只重新派生 bucket 边界 → --bucket-only\"\n\n如果用户说\"我觉得 ER 太低了想调\"→ 是 `--propose` 路径。\n如果用户说\"我账号长大了，bucket 不准了\"→ 是 `--bucket-only` 路径。\n**两条路径不能混调**——一次操作只做一种事。\n\n---\n\n## 完整 rubric bump 流程\n\n```\n[用户：升级 rubric --propose \"ER×1.5→2.0，砍 NA，加 MS\"]\n  ↓\n[Phase 0: 前置门槛检查]\n  ↓\n[Phase 1: 写出新公式完整方程]\n  ↓\n[Phase 2: 校准池全量重打分]\n  ↓\n[Phase 3: 计算排序一致性]\n  ↓\n[Phase 4: 跨模型独立审核（强制）]\n  ↓\n[Phase 5: 落地 + cleanup pass]\n  ↓\n[Phase 6: 更新所有校准样本的 prediction 文件底部追加 Re-scored 行]\n```\n\n## Constants\n\n- **READINESS_HEURISTIC** —\n  - **默认参考**：校准池 ≥ 5 样本 + 至少 1 个跨样本观察有 ≥3 样本支持\n  - **但 Claude 可以提议 bump**（即使样本少）如果观察信号特别强：\n    - N=3 但出现完全推翻当前 rubric 假设的强反例（composite 8.5 vs 实绩 5w 这种 ≥3x 偏差）\n    - 1 篇出现单点但极强的现象（如评论区出现 ≥2000 赞的单一模因）\n  - **Claude 也可以拒绝 bump**（即使样本足）如果证据弱：\n    - N=10 但观察都是低置信度的零碎 pattern，无清晰方向\n    - 用户复盘时大量\"随便看了下\"的非严肃判断\n  - **写在 prediction header 或 cheat-bump 输出时必说明**：本次提议是 default-aligned 还是 judgment-driven，给用户审视依据\n- **THRESHOLD = 0.8** — 新排序与实绩排序一致性阈值（4/5）。这条**写死**——bump 验证的统计刚性\n- **CROSS_MODEL_AUDIT = true** — 调外部 LLM 独立审核。false 仅用于离线\n- **REQUIRE_CONFIRM = true** — 落地前要求用户明确\"yes, bump\"\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `--propose` 文本 | 用户参数；缺失则询问 |\n| `rubric_notes.md` | 用户项目根 |\n| `predictions/*.md` 全量 | 校准池数据 |\n| `.cheat-state.json` | 状态 |\n\n## Workflow\n\n### Phase 0: 前置门槛检查\n\n按 [bump-validation-protocol.md](../../shared-references/bump-validation-protocol.md) 的\"何时禁止\"段，逐项检查：\n\n| 检查 | 失败处理 |\n|---|---|\n| 校准池总样本数 vs 观察强度 | **Claude 判断**——按 READINESS_HEURISTIC：默认 ≥5 样本但允许特例（强反例 / 强模因）。如不满足默认，Claude 必须**显式说明**为什么仍然提议 bump（\"虽然只 N=3 样本，但 X 这条出现 composite Y vs 实绩 Z，这是 W 倍偏差\"），让用户审视 |\n| 上次 bump 距今的新校准数 vs 观察成熟度 | **Claude 判断**——默认建议 ≥3 篇新样本，但如果连续 3 篇都强证据指向同一方向 → 不必再等 |\n| `in_progress_session == null` | 拒绝：\"你有 in-progress 预测未完成。先走完那条流程或清掉 state\" |\n| 触发条件成立（系统性偏差 / 跨样本新观察 / 新维度证据足） | 警告但不阻塞——询问用户为什么现在 bump |\n\n通过 → 进入 Phase 1。\n\n### Phase 1: 写出新公式完整方程\n\n**不能只接受用户的简短描述**。把它展开为完整方程：\n\n```\n当前：v2  composite = (ER×1.5 + SR×1.5 + HP×1.5 + QL + NA + AB + SAT) / 8.5 × 2.0\n提议：v2.1  composite = (ER×2.0 + HP×1.5 + MS×1.5 + QL + SR + TS + SAT) / 9.0 × 2.0\n\n变化总结：\n- ER ×1.5 → ×2.0（升）\n- SR ×1.5 → ×1.0（降）\n- 新增 MS ×1.5（Memetic Shareability）\n- 新增 TS ×1.0（Topic Shareability）\n- 删除 NA（与 HP 重叠）\n- 删除 AB（被 TS 替代）\n- 归一化常数 8.5 → 9.0\n- 公式总维度数：7 → 7（净变化 0）\n```\n\n如果用户的提议含糊（如\"ER 权重提一点\"）→ 询问具体数值，**禁止自己猜**。\n\n### Phase 2: 校准池全量重打分（**强制走 blind sub-agent**）\n\nGlob `predictions/*.md` 中所有有完整复盘段的文件 → 校准池。\n\n**bump 是工具最高风险动作——所有重打必须走 [cheat-score-blind](../cheat-score-blind/SKILL.md) sub-agent**。inline 重打 = 主 Claude 已经看过实绩，rank 一致性变成 overfit 而非真信号。\n\n#### 强制约束\n\n- **不接受 self-scored fallback**——`/cheat-predict` 有 `--skip-blind` flag，但 `/cheat-bump` **没有**。如果 Task tool 不可用 → **abort bump**，向用户报告\"先解决 Task tool 再 bump\"\n- **不接受\"我只重算 composite 不重打 dim\"** —— 即使新公式只调权重不加维度，每条 prediction 的所有 dim 都要由 sub-agent 重新审 script。理由：旧 dim 分本身可能是污染的；权重变了不能保证旧 dim 还成立\n\n#### 对每篇 prediction：\n\n1. 解析 prediction 文件拿到对应 `scripts/<id>.md` 路径（从 `Script Path` header 字段）\n2. 校验 script 文件存在 + hash 跟 header `Script Hash` 一致；不一致 → 警告（script 改过了）但仍 spawn sub-agent\n3. **通过 Task tool spawn cheat-score-blind sub-agent**：\n   ```\n   Spawn cheat-score-blind sub-agent.\n\n   Input:\n     script_path: <prediction header 的 Script Path>\n     rubric_notes_path: rubric_notes.md\n     sidecar_path: .cheat-cache/bump-rescores/<prediction-id>.json\n\n   Task: 按 rubric_notes 当前公式（已是新版 vN+1）给 script 打分。\n   返回严格 JSON。写 sidecar 文件用于 bump 主流程批量读取。\n\n   不要读 state file / predictions/ / videos/ 任何其他文件。\n   不要询问用户 —— 你没有用户。\n   不要读这份 prediction 文件本身 —— 你只看 script + rubric。\n   ```\n4. 等 sub-agent 完成 → 读 sidecar JSON → 主流程用新公式算 composite\n5. 写\"重打表\"到 `.cheat-cache/bump-rescores.json`（汇总）。**每条 entry 标 `blind: true`** —— bump phase 5 cleanup 时把这个字段连同新分数写到 prediction 文件的 `Re-scored under v<N+1>` 行\n\n#### 还污染没污染的诚实标注\n\n即使走 sub-agent，**仍有两类残余 contamination 要在 bump report 里诚实标注**：\n\n| 类型 | 来源 | 标注字段 |\n|---|---|---|\n| 模型 prior contamination | sub-agent 仍是 Claude，RLHF 共享 | `model_prior_warning: true`（默认 true，不可关） |\n| 用户自己 rubric design bias | rubric_notes.md 是用户写的，自然 fit 自己内容 | `rubric_self_designed: true`（默认 true，不可关） |\n\n这两条提示用户 channel C（跨模型 audit）的不可省。bump 报告末尾必印：\"上面的 rank 一致性是 channel A 内的一致性。**最终决策必须等 channel C audit 通过**。\"\n\n#### 失败模式\n\n| 症状 | 处理 |\n|---|---|\n| 某条 prediction 的 script 文件不见了 | sub-agent skip 该条，主流程汇总报告\"N 条因 script 缺失被排除\"。如剩余有效池 < MIN_SAMPLES → abort bump |\n| sub-agent 返回 `refusal != null` | 重发 Task 最多 3 次；仍败 → 该条标 `rescore_failed: true` 排除出校准池 |\n| Task tool 整个不可用 | abort bump，提示用户\"Task tool 是 bump 的硬依赖。如真的离线环境，跑 `/cheat-bump --bucket-only` 走轻量分支\" |\n| sub-agent 输出含 contamination_signal | 标 `suspicious: true` 但不排除——bump report 末尾列这些可疑条目让用户审 |\n\n### Phase 3: 计算排序一致性\n\n```\n每个样本：\n  new_composite_rank: 用新公式排序的 rank\n  actual_plays_rank: 用实际播放排序的 rank\n  delta: |new_rank - actual_rank|\n\n输出对照表：\n| 样本 | composite (v2) | composite (v2.1) | rank (new) | actual | rank (actual) | delta |\n|---|---|---|---|---|---|---|\n| 仓鼠 | 9.41 | 9.55 | 1 | 124.8w | 1 | 0 |\n| 停止期待 | 8.24 | 9.11 | 2 | 71.1w | 2 | 0 |\n| 老板废话 | 7.65 | 8.11 | 4 | 39.6w | 3 | 1 |\n| 求职悖论 | 8.47 | 7.56 | 5 | 16.8w | 4 | 1 |\n| 谁问你了 | 8.24 | 7.00 | 6 | 11.7w | 5 | 1 |\n\n排序一致性：4/5 在 |delta| ≤ 1\nPairwise no-regression：旧公式做对的所有 pair 在新公式下未颠倒 ✓\n```\n\n判定：\n- 排序一致性 < THRESHOLD（默认 0.8） → **本地拒绝**，转 Phase 4 之前明确报告失败\n- pairwise 出现回归 → **本地拒绝**\n\n`THRESHOLD` 写死在协议里——不允许临时调低（那本身是另一个需要 bump 的元决策）。\n\n### Phase 4: 跨模型独立审核（**强制**，除非 escape hatch）\n\n`CROSS_MODEL_AUDIT=true`（默认）：\n\n调用 `mcp__llm-chat__chat`：\n\n```\nprompt:\n你是一个独立审稿人。下面是一个内容创作者准备升级的 rubric 公式。\n请独立判定两件事：\n1. 排序一致性：新公式给样本的排序与实际表现排序，是否真的在 ≥80% 样本上一致？\n2. 解释力：新公式相比旧公式，是否更好地解释了校准池的实绩分布？\n\n数据：\n旧公式：(ER×1.5 + SR×1.5 + HP×1.5 + QL + NA + AB + SAT) / 8.5 × 2.0\n新公式：(ER×2.0 + HP×1.5 + MS×1.5 + QL + SR + TS + SAT) / 9.0 × 2.0\n\n校准池：\n[Phase 2 重打表的完整 JSON]\n\n排序对照：\n[Phase 3 表格的完整 JSON]\n\n输出格式：\n- 判定：PASS 或 REJECT\n- 理由：≥100 字\n- 关键风险：[如有，列出新公式的潜在问题]\n```\n\n收到外部 LLM 回复 → 解析判定。\n\n判定逻辑：\n- 本地 PASS + 外部 PASS → 通过，进入 Phase 5\n- 本地 PASS + 外部 REJECT → **视为 REJECT**。冲突意味着至少一方解读不稳定\n- 本地 REJECT → 已在 Phase 3 终止\n- mcp__llm-chat__chat 不可用 → 优雅降级到 `CROSS_MODEL_AUDIT=false`，state file 标 `last_bump_self_audited: true`\n\n`CROSS_MODEL_AUDIT=false`：\n- 仅依赖本地判定\n- state file 持续标记，cheat-status 持续提示用户\"这次 bump 是自审，建议配置 mcp__llm-chat__chat\"\n\n### Phase 5: 落地 + cleanup pass\n\n通过审核后，**REQUIRE_CONFIRM=true** → 询问用户：\"新公式 PASS 本地与外部审核。最后确认：执行 bump 落地？这会修改 rubric_notes.md + rubric-memo.md 并删除若干已被吸收的观察。回答 'yes, bump' 才执行。\"\n\n用户确认后：\n\n#### 5a. 更新 `rubric_notes.md`（**只放通用语言，不含视频名 / 实绩**）\n\n- 顶部 metadata 更新：\n  - `**当前版本**: vN+1`\n  - `**Last bumped at**: <ISO 8601>`\n  - `**Upgrade memos**: 见 [rubric-memo.md](rubric-memo.md)`（指针，不复制 Memo 内容）\n- 版本速查表加一行（只含版本号 + 公式签名，**不含**证据样本）\n- 更新\"当前评分维度\"段（删 NA / AB，加 MS / TS）\n- **派生证据段** 如新维度需要锚点解释 → **用通用语言**：\n  - ✅ 允许：「派生证据：高抽象密度样本 → CC=1 → 低 reach」\n  - ❌ 禁止：「派生证据：「停止期待」CC=1 → 实绩 13.7w」（视频名 + 实绩 数字）\n  - 命中违禁 pattern → 把该段抽到 rubric-memo.md 的\"派生证据\"子段，原位用通用语言替代\n\n#### 5b. 写 Memo 到 `rubric-memo.md`（**append 模式，不覆盖历史**）\n\n按 [bump-validation-protocol.md](../../shared-references/bump-validation-protocol.md) Step 5 + [templates/rubric-memo.template.md](../../templates/rubric-memo.template.md) 格式 append 一段 Memo 到文件末尾：\n\n- 触发观察（含真实观察 ID）\n- 证据数据（**校准池重打表 + 排序对照，含真实视频名 + 实绩**）\n- 派生证据（**含真实样本名 + 实绩**）\n- 诊断\n- 新公式\n- 跨模型审核结论引用（含模型名 + 判定 + 理由摘录）\n- 已知局限\n\n**绝不**覆盖 rubric-memo.md 已有内容——bump memo 按时间顺序累积。\n\n#### 5c. cleanup pass（按 [observation-lifecycle.md](../../shared-references/observation-lifecycle.md) 的\"cleanup pass 强制时机\"）\n\n在 `rubric_notes.md` 内执行（**不**动 rubric-memo.md）：\n\n- 已被吸收为新维度的观察 → 删（如观察 E 被吸收为 MS → 删观察 E）\n- 被新数据推翻的观察 → 删\n- 仍未解决的观察 → 迁移到新版本\"待验证假设\"段\n- 已被验证的\"规律\"→ 移到\"规律沉淀区\"\n\n#### 5d. 整理 + 自检\n\n- 重新读 `rubric_notes.md` 全文，确保读者能在 60 秒内理解当下规则——超出 600 行触发额外清算\n- **自检 leak guard**：对 `rubric_notes.md` 跑 `grep -E '\\\\d+\\\\s*[wWmMkK万]|播放|实绩|实际'` → 如有命中 → **abort bump + 回滚**，提示用户\"rubric_notes.md 写入了违禁内容（实绩 / 播放数）\"。这些内容应在 rubric-memo.md，不在 rubric_notes.md\n\n### Phase 6: 校准样本批量更新\n\n对每个校准样本的 prediction 文件，**底部追加**（不动预测段、不动复盘段）：\n\n```markdown\n\n---\n**Re-scored under v2.1 on 2026-05-04**: composite=8.24 → 9.11 (blind: true)\n（rubric bump 时全量重算，由 cheat-score-blind sub-agent 独立打分；详见 rubric-memo.md 的 v2 → v2.1 升级 Memo）\n```\n\n`blind: true` 字段**必填**——告诉未来读这条记录的人\"这是 channel B 隔离打分，不是主 Claude 自评\"。如果某条 prediction 在 Phase 2 因 sub-agent 失败被排除 → 不会有 Re-scored 行（保持原样）。\n\n用 Edit 工具，匹配每个文件的最末尾。\n\n### Phase 7: 更新 state file\n\n```json\n{\n  \"rubric_version\": \"v2.1\",\n  \"last_bump_at\": \"<ISO timestamp>\",\n  \"last_bump_self_audited\": false,\n  \"consecutive_directional_errors\": [],\n  \"calibration_samples_at_last_bump\": <current value>\n}\n```\n\n清空 `consecutive_directional_errors`——新 rubric 重新计数。\n\n### Phase 8: 控制台报告\n\n```\n✅ Rubric 已升级 v2 → v2.1\n\n变化：\n- ER ×1.5 → ×2.0\n- SR ×1.5 → ×1.0\n- 新增 MS / TS\n- 删除 NA / AB\n\n校准池重打：5/5 通过排序检查（4/5 一致 + 0 pairwise 回归）\n跨模型审核：✅ PASS\nCleanup pass：删除观察 D 和 E（已吸收为 QL 重定义和 MS 维度）\n\n下一篇预测起按 v2.1 公式打分。\n所有历史预测文件已追加 Re-scored 标记。\n```\n\n---\n\n## Phase B：bucket-only 重校（轻量分支）\n\n`/cheat-bump --bucket-only [--scheme ratio|absolute|percentile]`\n\n**与完整 bump 的本质区别**：bucket 边界不是规则的一部分，是数据派生量。重新派生它**不需要跨模型审核**——派生算法是确定性的，没有\"判断\"成分。\n\n### B1: 选择算法（按可用样本数自动派生，**state 不存 scheme**）\n\n| 算法 | 适用 | 边界派生方式 |\n|---|---|---|\n| `ratio`（默认 N=1-4） | 小样本 | 上一篇 / 最近 3 篇中位数 × {0.3 / 1 / 3 / 10 / 30} |\n| `absolute`（默认 N=5-9）| 中等样本 | 校准池中位数 × {0.3 / 1 / 3 / 10 / 30}，固定边界 |\n| `percentile`（默认 N≥10）| 大样本 | 校准池实绩 percentile {30 / 60 / 85 / 95 / 100} |\n\n`--scheme` 参数允许用户**显式覆盖默认**：\n- `--scheme ratio` 强制用 ratio（即使 N≥5）\n- `--scheme absolute` 强制用 absolute\n- `--scheme percentile` 强制用 percentile（要求 N≥3，否则报错）\n\n未指定 `--scheme` → 按上表自动派生。\n\n> 旧设计有 `bucket_scheme` state 字段——v1.1 删了。所有 skill 实时按 calibration_samples 派生算法，不需要持久化\"当前用哪个\"。这避免了\"切换 scheme 后忘了同步\"的状态不一致问题。\n\n### B2: 派生新边界\n\n读 `predictions/*.md` 中所有有 `actual_plays` 的样本。\n\n**ratio 模式**：\n```\nbaseline = median(最近 3 篇 actual_plays)\nbuckets = {\n  \"退步\": (-inf, baseline * 0.3),\n  \"持平\": (baseline * 0.3, baseline * 1),\n  \"命中\": (baseline * 1, baseline * 3),\n  \"小爆\": (baseline * 3, baseline * 10),\n  \"大爆\": (baseline * 10, +inf),\n}\n```\n\n**absolute 模式**：\n```\nbaseline = median(全部校准池 actual_plays)\nbuckets = {\n  \"底部\": (-inf, baseline * 0.3),\n  \"基础盘\": (baseline * 0.3, baseline * 1),\n  \"命中\": (baseline * 1, baseline * 3),\n  \"爆款\": (baseline * 3, baseline * 10),\n  \"现象级\": (baseline * 10, +inf),\n}\n```\n\n**percentile 模式**：\n```\nsorted_plays = sorted(全部校准池 actual_plays)\nbuckets = {\n  \"底部\":   ≤ p30,\n  \"基础盘\": p30 - p60,\n  \"命中\":   p60 - p85,\n  \"小爆\":   p85 - p95,\n  \"大爆\":   ≥ p95,\n}\n```\n\n### B3: 报告变化 + 用户确认\n\n```\n当前 bucket scheme: ratio\nproposed scheme: absolute\nbaseline: 4.2w 中位数（基于 5 篇校准样本）\n\n新边界：\n- 底部:   < 1.3w\n- 基础盘: 1.3w - 4.2w\n- 命中:   4.2w - 12.6w\n- 爆款:   12.6w - 42w\n- 现象级: > 42w\n\n派生说明：\n- 5 篇实绩：1.5w / 3.8w / 4.2w / 5.6w / 18w\n- 中位数 4.2w，新桶按 ×{0.3, 1, 3, 10} 派生\n\n确认应用？(yes / no)\n```\n\n### B4: 落地\n\n用户确认后：\n1. 编辑 `rubric_notes.md` 的 \"Bucket 方案\" 段，替换为新表\n2. 更新 `.cheat-state.json` 的 `baseline_plays` 字段（bucket scheme 不持久化——下次 cheat-predict 实时派生）\n3. 在 `rubric_notes.md` 的 bucket 段顶部追加一行变更记录：`v2 buckets recalibrated on YYYY-MM-DD: scheme=absolute, baseline=4.2w (基于 N=10 个样本)`\n4. **不**修改任何 prediction 文件——历史预测的 bucket 标签保持原样（在该样本写入时的方案下做出的判断）\n\n### B5: 对未来预测的影响\n\n下一次 `/cheat-predict` 起按新 bucket 派生。历史 prediction 文件里的 bucket 标签**不重算**——bucket 是预测时的语义判断，事后改写会破坏盲度。\n\n### Phase B 不做的事\n\n- 不重打 composite（公式没变）\n- 不重新审核观察段（rubric 没变）\n- 不调跨模型审核（确定性派生无需判断）\n- 不要求严格的样本数门槛（按 READINESS_HEURISTIC 由 Claude 判断；ratio 模式 N=1 就能跑）\n\n---\n\n## Key Rules\n\n1. **5 步不可跳**（仅完整 rubric bump）。任何\"先简化跑一下\"的请求都拒绝\n2. **THRESHOLD 写死**（仅完整 rubric bump）。不允许动态调整\n3. **跨模型审核是默认**（仅完整 rubric bump）。关闭审核需要在 state file 显式标记\n4. **cleanup pass 是 bump 的一部分**（仅完整 rubric bump）。不允许 bump 完不清理观察段\n5. **REQUIRE_CONFIRM**（两种模式都要）。最后落地前必须用户明确说 \"yes, bump\" 或 \"yes, recalibrate\"\n6. **bucket 重校不动历史预测**。bucket 是预测时语义，事后改写破坏盲度\n\n## Refusals\n\n- 「跳过校准池重打，直接换公式」 → 拒绝。原则 #2\n- 「跳过 cheat-score-blind sub-agent，主 Claude 直接重打就行」 → 拒绝。bump **不接受**任何 self-scored fallback——sub-agent 不可用 → abort bump，不接受\"自审\"\n- 「跳过外部 LLM 审核」 → 仅当 `CROSS_MODEL_AUDIT=false` 显式设置\n- 「这次 THRESHOLD 调到 3/5 让它过」 → 拒绝。改 THRESHOLD 是元层级 bump\n- 「保留所有旧观察作为历史」 → 违反原则 #3\n- 「先 bump，cleanup 下次再做」 → 拒绝。cleanup 是 bump 的一部分\n- 「只重算 composite 不重打 dim」 → 拒绝。新权重 × 旧 dim 仍是旧污染。每个 dim 都由 sub-agent 重审 script\n- 「把 Memo 全文写进 rubric_notes.md 顶部，方便我读」 → 拒绝。rubric_notes.md 是 blind sub-agent 白名单——含视频名 / 实绩 → 通过白名单泄漏。Memo 写 rubric-memo.md（白名单**外**），rubric_notes.md 只放公式 + 通用语言维度定义 + 指针\n- 「派生证据段保留真实视频名，让 rubric 读起来更具体」 → 拒绝。在 rubric_notes.md 必须用通用语言（\"高抽象密度样本\"）；带视频名的派生证据写 rubric-memo.md\n\n## Integration\n\n- 上游：`/cheat-retro` 检测到 ≥3 同向偏差 → 提议跑 `/cheat-bump`\n- 依赖：`mcp__llm-chat__chat`（如配置）+ Task tool（spawn cheat-score-blind）\n- 修改：\n  - `rubric_notes.md`（结构性更新，**绝不**写真实视频名 / 实绩）\n  - `rubric-memo.md`（**新**——append Memo 全文，含证据 + 派生证据）\n  - 所有 `predictions/*.md`（追加 Re-scored 行，不动预测段）\n  - `.cheat-state.json`\n- 下游：下一篇 `/cheat-predict` 自动按新 rubric_version 打分\n","skills/cheat-init/SKILL.md":"---\nname: cheat-init\ndescription: cheat-on-content 的首次 onboarding 与脚手架创建器。统一流程——所有用户都走相同 5 阶段闭环，唯一区别是\"发过视频的人\"会在 init 时多一步：抓取已有视频建立历史 context（用于后续 cheat-seed 给更贴合的选题、更准的 baseline）。触发词：\"初始化\"/\"init\"/\"首次使用\"/\"我是新用户\"/\"setup cheat-on-content\"。**必须在用户第一次会话执行；其他子 skill 在 .cheat-state.json 不存在时自动路由到此。**\nargument-hint: \"[— form: opinion-video|long-essay|short-text|podcast]\"\nallowed-tools: Bash(*), Read, Write, Edit, Glob, WebFetch, Skill\n---\n\n# /cheat-init — 首次 onboarding\n\n让用户从零到能跑第一篇预测，全程 ≤ 5 分钟（没发过历史的）或 ≤ 10 分钟（已发过、要 import 历史的）。\n\n## Overview\n\n```\n[用户首次说\"初始化\"]\n  ↓\n[Phase 0: 检测当前状态]\n  ↓\n[Phase 1: 首屏文案 — 适用性 + 期望管理]\n  ↓\n[Phase 2: 6 个问题（Q1-Q5 都问；Q2 决定是否走 user-history import）]\n  ↓\n[Phase 2.5: 对标账号 — 强烈建议（cold-start 必须问，已发用户可选）]\n  ↓\n[Phase 3: 创建脚手架（含 scripts/ + videos/ + samples/ 空目录 + 模板文件含 benchmark.md）]\n  ↓\n[Phase 3.5: user-history import 流程（仅 Q2=有发过历史 + 用户同意）]\n  ↓\n[Phase 4: 测试 hook 是否生效]\n  ↓\n[Phase 5: 给\"下一步该说什么\"清单]\n```\n\n## Constants\n\n- **DEFAULT_RETRO_WINDOW_DAYS = 3**\n- **INSTALL_HOOKS = ask** — 默认询问；用户选 `auto` 直接装；`skip` 不装\n- **TREND_DEFAULT_SOURCES = [\"manual-paste\"]**\n\n## Inputs\n\n无。所有信息从 6 个对话问题里收集。\n\n## Workflow\n\n### Phase 0: 检测当前状态\n\n1. 读用户当前工作目录（**用户的 content project，不是 cheat-on-content 自己**）\n2. 检查是否已存在 `.cheat-state.json`：\n   - 存在 → 提示\"项目似乎已初始化（state file 存在）。要重新初始化会覆盖现有配置——确认？\" 等用户明确确认才继续\n   - 不存在 → 进入 Phase 1\n3. 检查是否已存在 `rubric_notes.md` / `predictions/` 等核心文件——存在但 state file 不存在 → 是\"半初始化\"状态，提示用户并询问\"要从现有文件推断状态还是重置？\"\n\n### Phase 1: 首屏直白告知期望（含适用性验证）\n\n向用户输出（一字不漏，不要软化）：\n\n```\n🎯 Cheat on Content / 网红外挂 — 初始化\n\n你的下一条内容已经在改写 3 个月后的你。\n规律是客观存在的，区别是你**看见**还是**没看见**。\n这套让你看见。\n\n接下来 5-10 分钟我会问你 5-6 个问题搞清楚你做什么、有什么、怎么用。\n两件事先说在前面：\n\n1. **早期预测会不准**——前 5 篇精度大概 ±50%，这是数学事实。\n   工具用 🔴🟠🟡🟢🔵 标 confidence 等级，不藏数字——\n   你自己判断这次能不能信。\n\n2. **强烈建议导对标账号**——5-10 条对标视频，工具立刻有 anchor。\n   不然第一批预测基本是占星。后面 Q5 会再问一次。\n\n准备好开始吗？\n```\n\n如果用户答\"继续\"或类似肯定回应 → Phase 2。\n不再因为 content_form 拒绝继续——任何形态都允许，只是 `rubric_form_mismatch` 字段标真，cheat-status 后续会持续提示用户\"你的形态需要 bump 调权重\"。\n\n### Phase 2: 6 个问题（一问一答，**不**批量提问）\n\n**Q1: 内容形态**\n\n> \"你的内容更接近哪一种？\n> a) **观点视频**（评论 / 时评 / 论说 / 议题讨论 / 个人观点）— 直接匹配内置 rubric\n> b) **长文 essay**（公众号 / Substack / Medium）— 可借观点视频 rubric 起步，bump 时调权重\n> c) **短文 / thread**（X / 微博 / 即刻）— 同上\n> d) **播客 / 视频长内容**（YouTube 长片 / 播客）— 同上\n> e) **教程 / 工具教学 / Builder**（教别人怎么用 X 工具 / 怎么做 Y 项目）— 同上\n> f) **其他**（游戏 / 美食 / 妆教 / 新闻 / 剧情）— 工作流通用，但 rubric 维度需要调\n>      （ER / SR / HP 这套对你形态可能不太预测，需要自己拆出适合的维度）\n> g) **混合**\"\n\n记录到 `content_form` + `rubric_form_mismatch`。\n\n**Q1 → `content_form` enum 映射**（**必须存 enum 值，不是字母**）：\n\n| 用户答 | `content_form` 写入值 |\n|---|---|\n| a | `\"opinion-video\"` |\n| b | `\"long-essay\"` |\n| c | `\"short-text\"` |\n| d | `\"podcast\"` |\n| e | `\"tutorial-builder\"` |\n| f | `\"other\"` |\n| g | `\"mixed\"` |\n\n`rubric_form_mismatch` 派生：\n- 选 a → `false`\n- 选 b/c/d/e/f/g → `true`，cheat-status 持续提示\"你的形态可能需要 bump 调权重\"\n- **不再有\"严重不匹配\"档**——所有形态都能跑工作流，只是有的 rubric 需要更激进的 bump\n\n**Q1.5: 典型时长**（仅 Q1=a/d/f 时问）\n\n> \"你的视频典型时长？\n> a) 30秒-1分钟  b) 1-3分钟  c) 3-5分钟（推荐起步）\n> d) 5-10分钟   e) 10分钟以上\"\n\n记录到 `typical_duration_seconds`（30 / 90 / 240 / 450 / 900）。\n\n**Q1.6: 发布频率**\n\n> \"你打算多久发一篇？\n> a) 日更   b) 隔日   c) 每周   d) 灵活 / 不固定（关闭 buffer 监控）\"\n\n记录到 `target_publish_cadence_days`（1 / 2 / 7 / null）。\n\n**Q2: 你这个频道发过视频吗？**\n\n> \"a) 没发过 — 我会帮你从兴趣 + 热点 brainstorm 5 个候选 + 写 5 份初稿\n>  b) 发过 — 不管 1 条还是 100 条，我会帮你抓历史让后续 brainstorm 更贴合你做过什么\"\n\n如选 a → state 写 `calibration_samples: 0`，**Phase 3.5 跳过**，直接进入 Phase 4。\n如选 b → 进入 **Q2.1**。\n\n**Q2.1: 平台 + 抓取计划**（仅 Q2=b）\n\n> \"你内容主要在哪个平台？\n> a) 抖音 — 装 douyin-session adapter（Playwright + 扫码登录抖音创作者中心）\n> b) 小红书 — 装 xhs-explore adapter（Playwright + 扫码登录小红书创作者中心）\n> c) YouTube — 装 youtube-data-api adapter（需 API key）\n> d) B 站 — bilibili-stat adapter\n> e) LinkedIn — 装 linkedin-session adapter（Playwright + 登录 LinkedIn，抓单帖分析）\n> f) 微信视频号 — 装 wechat-channels adapter（Playwright + 扫码登录视频号助手，仅抓自己账号后台数据）\n> g) 其他 / 多平台 — 走 manual paste 模式\"\n\n如选 a/b/c/d/e/f → 询问 Q2.2；如选 g → 跳到 Q2.3 manual。\n\n**Q2.2: adapter 安装时机**（仅 Q2.1=a/b/c/d/e/f）\n\n> \"现在装 adapter 自动抓取，还是先手动告诉我？\n> - 现在装 — 引导你装 Playwright + 扫码 → 抓回最近 N 条数据\n> - 等下再装 — 先 manual 模式，state 标 'pending_adapter_setup'，\n>            cheat-status 持续提示装\"\n\n如选\"现在装\"→ 走 adapter install 引导（详见各 adapter README）→ 验证抓取可用 → Q2.3。\n如选\"等下\"→ 跳到 Q2.3 manual。\n\n**Q2.3: 抓取范围 / 历史规模**\n\n如 adapter 已装并验证可用：\n> \"我可以抓你最近多少条作为基础？\n> （建议 10-25 条；样本越多 baseline 越准。最多到你账号实际数量）\"\n→ 用户给数字 N，Phase 3.5 抓取 N 条\n\n如 manual 模式：\n> \"你大概发过多少条？给个范围就行（比如 '5-10 条' / '20+ 条'），\n>  这只用来标 calibration_samples 估值，不用准确。\"\n→ 用户给一个估值，Phase 3.5 跳过抓取，calibration_samples 写估值\n\n**Q3: 数据回收方式**\n\n> \"T+3 天复盘怎么拿数据？\n>\n> a) 手动粘 — 候补方案。**你必须粘 top 20+ 评论（带赞数）**，不是只粘播放数。\n>    评论才是真信号——'她不一样'这种模因爆发只能从评论看出，\n>    播放数永远告诉不了你什么内容真的击中了观众。\n> b) **[推荐默认]** adapter 自动抓 — 评论 + 数据全要。\n>    如果你现在没装 adapter，没关系，state 标 'pending_adapter_setup'，\n>    第一次 publish 之前装上就行（cheat-status 会持续提醒装）。\n>    装的指引在 adapters/perf-data/<platform>/README.md。\"\n\n**Q3 → `data_collection` enum 映射**：\n\n| 用户答 | `data_collection` 写入值 |\n|---|---|\n| a | `\"manual\"` |\n| b（默认） | `\"adapter\"` |\n\n默认推荐 b——除非用户明确说 \"a 我就要手动\"。\n\n**Q4: 候选选题**\n\n> \"你现在有候选选题列表吗？（如有外部 markdown / Notion 维护的）\n> a) 没有（默认）— 一会儿我帮你 brainstorm，或日常用 /cheat-trends 抓\n> b) 有，markdown 列表\n> c) 有，Notion / 其他\"\n\n**Q4 → `pool_status` enum 映射**：\n\n| 用户答 | `pool_status` 写入值 |\n|---|---|\n| a（默认） | `\"none\"` |\n| b | `\"markdown\"` |\n| c | `\"notion\"` |\n\n**Q5: 装几个 hook（默认装，不需要你决定）**\n\n> \"Q5：我顺便装几个 hook，回 'yes' 或 'enter' 就装：\n>\n> 1. **预测锁** — 我们一起做完预测后，文件被锁。你或我都不能改预测段。\n>    复盘只能往同一文件下半段追加，不污染上半段判断。\n>    （没这个锁，事后看到数据想\"修一下当时的预测\"几乎是必然的——你或我都会犯）\n>\n> 2. **SessionStart 自动报告** — 每次开新会话顶部显示 buffer / 待复盘 / 候选 top\n>\n> 3. **静默使用日志** — 异步记录使用频率，不阻塞，给将来诊断用\n>\n> 三个一起装。**不装也可以**（回 'no'）但你失去预测锁，校准价值会下降。\n>\n> 回 yes / no。\"\n\n**Q5 → `hooks_installed` 映射**：\n\n| 用户答 | `hooks_installed` 写入值 |\n|---|---|\n| yes / enter / 默认 | `true`（bool，**不是字符串 `\"yes\"`**） |\n| no | `false` |\n\n默认 yes——除非用户明确说 no。\n\n### Phase 2.5: 对标账号（**所有用户都问**，cold-start 强烈建议）\n\n> 工具早期最重要的信号源是**对标账号**——你 init 完没数据，rubric 等权 v0 等于占星。\n> 但如果你能找一个你想做成那样的账号，导入 5-10 条它的高 / 中 / 低样本，工具就有了 anchor。\n\n询问：\n\n```\n🎯 对标账号\n\n你能找一个对标账号吗？至少 3 条该账号的视频。\n\n  - 你**完全没发过历史**（Q2=a）→ **强烈建议**——rubric 没 anchor 全靠对标。\n    不找的话用通用 v0 等权 rubric，前 5 篇精度更差更久\n  - 你**已发历史**（Q2=b）→ **可选**——你也可以只用自己历史 calibrate；\n    但建议至少导 1 个对标做 sanity check（看你账号是否真的偏离对标方向）\n\na) 现在找 → 立刻进入 /cheat-learn-from（5-15 分钟，看你材料准备程度）\nb) 等下找 → state 标 `benchmark_status: pending`，cheat-status 持续提醒\nc) 不找 → state 标 `benchmark_status: none`，用通用 v0 起步\n\n回 a / b / c。\n```\n\n行为：\n- 选 a → Phase 3 创建脚手架完毕后，**自动 dispatch 到 /cheat-learn-from**（不让用户手动跑——已经在 init 流程里了）。完成后回 init Phase 4\n- 选 b → state 标 `benchmark_status: pending` + `benchmark_name: null`\n- 选 c → state 标 `benchmark_status: none`\n\n记录到 `benchmark_status` / `benchmark_name`（如 a 选则在 cheat-learn-from 里写入）。\n\n### Phase 3: 创建脚手架（逐项解释）\n\n按顺序创建并**解释每一项的作用**：\n\n0. **`.gitignore`（安全 — 必须第一步创建）**\n   ```\n   \"先创建 .gitignore，把账号凭证挡在版本控制外——这是第一件事。\n    .auth/ / .auth-xhs/ / .auth-linkedin/ / .auth-wechat-channels/ 存的是各平台登录态（等同账号密码），\n    .cheat-secrets.json 存 API key / cookie——一旦被 commit 或云同步就等于泄露账号。\n    注意：predictions/ videos/ scripts/ 这些**不**忽略——原则 #1/#3 依赖\n    git history 作为预测的不可变档案，必须入库。\"\n   ```\n   - 复制 `cheat-on-content/templates/gitignore.template` → `<user-repo>/.gitignore`\n   - 如 `<user-repo>/.gitignore` **已存在** → 不覆盖；逐行检查并**追加缺失行**，至少确保\n     `.auth/`、`.auth-xhs/`、`.auth-linkedin/`、`.auth-wechat-channels/`、`.cheat-secrets.json` 五行存在\n   - 即使用户项目当前**还不是 git 仓库**也照常创建——它会在用户 `git init` 的那一刻立即生效\n   - 创建后提醒一句：如果项目已经 `git init` 过且可能误加过 `.auth/`，让用户跑\n     `git rm -r --cached .auth .auth-xhs .auth-linkedin .auth-wechat-channels .cheat-secrets.json` 把已暂存的凭证移出\n\n1. **`.cheat-state.json`**\n   ```\n   \"正在创建 .cheat-state.json — 各子 skill 共享上下文的地方。\n    这次 init 收集的所有答案都会写在这里。\"\n   ```\n   写入（**所有 `<...>` 占位必须查上面 Q 的映射表换成具体 enum 值，绝不直接存字母**）：\n   ```json\n   {\n     \"schema_version\": \"1.4\",\n     \"skill_version\": \"1.0.0\",\n     \"rubric_version\": \"v0\",\n     \"content_form\": \"<查 Q1 映射表，写 enum 字符串如 \\\"opinion-video\\\">\",\n     \"typical_duration_seconds\": <Q1.5 派生：30/90/240/450/900>,\n     \"target_publish_cadence_days\": <Q1.6 派生：1/2/7/null>,\n     \"rubric_form_mismatch\": <Q1=a→false；其他→true>,\n     \"benchmark_status\": \"<Phase 2.5 派生：a→\\\"imported\\\"/b→\\\"pending\\\"/c→\\\"none\\\">\",\n     \"benchmark_name\": <imported 则字符串名，否则 null>,\n     \"benchmark_sample_count\": <imported 则数字，否则 0>,\n     \"baseline_plays\": null,\n     \"calibration_samples\": <Q2=a→0；Q2=b→Phase 3.5 import 回填或 Q2.3 估值>,\n     \"data_collection\": \"<查 Q3 映射表，写 \\\"manual\\\" 或 \\\"adapter\\\">\",\n     \"pool_status\": \"<查 Q4 映射表，写 \\\"none\\\"/\\\"markdown\\\"/\\\"notion\\\">\",\n     \"data_layer\": \"markdown\",\n     \"hooks_installed\": <查 Q5 映射表，写 bool true/false>,\n     \"enabled_trend_sources\": [\"manual-paste\"],\n     \"enabled_perf_adapters\": <Q2.1=a→[\\\"douyin-session\\\"]；b→[\\\"xhs-explore\\\"]；c→[\\\"youtube-data-api\\\"]；d→[\\\"bilibili-stat\\\"]；e→[\\\"linkedin-session\\\"]；f→[\\\"wechat-channels\\\"]；其他→[]>,\n     \"last_bump_at\": null,\n     \"last_bump_self_audited\": false,\n     \"last_published_at\": null,\n     \"last_published_file\": null,\n     \"last_retro_at\": null,\n     \"last_trends_run_at\": null,\n     \"last_trends_added_count\": 0,\n     \"last_prediction_self_scored\": false,\n     \"last_self_scored_at\": null,\n     \"consecutive_directional_errors\": [],\n     \"pending_retros\": [],\n     \"shoots\": [],\n     \"in_progress_session\": null,\n     \"initialized_at\": \"<本地 ISO 8601 含时区，如 \\\"2026-05-05T20:11:13+08:00\\\"，**不要用 UTC 的 Z 后缀**>\"\n   }\n   ```\n\n2. **`rubric_notes.md`**\n   ```\n   \"正在创建 rubric_notes.md — 你的评分维度的真实来源。\n    用的是 v0 占位 rubric——等权 7 维（每个维度同等重要）。\n    \n    为什么叫 v0：v0 是没校准前的占位。你的账号自己的真权重要从你\n    的数据反推，不是预设。跑完 5 篇有数据的内容后，会自动提议\n    升级到「校准 v1」（你的第一个真正校准过的 rubric）。\n\n    ⚠️ rubric_notes.md 是 blind sub-agent (channel B) 的白名单文件——\n    只能含通用语言（公式 / 维度定义 / bucket 边界），不能含真实视频名 / 实绩。\n    每次 bump 升级时的 Memo（含证据数据 + 派生证据）写到 rubric-memo.md（下一步创建）。\"\n   ```\n   - 复制 `cheat-on-content/starter-rubrics/<form>-zero.md`（cold-start）或 `<form>.md`（已有数据时仍可参考）\n\n2.5. **`rubric-memo.md`**（**新**——配合 cheat-score-blind 隔离协议）\n   ```\n   \"正在创建 rubric-memo.md — bump 升级 Memo 累积档案。\n    这是 cheat-bump Phase 5 写入 Memo 全文（含真实视频名 + 实绩 + 派生证据）的位置。\n\n    为什么单独一个文件：blind sub-agent 的白名单是 rubric_notes.md，\n    历史上 bump Memo 写进 rubric_notes.md 会让 blind sub-agent 通过白名单\n    拿到本该看不到的实绩数据——本文件是隔离修复，sub-agent 硬禁读本文件。\n    \n    现在是空的，等第一次 cheat-bump 升级后 append 第一段 Memo。\"\n   ```\n   - 复制 `cheat-on-content/templates/rubric-memo.template.md` → `<user-repo>/rubric-memo.md`\n\n3. **`script_patterns.md`**\n   ```\n   \"正在创建 script_patterns.md — 你的写作 pattern 沉淀（与 rubric 解耦）。\n    rubric_notes.md 教 Claude 怎么打分；\n    script_patterns.md 教 Claude 怎么写。\"\n   ```\n   - 复制 `cheat-on-content/templates/script_patterns.template.md`\n\n4. **四个目录**：`scripts/` + `predictions/` + `videos/` + `samples/`（都加 `.gitkeep`）\n   ```\n   \"正在创建四个目录：\n    \n    scripts/      — 拍前的草稿（cheat-seed 写或你写）\n    predictions/  — immutable 预测日志（hook 保护）\n    videos/       — 拍后的工作目录（cheat-shoot 创建子目录）\n    samples/      — 对标账号视频 / 转录（cheat-learn-from 创建子目录）\n    \n    前三处用同一组 <date>_<id>_<short> 命名相互关联。\n    samples/ 按对标账号名分组：samples/<账号名>/<video-id>/。\"\n   ```\n\n4.5. **`benchmark.md`**（仅 Phase 2.5 选 a/b 时）\n   ```\n   \"正在复制 benchmark.md 占位模板（实际内容由 cheat-learn-from 填）—— \n    这是你的对标账号的中央 reference。\n    前期工具的 rubric / pattern / 选题方向感大量从这里推；\n    后期 N≥10 后影响淡出，但保留作 sanity check。\"\n   ```\n   - 复制 `cheat-on-content/templates/benchmark.template.md` → `<user-repo>/benchmark.md`\n   - **Phase 2.5 选 c 不创建** → benchmark.md 不存在，state 标 `benchmark_status: none`\n\n4.7. **`audience.md`**\n   ```\n   \"正在创建 audience.md — 你账号的受众画像（'谁在看'）。\n    \n    现在是空骨架。它和 rubric_notes.md 平行——rubric 教 Claude 怎么打分，\n    audience 告诉 Claude 你的观众是谁。跑够几篇复盘后跑 /cheat-persona，\n    它会从评论数据聚类出真实画像，cheat-seed 选题写稿时就有了一面镜子。\n    \n    注意：audience.md 由评论派生 → 含实绩信号 → blind 打分 sub-agent 硬禁读它。\"\n   ```\n   - 复制 `cheat-on-content/templates/audience.template.md` → `<user-repo>/audience.md`\n   - 如 Phase 2.5 选了 a/b（有 benchmark）→ Phase 5 清单里提示\"可跑 `/cheat-persona — seed-from-benchmark` 先 seed 一份未验证画像\"\n\n5. **`WORKFLOW.md`** + **`STATUS.md`**\n   - 复制 templates/ 对应文件\n\n6. **如果 Q5=是 → 安装 hooks**\n   - 读 `.claude/settings.json`（如不存在则创建空 `{}`）\n   - merge 进 `hooks/prediction-immutability.json` 的 `hooks.PreToolUse`\n   - merge 进 `hooks/session-start.json` 的 `hooks.SessionStart`\n   - merge 进 `hooks/meta-logging.json` 的 hooks（如同时启用）\n   - 复制 `prediction-immutability.sh` + `session-start.sh` + `log-event.sh` 到 `.cheat-hooks/`，chmod +x\n   - settings.json 里的 command 路径用 `${CLAUDE_PROJECT_DIR}/.cheat-hooks/`\n\n7. **(Pool 选项 c—Notion)** 仅记录到 state file 的 `pool_status: notion`，后续 cheat-trends 调用时再处理\n\n### Phase 3.5: import 流程（仅 Q2=b 且用户同意抓取）\n\n如 Q2.2=现在装 → 走 adapter install + login（详见 [adapters/perf-data/<platform>/README.md](../../adapters/perf-data/)）。\n\n抓取成功后，对每条已发视频：\n\n1. **建 video folder**：`videos/<date>_<id>_<short>/`\n   - `<date>` = 视频实际发布日\n   - `<id>` = 12 位 hash，对 (title + 平台 ID) 做 sha256\n   - `<short>` = 标题前 3-8 字\n2. **写 report.md**：从 adapter 抓回的数据（播放 / 点赞 / 评论 / 转发 / top 评论）填入\n3. **询问用户原稿**：\"video 「{标题}」 你保留了原稿吗？\"\n   - 是 → 用户提供 → 存为 `videos/<id>/script.md`\n   - 否 → 标 `script_lost`（仍建 video folder，只是 script.md 缺失）\n4. **写 reconstructed prediction**：`predictions/<date>_<id>_<short>.md`\n   - header 标 `**Reconstructed retrospective — NOT a blind prediction**`\n   - 7 维打分基于 script + 复盘段实绩数据**反向打**——明确这是非校准用途\n   - 不计入 calibration_samples（这是导入的历史，不是校准积累）\n\nimport 完成后：\n- 派生 `baseline_plays` = 抓回视频的播放中位数 → 写入 state file\n- 派生 confidence 等级 → 后续 cheat-predict 写预测时直接用\n- 输出汇总：\"已 import N 条历史。最近一条 X w 播放，中位数 Y w，已建 N 个 video folder + reconstructed predictions\"\n\n### Phase 4: 测试 hook 是否生效（仅当 Q5=是）\n\n跑一次假的 Edit 拦截测试：\n1. 创建临时文件 `predictions/_test_hook.md`，含 `## 预测\\n[test]\\n## 复盘\\n`\n2. 尝试 Edit 这个文件的 `## 预测` 段\n3. 钩子应 exit 1 阻塞 → 报告\"✅ immutability 钩子生效\"\n4. 删除测试文件\n5. SessionStart hook 验证：直接调一次 `bash .cheat-hooks/session-start.sh` → 应输出报告（即使是空的也行）\n\n如果钩子未生效 → **不要假装成功**，明确告诉用户：\"钩子安装失败，可能是 .claude/settings.json 配置没生效。建议手动检查或重启 Claude Code。\"\n\n### Phase 4.5: 如 Phase 2.5 选 a → dispatch 到 /cheat-learn-from\n\n如果用户在 Phase 2.5 选了 a（现在导对标账号）→ **自动触发 /cheat-learn-from**：\n\n```\n✅ 脚手架 + hooks 装完。\n\n下面立刻进入 /cheat-learn-from 帮你导入对标账号——\n你 init 时选了\"现在找\"，不让你又开一个会话才跑。\n\n[invoke /cheat-learn-from]\n```\n\ncheat-learn-from 完成后回到 init 的 Phase 5。\n\n如 Phase 2.5 选 b/c → 跳过 Phase 4.5，直接 Phase 5。\n\n### Phase 5: 给\"下一步该说什么\"清单\n\n```\n✅ 初始化完成（rubric: v0，calibration_samples: <N>，confidence: <emoji 等级>）\n\n下次你可以直接说这些：\n\n📝 写完一篇稿子 → \"打分这篇 scripts/<...>.md\"\n🎯 准备发布前  → \"启动预测 scripts/<...>.md\"\n🎬 拍完了      → \"拍了 scripts/<...>.md\" → 建 video folder + buffer +1\n🚀 发布后      → \"已发布 https://...\"\n📊 T+3 天      → \"复盘 videos/<...>/\"\n📈 任何时候    → \"状态\"（看完整看板）\n\n<如果 Q4=没有候选选题:>\n🌱 现在跑 /cheat-seed 找选题？\n   - 没发过历史的：纯 brainstorm（兴趣 × 热点）\n   - 发过历史的（已 import）：brainstorm 会基于你过去做过什么给推荐\n   回 \"yes, seed\" 立刻跑，回 \"no\" 你自己想。\n\n💡 你的 confidence 是 <当前等级> —— 它会随着你跑更多复盘自动提升。\n   不要因为 confidence 低就跳过预测——预测的纪律本身就是工具的核心，\n   早期预测的\"价值\"是数据采集，不是决策。第 5 次复盘后 rubric 第一次校准，\n   confidence 会跨入 🟡 偏低；第 10 次后 🟢 中。\n```\n\n## Key Rules\n\n1. **不假装成功**：任何步骤失败 → 明确告诉用户哪一步出错。绝不写\"✅ 初始化完成\"如果实际没完成\n2. **不批量提问**：5 个问题一次问一个\n3. **不静默 mkdir**：每创建一个文件都解释它的作用\n4. **不强推 SQLite**：所有用户给 markdown，提一句\"将来到 30 篇会建议升级\"就够了\n5. **state 字段统一**：删掉 mode / prediction_complexity / bucket_scheme 等枚举字段——单一用 calibration_samples 整数 + confidence 派生\n6. **import 失败不阻塞**：Q2=b 但 adapter 装失败 / 抓取失败 → 优雅降级到\"标 calibration_samples 估值，不导入历史 video folder\"\n\n## Refusals\n\n- 「跳过 Q1-Q5，直接给我创建所有文件」 → 拒绝。问题答案直接影响默认配置（content_form、cadence、hooks）\n- 「我已经在别处初始化过了，把那个项目的配置同步过来」 → 慎重。提示用户手动 cp 现有 `.cheat-state.json` 和 `rubric_notes.md`，不自动跨项目同步\n- 「不装 hook 但保留 immutability 承诺」 → 允许，state 标 `hooks_installed: false`，cheat-status 持续提示\"你的 immutability 是君子协定\"\n\n## Integration\n\n- 写完后，主 SKILL.md 的路由就解锁了所有其他子 skill\n- `cheat-status` 读 `.cheat-state.json` 的 `calibration_samples` 字段决定显示哪个 confidence 等级\n- 如 Q2=b 走了 import → 历史 reconstructed predictions 进 `predictions/` 和 `videos/<...>/`，但**不**计入 calibration_samples（不是真校准样本）\n- `/cheat-seed` 读 `predictions/` 的所有历史 reconstructed prediction → brainstorm 时知道\"用户过去做过什么\"\n\n## State 字段写入清单\n\n| 字段 | 写入时机 | 来源 |\n|---|---|---|\n| `schema_version` | Phase 3 | 硬编码 \"1.4\" |\n| `skill_version` | Phase 3 | 硬编码 \"1.0.0\" |\n| `rubric_version` | Phase 3 | \"v0\" |\n| `content_form` | Phase 3 | Q1 → 查映射表换 enum 值（**不是字母**） |\n| `typical_duration_seconds` | Phase 3 | Q1.5 派生 |\n| `target_publish_cadence_days` | Phase 3 | Q1.6 派生 |\n| `rubric_form_mismatch` | Phase 3 | Q1≠a → true |\n| `benchmark_status` | Phase 3 / 2.5 | Q2.5 答案派生 |\n| `benchmark_name` | Phase 3 / 2.5 | Q2.5 用户提供 |\n| `benchmark_sample_count` | Phase 3 / 2.5 | cheat-learn-from import 后回填 |\n| `baseline_plays` | Phase 3.5（如 import 成功） | import 数据中位数；否则 null |\n| `calibration_samples` | Phase 3 / Phase 3.5 | Q2=a→0；Q2=b→Q2.3 估值或 import 数 |\n| `data_collection` | Phase 3 | Q3 → 查映射表换 enum 值 |\n| `pool_status` | Phase 3 | Q4 → 查映射表换 enum 值 |\n| `enabled_perf_adapters` | Phase 3 | Q2.1 派生（如 Q2=a 则 `[]`） |\n| `hooks_installed` | Phase 3-4 | Q5 → bool（不是字符串） |\n| `last_bump_at` / `last_published_at` / `last_published_file` / `last_retro_at` / `last_trends_run_at` | Phase 3 | 全部 `null` |\n| `last_bump_self_audited` | Phase 3 | `false` |\n| `last_trends_added_count` | Phase 3 | `0` |\n| `last_prediction_self_scored` | Phase 3 | `false` |\n| `last_self_scored_at` | Phase 3 | `null` |\n| `initialized_at` | Phase 3 | now() 本地 ISO 8601，含 `+08:00` 时区，**不要 UTC `Z`** |\n","skills/cheat-learn-from/SKILL.md":"---\nname: cheat-learn-from\ndescription: 从对标账号导入 script + 数据 → 拆 pattern + 派生 base rubric 信号 → 写到 benchmark.md / script_patterns.md / rubric_notes.md。**这是工具最早期信号的来源**——cold-start 用户没自己历史时全靠对标，发过历史的用户也建议至少 1 个对标做 sanity check。触发词：\"学这个账号\"/\"拆这几个对标视频\"/\"learn from\"/\"导入对标账号\"/\"找对标\"。\nargument-hint: <账号名> [— way: a (default) | b] [— append | --replace]\nallowed-tools: Bash(*), Read, Write, Edit, Glob, WebFetch, Skill\n---\n\n# /cheat-learn-from — 对标账号导入\n\n工具早期最重要的信号源是**对标账号**——你 init 完没数据，rubric 等权 v0 等于占星。但如果你能找一个你想做成那样的账号，导入 5-10 条它的高/中/低样本，工具就有了 anchor。\n\n后期当你自己 calibration_samples ≥ 10 时，benchmark 影响自然减弱——你的真实数据成为主要信号源。但 benchmark.md **不删**，仍是 cheat-seed brainstorm 的 reference frame。\n\n## Overview\n\n```\n[用户：学这个账号 / 启动 cheat-learn-from]\n  ↓\n[Phase 0: 检查 benchmark 状态]\n  ↓\n[Phase 1: 选 input 方式（Way a 默认）]\n  ↓\n[Phase 2: 收集材料]\n  Way a: 用户粘 N 条 script 文本 + 数据\n  Way b: 用 whisper 转录 samples/ 目录里的视频\n  ↓\n[Phase 3: 询问每条样本的\"印象判断\"（高/中/低 + 为啥）]\n  ↓\n[Phase 4: Claude 拆 pattern + 派生 rubric 信号]\n  ↓\n[Phase 5: 用户 review → 改 → 落盘]\n  ↓\n[Phase 6: 写 benchmark.md / script_patterns.md / rubric_notes.md]\n  ↓\n[Phase 7: 更新 state.benchmark_status]\n```\n\n## Constants\n\n- **MIN_SAMPLES = 3** — 最少 3 条样本（少于拆不出 pattern）\n- **RECOMMENDED_SAMPLES = 5-10** — 推荐区间，平衡信号量 vs 用户工作量\n- **MAX_SAMPLES_PER_RUN = 15** — 单次导入上限——再多 Claude context 不够 + 用户也累\n- **DEFAULT_WAY = a** — Way a 简单 + 准确，是 default\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<账号名>` | 用户参数；缺失则询问 |\n| `.cheat-state.json` | 状态文件 |\n| Way a: 用户粘的 script 文本 + 数据 | 对话 |\n| Way b: `samples/<账号名>/*.mp4` 等视频文件 | 用户提前下载好放进去 |\n\n## Workflow\n\n### Phase 0: 检查 benchmark 状态\n\n读 `.cheat-state.json` 的 `benchmark_status`：\n\n| 状态 | 处理 |\n|---|---|\n| `none` | 首次导入——继续 Phase 1 |\n| `pending` | 用户之前答应等下找——继续 Phase 1 |\n| `imported` 已有 benchmark | 询问\"你已有 benchmark [当前名]，N 条样本。要做什么？  a) 追加新视频到当前 benchmark  b) 替换为新 benchmark  c) 只看不改\" |\n\n参数解析：\n- `--append` → 追加到现有 benchmark\n- `--replace <new-name>` → 用新 benchmark 替换（旧的归档到 benchmark.archived/）\n- 没标志 + 已有 benchmark → 走上面询问\n\n### Phase 1: 选 input 方式（**两个独立维度**）\n\n每条样本 = **script** + **数据**。两者怎么拿是独立的——你可以混搭。\n\n#### Phase 1a: script source（怎么拿稿子）\n\n```\nscript 怎么拿？\n\na) **粘文本（最简单，推荐）**\n   - 你自己整理过 / 用工具提取过——直接粘进对话\n   - 工具推荐（按方便程度排）：\n   \n     抖音 / 小红书：\n     - 微信小程序「轻抖」—— 粘视频链接 → 自动提取文案 + 评论。最快\n     - 类似工具：\"视频解析助手\" / \"短视频文案提取\" 等小程序\n     - 通常有免费额度，重度使用收费\n     \n     B 站 / YouTube：\n     - 视频页面有\"显示字幕/文字记录\"按钮（如果 UP 主开了）\n     - 第三方：DownSub / SaveSubs / yt-dlp --write-auto-sub\n     \n     公众号 / Substack：\n     - 直接复制网页文字\n   \nb) **whisper 转录视频文件**\n   - 你下载了视频到 samples/<账号名>/<video>/source.mp4\n   - 需要装 whisper-cpp + ffmpeg（见 adapters/script-extraction/whisper/README.md）\n   - 转录可能有错别字 / 漏字 / 标点不准——准确度比 a 差\n\nc) **跳过 script，只用元数据 + 印象**\n   - 你拿不到稿子也懒得用工具\n   - 后果：pattern 拆不深（只能看标题 / 数据 / 你的印象），但 rubric 信号还行\n   - 适合\"先快速搭起来，将来补\"\n\n回 a / b / c。\n```\n\n#### Phase 1b: data source（怎么拿播放/点赞/评论）\n\n```\n数据（播放 / 点赞 / 评论 / 转发）怎么拿？\n\na) **手填数字（最简单）**\n   - 你查一下账号后台或视频页面，告诉我数字\n   - 不需要装任何工具\n\nb) **adapter 自动抓（如已配置）**\n   - 你已经装了 perf-data adapter（如 douyin-session）\n   - 给我视频 URL，工具自己抓数据 + top 评论\n   - 评论数据更全（手填只能告诉我数字，adapter 能拿到具体评论文本）\n\n回 a / b。\n```\n\n**最常见的组合**：\n- 完全零依赖路径：1a + 1b（粘文本 + 手填）—— 5 分钟搞定\n- 评论质量优先：1a + 2b（粘文本 + adapter 抓）—— 装了 adapter 就走这个\n- 拿不到稿子兜底：1b + 1b（whisper + 手填）\n\n### Phase 2: 收集材料\n\n按 Phase 1a + 1b 的组合走对应路径。\n\n**通用纪律**：每条样本最少要有 (script 或 transcript 或 N/A标记) + 数据 (4 项基础：播放/点赞/评论/转发)。\n\n#### Path A: 粘文本（Phase 1a=a）\n\n```\n好。我们一条一条来。最少 3 条，推荐 5-10 条。\n\n第 1 条 script，把整段粘下面（段落版，不要字幕格式）：\n```\n\n收到 script → 算 video_id（sha256(script_content)[:12]）→ 进 Phase 2 数据采集。\n\n#### Path B: whisper 转录（Phase 1a=b）\n\n```\n先确认 whisper 装了：\n\n[运行 `command -v whisper-cpp` 或 `command -v whisper` 检测]\n\n如果没装：\n  ❌ whisper 没装。三选一：\n  - brew install whisper-cpp（推荐——快）\n  - pip install openai-whisper（Python 慢些）\n  - 切回 Path A 自己粘文本（推荐用轻抖等小程序）\n\n装好后：把视频文件放到 samples/<账号名>/<video-name>/source.mp4\n（一个视频一个子目录）\n\n放好后告诉我\"放好了 N 条\"，我转录。\n```\n\n用户放好后：\n1. Glob `samples/<账号名>/*/source.*` 找视频文件\n2. 对每个视频跑 `bash adapters/script-extraction/whisper/run.sh <video> samples/<账号名>/<id>/`\n3. 失败项报告但继续其他\n4. 进 Phase 2 数据采集\n\n#### Path C: 跳过 script（Phase 1a=c）\n\n直接进 Phase 2 数据采集——告诉用户\"没 script 我能拆的 pattern 仅限标题级别 / 你的印象，rubric 信号正常拆\"。\n\n#### Phase 2 数据采集（Phase 1b=a 手填 / b adapter）\n\n**如 Phase 1b=a（手填）**：\n\n```\n第 1 条数据：告诉我\n- 标题\n- 播放量\n- 点赞\n- 评论数（不是评论内容，是数字）\n- 转发 / 分享数\n\n格式随意，能识别就行。例如：\n  \"标题：怎么停止期待\n   播放：71w / 点赞 2.4w / 评论 899 / 转发 1.8w\"\n\n如果你能再粘 top 5-10 条评论（带赞数）更好——pattern 拆能挖到模因层。\n```\n\n**如 Phase 1b=b（adapter）**：\n\n```\n你说装了 adapter（如 douyin-session）。给我每条视频的 URL，\n我跑 adapter 自动抓数据 + top 评论。\n\n第 1 条 URL：\n```\n\n收到 URL → 调对应 adapter → 写数据 + 评论到 samples/<账号名>/<id>/meta.md。\n\n**通用**：继续问第 2 条 / 第 3 条 / ... 用户说\"够了\"或达到 MAX_SAMPLES_PER_RUN 时进 Phase 3。\n\n### Phase 3: 询问\"印象判断\"\n\n对每条样本（不管 Way a 或 b），收完数据后**追加问印象**：\n\n```\n你看完 / 听完这条视频的印象，算这个账号的：\n  a) 高表现样本（代表作 / 你想做成这样的）\n  b) 中表现样本（普通水准 / 不上不下）\n  c) 低表现样本（不算这个账号的代表 / 你不想做成这样）\n\n为什么？（一句话——这个判断比数据更能告诉我你想做什么风格）\n```\n\n记录 (impression_label, impression_reason) 到内存。\n\n> 注意：印象**可以**和数据冲突——比如某条数据高但用户觉得\"不算代表作\"。这种冲突本身是有用信号，记录下来。\n\n### Phase 4: Claude 拆 pattern + 派生 rubric 信号\n\n阅读所有 (script, 数据, 印象) → 自己分析：\n\n#### 4a. Script patterns\n\n按 script_patterns.md 的 cheat sheet 框架拆：\n- 开头钩子：3 种类型分布（场景代入 / IS 戏仿 / 数据反转）\n- 主体结构：几段 / 怎么切\n- 句式 / 句长 / 节奏：短句还是长句、有没有标志性句式\n- emotional 标记 / 双声道\n- 致谢段 / 收尾\n- 高频词汇 / 词汇风格\n\n输出 N 个具体 pattern（每个引用具体样本作证据）。\n\n#### 4b. Rubric 信号（**仅定性，不给数值权重**）\n\n对每条样本打 7 维分（用通用维度），然后看：\n- 高表现样本（按用户印象）共有哪些维度高？\n- 低表现样本共有哪些维度低？\n- 哪些维度在高/低样本之间无差异（说明不是关键维度）？\n\n输出**定性方向**（不是数值权重）：\n- \"ER 看起来重要\"（3/3 高样本 ER ≥4）\n- \"SR 看起来不显著\"（高低样本 SR 分布无差异）\n- \"MS 高的样本评论区有明显模因爆发\"\n\n### Phase 5: 用户 review\n\n一次性展示所有结果给用户：\n\n```\n我从你给的 N 条对标视频拆出：\n\n📝 N 个 script pattern：\n  1. **[Pattern 1 名称]**：[一句话描述] → 证据：[样本 X / Y]\n  2. ...\n  \n🎯 Rubric 定性信号：\n  - 看起来重要的维度：ER / QL / MS（每个有 N 条高样本支持）\n  - 看起来不显著的维度：SR / NA\n  - 给的初始建议：你的对标账号是 [情感+金句驱动型] / [数据驱动型] / [类比讲解型] / ...\n  - **不直接给数值权重**——5-10 样本拟合容易过拟合，先用作 tier-2 信号\n  \n🎨 选题方向感：\n  - 主题分布大概：[主题 A 40% / 主题 B 30% / ...]\n  - 调性：[一句话]\n\n回 \"ok\" 我落盘，\n或指出哪些 pattern / 信号你不认同（\"Pattern X 我觉得不准\" / \"Rubric 信号 Y 错了\"）。\n```\n\n用户反馈循环：\n- \"ok\" → Phase 6 落盘\n- 用户挑刺 → Claude 改 → 重新展示 → 直至确认\n\n### Phase 6: 落盘\n\n#### 6a. benchmark.md\n\n按 [templates/benchmark.template.md] 格式写到 `<user-channel>/benchmark.md`：\n- 账号信息（账号名、URL、调性、粉丝量级——用户提供）\n- 导入的样本表\n- 基础 rubric 派生（仅定性）\n- 选题方向感\n\n如 `--append` → 在现有 benchmark.md 的样本表追加新行 + 重新拆 pattern；不重写整个文件。\n如 `--replace` → 把现有 benchmark.md 移到 `benchmark.archived/<旧账号名>_<日期>.md`，写新的。\n\n#### 6b. samples/<账号名>/\n\n为每条样本建子目录：\n```\nsamples/<账号名>/<video-id>/\n├── source.mp4 (Way b 才有，Way a 没有)\n├── transcript.md (从粘文本写 / whisper 转出来)\n└── meta.md (标题 / 数据 / 印象 / 印象理由)\n```\n\n#### 6c. script_patterns.md\n\n在 `<user-channel>/script_patterns.md` 加新段：\n\n```markdown\n## 对标 [账号名] 借鉴（imported on YYYY-MM-DD，N 条样本）\n\n> 这些 pattern 来自对标账号——**Imported, untested on my channel**。\n> 实拍验证后（≥2 次跑出 + 复盘确认有效）再去掉这个标记，升入正式 pattern。\n\n### Pattern A: [一句话名]\n**来自**: [样本 X]\n**描述**: [详细]\n\n### Pattern B: ...\n```\n\n#### 6d. rubric_notes.md\n\n在 `<user-channel>/rubric_notes.md` 加 / 更新\"benchmark-derived initial signals\"段：\n\n```markdown\n## Benchmark-derived initial signals\n\n> 来自 benchmark.md 的对标账号 [账号名]（N=N 样本，import on YYYY-MM-DD）。\n> **仅定性方向，不直接采纳为数值权重**——5-10 样本拟合容易过拟合。\n> 等你自己 N≥5 校准样本后正式 bump 时**再决定**是否调权重。\n\n- 看起来重要的维度: ER / QL / ...\n- 看起来不显著的维度: SR / NA / ...\n- Claude 给的初始建议: [一句话定性]\n```\n\n### Phase 7: 更新 state file\n\n```json\n{\n  \"benchmark_status\": \"imported\",\n  \"benchmark_name\": \"<账号名>\",\n  \"benchmark_sample_count\": <N>\n}\n```\n\n## Key Rules\n\n1. **Way a 默认**——简单 + 准确。Way b 仅给\"找不到 script 只有视频\"的兜底\n2. **必须问印象**——纯看 transcript 拆 pattern 容易抓表面，加用户印象才挖到深层\n3. **Rubric 信号仅定性**——不直接给数值权重。5-10 样本拟合过拟合\n4. **pattern 默认标 untested**——避免污染用户自己的 pattern 库\n5. **不直接抓视频**——下载是用户的事，避免 TOS + 反爬\n6. **可重复跑**——`--append` 加新视频，`--replace` 换账号\n7. **MIN_SAMPLES = 3**：少于 3 拆不出 pattern，拒绝继续\n\n## Refusals\n\n- 「跳过印象判断，直接拆」 → 拒绝。印象是关键 input\n- 「我只能给 1 条样本」 → 拒绝。最少 3 条\n- 「直接给我数值权重」 → 拒绝。Phase 4 只给定性信号\n- 「能不能不写 transcript 文件，只在内存里拆」 → 不行。transcript 持久化是后续 cheat-retro Phase 4b diff 的依据\n- 「帮我下载对标视频」 → 拒绝。引导用户用 yt-dlp / BBDown 等工具自己下\n\n## Integration\n\n- 上游：`/cheat-init` Phase 2.5 在 cold-start 用户时**强烈建议**跑 `/cheat-learn-from`；calibration 用户**可选**\n- 上游：`/cheat-status` 在 `benchmark_status=pending` + 距 init >24h 时持续提醒\n- 下游：`/cheat-seed` brainstorm 时读 benchmark.md → 知道用户对标方向\n- 下游：`script_patterns.md` 加新段，cheat-seed 写 draft 时按 pattern 选结构\n- 下游：`rubric_notes.md` 加 benchmark-derived signals 段，cheat-bump 时作为参考之一\n- N≥10 提示：`cheat-status` 在用户 calibration_samples ≥10 时提示\"你已有足够自己数据，benchmark 影响淡出（保留作 sanity check）\"\n\n## benchmark 何时淡出\n\n不是死磕样本数，是 **Claude 判断\"用户数据信号是否已超过 benchmark\"**：\n\n- **默认参考**：calibration_samples ≥ 10 → benchmark 影响淡出\n- **可以更早**：N=5 但用户的 (打分, 实绩) 配对里出现 ≥3 条与 benchmark pattern 不一致的——说明你账号已经走出对标的路径\n- **可以更晚**：N=15 但用户的样本都很相似（都做同一类内容），benchmark 仍有信号价值\n\n判断条件 + 默认值都在 cheat-status 触发器 #19 / cheat-seed Phase 0 里实现。\n\n**淡出后**：\n- cheat-seed brainstorm 仍读 benchmark.md，但**优先级低于用户自己的 predictions/**\n- rubric_notes 的 benchmark signals 段标 `**Status: superseded by user data**`，不删但不再主导\n- benchmark.md **不删**——保留作 sanity check（看你账号是否真的偏离对标方向太远）\n\n**任何时候用户主动**：跑 `/cheat-learn-from --replace none` 完全解除 benchmark 影响\n\n## 与其他 skill 的区别\n\n| Skill | 用途 |\n|---|---|\n| `/cheat-learn-from` | **从对标账号**导入 pattern / rubric 信号（一次性 / 偶尔追加） |\n| `/cheat-seed` | brainstorm 选题 + 写 draft（读 benchmark.md 作参考） |\n| `/cheat-trends` | 抓今天的热点（与 benchmark 无关） |\n| `/cheat-bump` | 升级 rubric（用户自己 N≥5 后用真实数据，不直接用 benchmark signals） |\n","skills/cheat-migrate/SKILL.md":"---\nname: cheat-migrate\ndescription: 把老用户的 .cheat-state.json 升级到当前 schema_version。读 migrations/registry.md 算迁移链，按顺序应用每一步迁移文件。幂等：跑两次结果一样。失败停在中间版本不前进。触发词：\"迁移\"/\"升级 state\"/\"migrate\"/\"我的 state 是老版本\"/\"schema 版本不对\"。\nargument-hint: \"[— from: <version>] [— to: <version>] [— dry-run]\"\nallowed-tools: Bash(*), Read, Write, Edit, Skill\n---\n\n# /cheat-migrate — Schema 版本迁移\n\n把用户 `.cheat-state.json` 从旧 `schema_version` 升级到 cheat-on-content 当前期望的 `LATEST_SCHEMA`。\n\n---\n\n## Overview\n\n```\n[用户：迁移 / 或 SessionStart 提示后用户跑]\n  ↓\n[Phase 0: 读 .cheat-state.json + migrations/registry.md → 确定迁移链]\n  ↓\n[Phase 1: dry-run（默认）展示迁移计划，等用户确认]\n  ↓\n[Phase 2: 备份 .cheat-state.json → .cheat-state.json.backup-<timestamp>]\n  ↓\n[Phase 3: 按顺序对每个 step 应用对应迁移文件的 HOW 段]\n  ↓\n[Phase 4: 验证升级后 state 文件能被解析 + schema_version 已更新]\n  ↓\n[Phase 5: 报告 + 提示如有备份需要清理]\n```\n\n---\n\n## Constants\n\n- **REGISTRY_PATH = `${SKILL_DIR}/../../migrations/registry.md`** — 版本链单一来源\n- **MIGRATIONS_DIR = `${SKILL_DIR}/../../migrations/`** — 迁移文件目录\n- **DRY_RUN_BY_DEFAULT = true** — 首次跑展示计划，不直接改文件\n- **BACKUP_BEFORE_WRITE = true** — 写之前必备份；备份文件保留至下次成功 init / 用户手动清理\n- **STOP_ON_STEP_FAILURE = true** — 任何 step 失败 → 停在中间版本，不前进，不回滚\n\n> 💡 调用时覆盖：`/cheat-migrate — dry-run: false` 直接执行 / `/cheat-migrate — to: 1.2` 仅升到指定版本\n\n---\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `.cheat-state.json` | 用户项目根 |\n| `migrations/registry.md` | LATEST_SCHEMA + 版本链表 |\n| `migrations/<from>-to-<to>.md` | 每步具体迁移指令 |\n\n---\n\n## Workflow\n\n### Phase 0: 确定迁移链\n\n1. 读 `.cheat-state.json` → 解析 `current_version = state.schema_version`\n2. 读 `migrations/registry.md` → 解析 `LATEST_SCHEMA` 字段（行：`LATEST_SCHEMA = \"X.Y\"`）\n3. 解析 `args.to` 覆盖（如有）；否则 target = LATEST_SCHEMA\n4. 解析 `args.from` 覆盖（罕见场景：用户的 state 文件 schema 字段坏了，强制指定起点）\n5. **状态判断**：\n   - `current_version == target` → 输出\"✅ state 已是 {target}，无需迁移\" → 退出\n   - `current_version > target`（比如用户跑了 dev 版又切回 release）→ 报错\"无法降级，请手动调整或重新 init\"\n   - `current_version < target` → 继续，从注册表查出迁移链\n6. 从注册表\"版本链\"表算出 `chain = [(from, to, file), ...]`，按顺序串起 current → target\n\n如果某一步在注册表里缺失（比如 `current_version` 不在表里）→ 报错并展示\"目前已知版本：[1.0, 1.1, ...]\"，让用户检查。\n\n### Phase 1: dry-run\n\n输出迁移计划：\n\n```\n📋 迁移计划\n\n当前版本: 1.0\n目标版本: 1.2\n将按顺序跑 2 步：\n\n  [1/2] 1.0 → 1.1（MINOR）\n       新增字段：typical_duration_seconds, target_publish_cadence_days, ...（共 12 字段）\n       删除字段：mode, prediction_complexity, bucket_scheme\n       详见: migrations/1.0-to-1.1.md\n\n  [2/2] 1.1 → 1.2（MINOR）\n       新增字段：[...]\n       详见: migrations/1.1-to-1.2.md\n\n⚠️ 备份位置: .cheat-state.json.backup-<timestamp>\n\n继续吗？回 yes 执行 / no 退出 / dry-run-detail 看每步具体改什么。\n```\n\n如 `args[\"dry-run\"] == false` 或用户回 yes → 进 Phase 2。\n\n### Phase 2: 备份\n\n```bash\ncp .cheat-state.json .cheat-state.json.backup-$(date +%s)\n```\n\n输出：\"📦 备份到 .cheat-state.json.backup-1714838400\"\n\n### Phase 3: 按顺序应用每步\n\n对 chain 里的每个 (from, to, file)：\n\n1. 输出 \"→ [N/M] 应用 {file}...\"\n2. 读 `migrations/<file>` → 找到 `## HOW (Claude steps for /cheat-migrate)` 段\n3. **按段内自然语言步骤逐项执行**——这是关键：迁移是 Claude 读 markdown 跑的，不是 python 脚本\n4. 每步完成后：\n   - 更新内存里的 state.schema_version = to\n   - **原子写**到磁盘（写 .tmp → rename）\n5. 如某步失败：\n   - 输出\"❌ {file} 第 N 步失败：{error}\"\n   - 不前进、不回滚（state 已停在前一步成功的中间版本）\n   - 提示：\"已停在 schema_version: {last_success_version}。修复后重跑 /cheat-migrate 会从这里继续\"\n   - 退出\n\n### Phase 4: 验证\n\n升完后：\n1. 读 `.cheat-state.json` → 解析 → 应能成功\n2. 检查 `schema_version == target`\n3. 检查所有\"必填字段\"非缺失（参照 [shared-references/state-management.md](../../shared-references/state-management.md) 完整 schema）\n4. 失败 → 报错\"迁移完成但验证失败：{detail}。state 文件可能不一致——查看备份恢复\"\n\n### Phase 5: 报告\n\n```\n✅ 迁移完成\n\n  从: 1.0\n  到: 1.2\n  应用步骤: 2\n\nstate 文件现在含 X 字段，全部通过验证。\n\n📦 备份保留：.cheat-state.json.backup-1714838400\n   （确认一切正常后可手动 rm；下次成功 /cheat-init 也会清理过期备份）\n\n下一步建议：\n  - 跑 /cheat-status 确认看板正常\n  - 如有 hooks 重装需求，跑 bash <skill_repo>/install.sh --reinstall-hooks\n```\n\n---\n\n## Key Rules\n\n1. **幂等**：在已升过的 state 上重跑应该立刻退出\"无需迁移\"，**不**重复应用步骤。靠对比 `current_version == target` 实现\n2. **不跳版**：1.0 → 1.3 必须按 1.0→1.1→1.2→1.3 顺序，每步独立可恢复。不允许\"直接升 1.0 → 1.3 的合并 migration\"\n3. **不静默兼容**：state 文件 schema_version 不识别 → 明确报错\"未知版本 X，最近已知版本 Y\"，不假装能继续\n4. **失败停在原地**：第 N 步失败时 schema_version 停在 N-1 已成功的版本，不回滚到迁移前。重跑能从断点继续\n5. **备份是硬约束**：写之前必有备份。即使用户跑 `--dry-run: false`，备份动作仍执行\n6. **不动 predictions / rubric / videos**：只改 `.cheat-state.json`。其他用户数据由各自 skill 负责，迁移 skill 不碰\n7. **MAJOR vs MINOR 透明**：dry-run 输出必标 (MAJOR) / (MINOR)。MAJOR 时额外提示\"老 skill 用旧字段读会出问题，迁移完不能回退到老 skill 版本\"\n\n---\n\n## Refusals\n\n- 「跳过 dry-run，立刻覆盖我的 state」 → **允许**（`--dry-run: false`），但备份仍强制执行\n- 「我的 state 损坏了 / schema_version 字段没了，能不能猜一个版本来跑」 → 允许指定 `--from: 1.0`，但要警告\"基于猜测的迁移可能导致字段错位\"\n- 「降级到旧版本（current > target）」 → 拒绝。schema 演进单向。要降级请手动 cp 历史 git 快照\n- 「合并多步迁移成一个 atomic」 → 拒绝。每步独立可恢复是设计核心\n- 「在跑 cheat-bump / cheat-predict 中途调 migrate」 → 拒绝。等其他 skill 完成再跑，避免 in_progress_session 状态被破坏\n\n---\n\n## Integration\n\n- 上游：SessionStart hook 检测 `state.schema_version != LATEST_SCHEMA` → 输出红色警告 + 建议跑 `/cheat-migrate`\n- 上游（手动）：用户 git pull 拉了新版后，看 CHANGELOG 标 BREAKING → 主动跑\n- 下游：跑完后所有其他 skill 读 state 都能拿到最新字段\n- 与 `cheat-init`：init 写新 state 时直接用 LATEST_SCHEMA，不需要走 migrate\n- 与 `install.sh --reinstall-hooks`：迁移**不**重装 hook 脚本（hook 脚本属于 skill 包代码，不属于用户 state）。这两件事解耦\n\n---\n\n## State 字段读写\n\n本 skill **写**：\n- `schema_version`（每步成功后更新）\n\n本 skill **读**：\n- 所有现有字段（取决于具体 migration 文件的 HOW 步骤）\n\n本 skill **绝不**写：\n- `calibration_samples` / `pending_retros` / `shoots` 等业务状态（这些是其他 skill 的职责）\n- 例外：迁移文件明确说\"派生新字段值时需要扫 predictions/ 算 baseline_plays\"，那是初始化新字段，不是改老字段\n\n---\n\n## Examples\n\n### 示例 1：用户从 v0.1.0 升到 v0.2.0（假设 0.2 引入 schema 1.2）\n\n```\n用户：迁移\nClaude: [跑 cheat-migrate]\n  Phase 0: current=1.1, target=1.2, chain=[(1.1, 1.2)]\n  Phase 1: dry-run 输出计划\n  用户: yes\n  Phase 2: 备份\n  Phase 3: 应用 1.1-to-1.2.md（MINOR：新增 platform_metrics_url 等字段）\n  Phase 4: 验证 OK\n  Phase 5: 报告 ✅\n```\n\n### 示例 2：用户跳了多版\n\n```\n用户：我从 v0.1.0 升到 v0.5.0，state 还是 1.0\nClaude: [跑 cheat-migrate]\n  Phase 0: current=1.0, target=1.4 (LATEST), chain=[(1.0, 1.1), (1.1, 1.2), (1.2, 1.3), (1.3, 1.4)]\n  Phase 1: dry-run 输出 4 步计划\n  ...\n```\n\n### 示例 3：迁移中途失败\n\n```\nPhase 3:\n  → [1/4] 应用 1.0-to-1.1.md ✓\n  → [2/4] 应用 1.1-to-1.2.md ✓\n  → [3/4] 应用 1.2-to-1.3.md... ❌ 失败：用户 baseline_plays 字段含非数字值，无法转 int\n  \nstate 已停在 schema_version: 1.2。\n修复 .cheat-state.json 后重跑 /cheat-migrate 会从 1.2 → 1.3 继续。\n```\n","skills/cheat-persona/SKILL.md":"---\nname: cheat-persona\ndescription: 从复盘评论数据派生 / 刷新账号的受众画像，写入 audience.md。这是和 rubric 平行的第二个派生物——rubric 答\"怎么打分\"，persona 答\"谁在看\"。cheat-seed 选题 / 写稿时读它。**audience.md 含实绩信号，cheat-score-blind 硬禁读**。触发词：\"构造受众画像\"/\"更新 persona\"/\"我的观众是谁\"/\"build persona\"/\"刷新受众画像\"/\"看看我的受众画像\"。\nargument-hint: \"[— seed-from-benchmark] [— rebuild]\"\nallowed-tools: Bash(*), Read, Write, Edit, Glob, Grep\n---\n\n# /cheat-persona — 受众画像派生\n\n从 `predictions/*.md` 复盘段的评论数据，聚类出账号真实受众画像，写入 `audience.md`。\n\n---\n\n## 核心定位\n\npersona 是和 rubric **平行**的第二个派生物，**不是 rubric 的一部分**：\n\n```\n复盘数据（评论 + 完播 + 转粉）\n   ├──→ rubric 进化（cheat-bump）   —— \"怎么打分\"\n   └──→ 受众画像（cheat-persona）    —— \"谁在看\"\n两者都喂给 cheat-seed，但用途不同\n```\n\n- **rubric**：这稿子会不会爆 → 喂 cheat-predict 打分\n- **persona**：谁会因为这条多看 3 秒 / 留评论 / 转发 → 喂 cheat-seed 选题 + 写稿\n\n**绝不混**：persona 不进打分公式。rubric 的 AB 维度（受众广度）可以参考 persona，但那是 cheat-bump 的事，不是这里。\n\n## ⚠️ 污染隔离（不可省）\n\n`audience.md` 从复盘评论派生 = **含已发布作品的实绩信号**。因此：\n\n- `audience.md` 在 [cheat-score-blind](../cheat-score-blind/SKILL.md) 的 hard refusal list 里，refusal_code `blocked_audience`\n- persona 影响 cheat-seed **写什么**（creative direction），不影响 cheat-predict **怎么打分**（blind sub-agent 永远不读 audience.md）\n- 这是干净的：persona 塑造的内容进了成稿，blind sub-agent 照成稿本身打分——没有 leak。leak 只会发生在 sub-agent 能读 audience.md \"因为这受众爱 X 所以加分\" 的情况，而它读不到\n\n## Overview\n\n```\n[用户：构造受众画像 / 更新 persona]\n  ↓\n[Phase 0: 收集数据 — 扫 predictions/*.md 复盘段评论 + benchmark.md]\n  ↓\n[Phase 1: 数据量判定 → 派生 Confidence 等级]\n  ↓\n[Phase 2: 评论聚类 — 自我认同 / 情绪寄存 / 反驳点 / 语言]\n  ↓\n[Phase 3: persona × rubric 交叉检验]\n  ↓\n[Phase 4: 写 audience.md（覆盖式重建，header 记 version + last_rebuilt）]\n  ↓\n[Phase 5: 控制台报告 + 跟上次画像的 diff]\n```\n\n## Constants\n\n- **AUDIENCE_PATH = audience.md** — 受众画像落盘位置\n- **MIN_RETROS_FOR_DATA_GROUNDED = 3** — 复盘数 ≥3 才算\"数据扎实\"（可基于评论质量软判断）\n- **MIN_COMMENTS_PER_TRAIT = 3** — 一条\"验证特征\"至少要 3 条评论证据，否则降到\"假设特征\"\n- **SEED_FROM_BENCHMARK = auto** — 无自己复盘数据但有 benchmark 时，seed 一份未验证画像\n\n> 💡 调用覆盖：`/cheat-persona — seed-from-benchmark`（强制用 benchmark seed）/ `— rebuild`（即使数据没变也重建）\n\n## Inputs\n\n| 来源 | 用途 |\n|---|---|\n| `predictions/*.md` 的 `## 复盘` 段 | **主数据源**——top 评论（带赞数）。persona 的金矿 |\n| `videos/*/report.md` | 完播 / 转粉率——薄信号，推\"留得住 vs 留不住\" |\n| `benchmark.md` | 冷启动 seed——\"看对标的人 ≈ 你想要的人\" |\n| `rubric_notes.md` | Phase 3 交叉检验用——persona 食欲 vs rubric 校准现实 |\n| `audience.md`（如已存在） | 上一版画像，用于 Phase 5 diff |\n\n## Workflow\n\n### Phase 0: 收集数据\n\n1. Glob `predictions/*.md`，对每个文件读 `## 复盘` 段（**只读复盘段——这是 channel A，本来就看实绩**）\n2. 抽取每篇的 top 评论（带赞数）+ 实绩 bucket\n3. 统计：有评论的复盘篇数 `N_retros`、评论总数 `N_comments`\n4. 读 `benchmark.md`（如存在）\n5. 读 `audience.md`（如已存在）→ 留作 Phase 5 diff\n\n### Phase 1: 数据量判定 + Confidence\n\n| 情况 | Confidence | 行为 |\n|---|---|---|\n| `N_retros == 0` 且无 benchmark | 🔴 无数据 | **不强行造**——告诉用户\"persona 需要复盘数据。先跑几篇 cheat-retro，或导 benchmark\"，退出 |\n| `N_retros == 0` 但有 benchmark | 🟠 benchmark-seed 未验证 | seed 一份 aspirational persona，**全文标\"未验证\"** |\n| `N_retros` 1-2 | 🟡 早期信号 | 能产出但特征多落\"假设\"段 |\n| `N_retros` 3-5 | 🟢 数据扎实 | 正常产出 |\n| `N_retros` ≥6 | 🔵 稳健 | 正常产出 + 可做更细的食欲分层 |\n\nConfidence 等级写进 audience.md header。\n\n### Phase 2: 评论聚类\n\n对收集到的所有评论，按四个维度聚类：\n\n1. **自我认同**——\"我也是…\" / \"这就是我\" / \"作为一个…\" 模式。统计哪类身份反复出现（\"大厂打工人\" / \"一人公司\" / \"考研党\" / ...）\n2. **情绪寄存**——观众来评论是为了什么情绪？被验证（\"说得太对了\"）/ 宣泄（\"我也好累\"）/ 抬杠（\"我不同意\"）/ 求助（\"那该怎么办\"）。统计占比\n3. **反驳点**——哪些观点引来稳定的反对声。这是 persona 边界\n4. **语言**——他们怎么说话。玩梗密度、真诚 vs 戏谑、有没有复制你的金句\n\n**聚类纪律**：\n- 一条\"验证特征\"至少 `MIN_COMMENTS_PER_TRAIT`（3）条评论证据。不够 → 降到\"假设特征\"段\n- 每条特征**必须**能引出具体评论 + 出处（哪篇 prediction 的复盘段）+ 条数\n- 发现\"反画像\"信号（你以为的受众 vs 实际评论的人不一样）→ 写\"反画像\"段\n\n### Phase 3: persona × rubric 交叉检验\n\n读 `rubric_notes.md` 当前 rubric + 校准历史。检查：\n\n- persona 说\"受众爱 X 类主题\" → rubric 校准池里 X 类主题真的 over-perform 吗？\n- 不一致 → 在 audience.md 的\"persona × rubric 交叉检验\"段 **flag 出来**\n\n诚实要求：两个派生物矛盾时**不要**强行调和。明确写\"persona 说 A，rubric 校准说 B，待下次复盘澄清\"——矛盾本身是信号。\n\n### Phase 4: 写 audience.md\n\n**覆盖式重建**（不是 append）——persona 是活文档，每次 rebuild 重写全文。但：\n\n- header 的 `Persona 版本` +1（v0 → v1 → v2）\n- header 记 `Last rebuilt` 日期 + `数据基础`（N 篇复盘 / M 条评论）+ `Confidence`\n- 文件底部\"版本历史\"段 **append 一行**：`vN — 基于 M 篇复盘 / K 条评论，主要变化：...`（这是唯一保留的历史；不搞 memo 累积——persona 是活文档不是公式）\n\n用 [templates/audience.template.md](../../templates/audience.template.md) 的结构。\n\n⚠️ **不走版本 memo / 不调跨模型审**——persona 不是高风险不可逆动作（写错了重跑一次就好），过度工程没必要。\n\n### Phase 5: 报告\n\n```\n✅ 受众画像已更新：audience.md（v2，🟢 数据扎实）\n\n数据基础：4 篇复盘 / 87 条评论\n核心画像：25-35 岁职场人，来找情绪共鸣不来找信息……\n\n跟 v1 的主要变化：\n- 新验证：\"深夜刷手机\" 场景共鸣强（v1 是假设，本次 17 条评论验证）\n- 新反画像：原以为\"学生党\"是受众，但评论里学生占比 <5% → 移到反画像\n- ⚠️ 交叉检验 flag：persona 说受众爱\"职场吐槽\"，但 rubric 校准显示职场类 composite 偏低——下次复盘留意\n\n下一步：\n- cheat-seed 选题 / 写稿时会自动参考这份画像\n- 再跑 3 篇复盘后建议再 /cheat-persona 刷新\n```\n\n## Key Rules\n\n1. **数据派生，不手写**——persona 必须来自评论聚类。用户想手动加特征 → 允许，但标 `user-asserted`（未经数据验证）\n2. **证据强制**——验证特征必须带评论条数 + 出处。无证据的进\"假设\"段\n3. **覆盖式重建**——每次 rebuild 重写 audience.md 全文，只在版本历史段 append 一行\n4. **不进打分**——persona 永远不喂 cheat-predict / cheat-score-blind。它是 cheat-seed 的 creative lens\n5. **矛盾不调和**——persona × rubric 冲突时如实 flag，不强行编一个故事\n6. **冷启动诚实**——没数据就说没数据，benchmark seed 全程标\"未验证\"\n\n## Refusals\n\n- 「我觉得我的受众就是 X，你直接写进 audience.md」 → 可以写，但标 `user-asserted` 放\"假设特征\"段，不放\"验证特征\"。persona 的价值在于数据 vs 你的幻想之间的 gap\n- 「persona 也给 cheat-predict 用，让打分更准」 → 拒绝。persona 是实绩派生物，进打分 = 把 channel B 的隔离打穿。persona 只服务 cheat-seed\n- 「跳过评论聚类，你凭感觉给我画一个」 → 拒绝。凭感觉画的是营销话术不是 persona。没评论数据就老实说\"先去复盘\"\n- 「把 persona 写进 rubric_notes.md，省一个文件」 → 拒绝。rubric_notes.md 是 blind 白名单，写 persona（实绩派生）进去 = 实绩泄漏漏洞重演（见 observation-lifecycle.md 的 leak guard）\n\n## Integration\n\n- 上游：`cheat-retro` 每完成一篇复盘 → flag \"已累计 N 篇复盘，可跑 /cheat-persona 刷新画像\"\n- 上游：`cheat-init` 创建空 audience.md 骨架；如导了 benchmark → 提示可 `/cheat-persona — seed-from-benchmark`\n- 下游：`cheat-seed` Mode A/B/C 读 audience.md 作为\"这个 persona 会在乎吗\"的镜子\n- 下游（phase 2 路线）：`cheat-recommend` persona-fit 排序；`cheat-status` persona 新鲜度 nag\n- **隔离**：`cheat-score-blind` 硬禁读 audience.md（refusal_code `blocked_audience`）\n\n## Known limitations\n\n1. **评论 ≠ 全部受众**——留评论的是受众里最活跃的一小撮（沉默大多数不在数据里）。persona 偏向\"会评论的人\"，不是\"所有看的人\"\n2. **平台评论可被污染**——水军 / 引战 / 跑题评论会进数据。cheat-persona 聚类时对明显异常值降权，但不能完全过滤\n3. **persona 滞后于真实受众变化**——画像基于过去 N 篇的评论。受众结构变了，要等新复盘累积才反映\n4. **不解决\"我想要的受众 ≠ 我实际的受众\"**——persona 只如实报告\"现在谁在看\"。想转向另一种受众是选题战略问题，cheat-persona 只提供\"现状 vs 目标\"的 gap，不替你做战略\n","skills/cheat-predict/SKILL.md":"---\nname: cheat-predict\ndescription: 给最终稿写一份 immutable 盲预测日志。这是 cheat-on-content 整个校准循环的核心动作——预测段一旦写完不可改，由 hook 强制。**自动检测**：如目标文件已有 `## 预测` / `## 预测 v1` 段（被 cheat-shoot 调用走 v2 模式），改成 append `## 预测 v2` 而非覆盖。**打分通过 Task tool 委派给 `cheat-score-blind` sub-agent**（context-isolated channel B），主 Claude review 后落盘。触发词：\"启动预测\"/\"start prediction\"/\"给这稿子打分并预测\"/\"写预测日志\"。\nargument-hint: <script-path> [— mode: v1|v2] [— prediction-file: <path>] [— skip-blind]\nallowed-tools: Bash(*), Read, Write, Edit, Glob, Task\n---\n\n# /cheat-predict — AI 主导的盲预测 + 用户 review\n\n**这个工具是\"作弊器\"——AI 帮你做判断**。所以 cheat-predict 的核心是：\n- **Claude 自己**读稿子 + 打 7 维分 + 给 bucket + 概率分布 + 反事实场景\n- 用户 **review** 后回 \"ok\" 接受，或指出哪个维度 / 哪个判断不对\n- 默认走快路径：用户直接 ok → 落盘\n- 慢路径：用户挑刺某个维度 → Claude 改 → 再 review → 直至确认\n\n不是用户从 7 维分到概率分布全部自己写，那 Claude 只剩\"格式化器\"——失去工具的核心价值。\n\n**严格遵守 [shared-references/blind-prediction-protocol.md](../../shared-references/blind-prediction-protocol.md)**——见过任何后续数据就不能写预测，只能记 reconstructed。\n完整组件清单见 [shared-references/prediction-anatomy.md](../../shared-references/prediction-anatomy.md)。\nConfidence 派生表见 [shared-references/state-management.md](../../shared-references/state-management.md)。\n\n## Overview\n\n```\n[用户：启动预测 scripts/<id>.md]\n  ↓\n[Phase 0: blind check 自检]                    ← 触犯就拒绝\n  ↓\n[Phase 0.7: 模式判定 — v1 (新建) 还是 v2 (append)]\n  ↓\n[Phase 1: 读 script + rubric + state + 派生 confidence]\n  ↓\n[Phase 2: **委派 cheat-score-blind sub-agent**（Task tool）拿 9 维盲打 + per-dim confidence]\n  ↓\n[Phase 2.5: 主 Claude 对 blind 输出做 review — 若任意维度 |delta| ≥ 2 vs 主估，弹给用户裁定]\n  ↓\n[Phase 3: **Claude 自己**找锚点对比]\n  ↓\n[Phase 4: **Claude 自己**给 bucket + 概率分布 + 中枢]   ← confidence 低时分布更平\n  ↓\n[Phase 5: **Claude 自己**写反事实场景 + 关键校准假设]\n  ↓\n[Phase 5.5: **用户 review**——展示完整草拟版，等用户 \"ok\" 或挑刺]\n  ↓\n   ├─ \"ok\" → Phase 6 落盘\n   └─ \"X 维度应该 Y 不是 Z\" → Claude 改 → 再 review → 循环\n  ↓\n[Phase 6: 落盘 — v1 写新文件 / v2 append 到现有文件 ## 复盘 之前]\n  ↓\n[Phase 7: 更新 state.in_progress_session]\n```\n\n## Constants\n\n- **SCRIPTS_DIR = scripts/** — 草稿源目录\n- **PREDICTION_DIR = predictions/** — 落盘目录\n- **BLIND_CHECK = strict** — strict（默认）/ lenient（仅警告，不推荐）—— 跟 [blind-prediction-protocol.md](../../shared-references/blind-prediction-protocol.md) \"见过数据\"边界相关\n- **BLIND_SCORING = on**（默认）/ off —— 是否走 [cheat-score-blind](../cheat-score-blind/SKILL.md) sub-agent。off 等价于 `--skip-blind` flag，标 `last_prediction_self_scored: true` 给 cheat-status 警告\n- **DISAGREEMENT_THRESHOLD = 2** —— blind 与主 Claude 自评的单维度差异 |Δ| ≥ 此值 → Phase 2.5 弹用户裁定\n- **BUCKET_PRESET = auto** — 自动派生：有 baseline_plays → 按 baseline × {0.3 / 1 / 3 / 10 / 30}；无 baseline → 平台通用默认\n- **MIN_ANCHORS = 2** — 锚点对比期望 2 个；不够时显式标\"锚点 N/A\"段（不删段，不省略）\n\n> 💡 调用时覆盖：`/cheat-predict scripts/<id>.md — BLIND_CHECK: lenient` / `--skip-blind`（都不推荐）\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<video-folder-path>` 或 `<script-path>` | 用户参数；缺失则询问 |\n| `rubric_notes.md` | 用户项目根 |\n| `.cheat-state.json` | 状态文件 |\n| `predictions/*.md`（可选） | 历史预测，作为锚点 |\n\n### 入参解析（Phase 0.5，在 blind check 之前）\n\n用户给的路径**应该是** `scripts/<date>_<id>_<short>.md`。如不在 scripts/ 下：\n\n| 形态 | 处理 |\n|---|---|\n| `scripts/<date>_<id>_<short>.md` | 标准路径，直接用 |\n| `<id>` 或 `<short>` 简写 | glob `scripts/*_<id>_*.md` 或 `scripts/*<short>*.md` 找匹配 |\n| 任意外部 .md 文件（如 `~/Desktop/my-draft.md`） | **警告 + 询问**：\"建议把稿子放到 scripts/<date>_<id>_<short>.md 让 cheat-on-content 管理。要我帮你 cp 过去并算 id 吗？\"用户同意 → 建标准路径再继续 |\n| `videos/<id>/` 路径（user 误以为视频文件夹存稿子）| 提示\"video folder 是拍后才建的——pre-shoot 草稿在 scripts/。你要 predict 哪份稿子？\" |\n\n如 scripts/<id>.md 不存在 → 报错并询问\"你想 predict 的稿子在哪？\"\n\n## Workflow\n\n### Phase 0: Blind check 自检（**最关键**，触犯立即终止）\n\n按 [blind-prediction-protocol.md](../../shared-references/blind-prediction-protocol.md) 的\"子 skill 必须做的检查清单\"执行：\n\n1. 询问用户该作品当前发布状态：\n   - 未发 → 通过\n   - 已发 < `RETRO_WINDOW_DAYS` 天 → 询问\"你看过任何后续数据吗（播放/点赞/评论）？\"\n     - 用户回答\"没看过\" → 通过，标记 `published_before_prediction: true` + `blind_status: confirmed_no_data_seen`\n     - 用户回答含糊 → 视为\"已看\"，按下一项处理\n   - 已发 ≥ `RETRO_WINDOW_DAYS` 天 → **立即拒绝写\"预测\"**，建议改用 `_redo.md` 路径记 reconstructed retrospective\n\n2. 自检对话历史里是否含播放/阅读/点赞/评论/转发等字眼的实际数字 → 命中则视为已见数据，按上面 strict 模式处理\n\n3. `BLIND_CHECK=lenient` 模式：仅警告 + 强制在文件头标注 `**Reconstructed retrospective — NOT a blind prediction**`，但仍允许继续\n\n通过 → 进入 Phase 0.7。\n\n### Phase 0.7: 模式判定（v1 vs v2）\n\n判定本次是新建预测（v1）还是对既有预测的 v2 追加（拍后改稿场景）。\n\n**显式参数优先**：用户/调用方传 `— mode: v2` + `— prediction-file: <path>` → 直接 v2 模式。\n\n**自动检测**（无显式参数）：\n1. 推断目标 prediction 路径：`predictions/<同 scripts/<id> 命名>.md`\n2. 读该路径：\n   - 不存在 → **v1 模式**，进入 Phase 1\n   - 存在但只有空 `## 复盘`（无任何 `## 预测...` 段）→ **v1 模式**（异常状态，覆盖警告 + 进 Phase 1）\n   - 存在且含 `## 预测` 或 `## 预测 v1` 段 → **v2 模式**\n\n**v2 模式额外动作**：\n- 比较输入 script（最终拍摄稿）与 `## 预测` 段引用的原 `Script Hash`\n- 如完全一致（hash 同）→ 警告\"稿子没改，是否真要写 v2？\"——用户确认才继续；不确认则退出\n- 如不同 → 算 diff 概要（行数 / 字数 / 结构变化）→ Phase 5.5 review 时展示给用户\n- 标记 `prediction_basis = \"post_shoot_pre_publish\"`（v1 默认 `pre_shoot`）\n\n### Phase 1: 读最终稿 + rubric + state + 派生 confidence\n\n1. 按 Phase 0.5 解析后的路径，读 `scripts/<id>.md` 全文\n2. 计算 `script_hash` = sha256(script 内容)[:12] → header 用\n3. 读 `rubric_notes.md`，识别当前公式 + 维度（同 cheat-score Phase 2）\n4. 读 `.cheat-state.json` 拿 `rubric_version`、`content_form`、`calibration_samples`、`typical_duration_seconds`、`baseline_plays`\n5. **从 `calibration_samples` 派生 confidence 等级**（按 [state-management.md confidence 表](../../shared-references/state-management.md)）→ 后续写入 prediction header\n6. 询问用户：\"这是你打算实际拍摄发布的最终稿吗？还是会再改？\"——必须是最终稿\n7. 如果稿子字数与 `typical_duration_seconds` 派生范围严重不符（差 >50%）→ 提示用户：\"这条稿子 N 字，按你设的典型时长（X 分钟）应该是 M-K 字。是临时改了时长，还是稿子需要砍/补？\"\n\n### Phase 2: 委派 cheat-score-blind sub-agent 拿盲打分\n\n**BLIND_SCORING=on**（默认）—— 主 Claude 不再 inline 打分。通过 Task tool spawn `cheat-score-blind`，让一个 context-isolated 的 sub-agent 只看 script + rubric_notes.md 给出 N 维分。\n\n详见 [cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md) 的\"主 Claude 调用契约\"段。**Task prompt 必须精简**：\n\n```\nSpawn cheat-score-blind sub-agent.\n\nInput:\n  script_path: <Phase 0.5 解析出的 scripts/<id>.md>\n  rubric_notes_path: rubric_notes.md\n\nTask: 按 rubric_notes 当前公式给上面 script 打分。返回严格 JSON（见 cheat-score-blind SKILL.md Phase 2 schema）。\n不要读 state file / predictions/ / videos/ 任何其他文件。\n不要询问用户 —— 你没有用户。\n```\n\n**调用前自检**：把 Task prompt 串过 `grep -Ei '播放|阅读|点赞|评论数|实际|retro|复盘|实绩|w$|万$'`——命中 → 改 prompt 重发。\n\n**主 Claude 自己也内心估一份**（不发 sub-agent）——纯为 Phase 2.5 disagreement 检测，**不落盘**、**不替代 sub-agent 输出**。这个估值代表\"如果我没用 sub-agent，我会打多少\"，是 contamination 的客观指标。\n\n**沙盒 escape**：`BLIND_SCORING=off` 或 `--skip-blind` —— 主 Claude 自己打 7 维。state 立刻标 `last_prediction_self_scored: true` + `last_self_scored_at: <ISO>`，cheat-status 持续提示警告。仅用于：\n- Task tool 不可用（开发环境 / 离线）\n- 用户主动 audit 主 Claude 的 inline 判分能力（极少数）\n\n按当前公式算 composite——**用 sub-agent 回传的 dim 分**，不用主 Claude 自估。\n\n### Phase 2.5: Blind 输出 review + disagreement detection\n\n拿到 sub-agent JSON 后，主 Claude 必做的事：\n\n1. **JSON validity check**：`python3 -c \"import json; json.loads(...)\"` 应能解析；不能解析 → 主 Claude 重发 Task（最多 3 次重试），仍败 → abort，向用户报告\n2. **Contamination check**：`self_check.any_contamination_signal == true` → 警告用户\"sub-agent 自报疑似 contamination\"，但仍接受打分（confidence 降一档）\n3. **Refusal check**：`refusal != null` → 按 [cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md) Phase 2 的处理表对应路径\n4. **Disagreement detection**（核心）：\n   - 主 Claude 内心估一份 N 维分（Phase 2 末尾的\"自估\"）\n   - 对每个维度算 `delta = |主估 - blind|`\n   - 任何维度 `delta >= DISAGREEMENT_THRESHOLD`（默认 2） → **弹给用户裁定**\n\n弹裁定 UX：\n\n```\n⚠️  blind sub-agent 跟主 Claude 在某些维度差异较大：\n\n| 维度 | blind (sub) | 主 Claude 自估 | delta | sub-agent 理由 |\n|---|---|---|---|---|\n| ER | 5 | 3 | 2 | \"PPT加油猫猫开头—具象画面强\" |\n| AB | 2 | 4 | 2 | \"一人公司视角，受众窄\" |\n\n谁更准？\n  a) 信 sub-agent（隔离打分，但同 Claude 模型）\n  b) 信主 Claude 自估（有更多对话上下文，可能是 contamination）\n  c) 我自己定（你直接给分）\n\n回 a / b / c <你的分数>\n```\n\n用户选：\n- a → 用 sub-agent 全套分进 Phase 3\n- b → 用主 Claude 自估全套分（视为有意接受 contamination）→ 强制标 `last_prediction_self_scored: true`\n- c → 用户给的分覆盖该维度，其他维度仍走 sub-agent → 记到 `User Override`\n\n**所有 delta** —— 即使全 < THRESHOLD —— 都记录到 prediction header 的 `BlindScore Disagreement` 字段（详见 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 组件 1）。delta=0 也要记录。\n\n### Phase 3: 锚点对比\n\n**所有阶段都跑此 phase**——锚点不够时显式标 N/A，不删段。\n\n1. Glob `predictions/*.md`，读每个文件 header（提取 composite、实绩 bucket、duration_seconds）。**注意排除 reconstructed predictions**（标记 \"Reconstructed\" 的不算锚点）\n2. **优先**找同时长样本（`Target Duration (s)` 与本次差 ±20% 内）\n3. 在同时长（或全部）池里，找 2-4 个 composite 与本次预测 ±0.5 范围内的样本\n4. **如果池子太小**（同时长 < 2 个 + 全部 < 2 个）→ 输出\"锚点对比 N/A 段\"（参考 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 组件 5）—— 仍写这段，告诉读者锚点为何缺\n5. 列对照表；如跨时长，每行额外列\"时长 vs 本次\"列\n6. **关键诊断**：如果某个锚点的 composite 几乎相同但实绩差异 ≥3x → 说明 rubric 没捕获关键维度。**在文件里明确标注**作为新观察的种子\n\n> 为什么按时长筛锚点：4 分钟视频 5w 播放 vs 1 分钟视频 5w 播放完全不是一回事——长视频每秒扛了更多注意力损失。跨时长锚点容易得出虚假结论。\n\n### Phase 4: Bucket + 概率分布 + 中枢\n\n**所有阶段都写**——confidence 低时分布**更平**，不是省略。\n\n1. 从 `starter-rubrics/<content_form>.md` 读默认 bucket 边界（除非用户在 rubric_notes.md 自定义了）\n2. 选择最可能的 bucket（headline call）\n3. **必须**给出所有 bucket 的概率分布——加起来 100%\n4. **必须**给出该 bucket 内的\"中枢\"点估计\n\n**反诚实陷阱**：如果你给一个 bucket 95% 概率，下次预测错了你没法说\"我其实不太确定\"。**真实的概率分布通常在 headline bucket 是 40-65%**，剩下 ≥35% 散布在邻近 buckets。\n\n### Phase 5: 反事实场景 + 关键校准假设\n\n**所有阶段都写**——校准池小时关键校准假设可能没有合适对照样本，那就写\"无可对照样本——仍写下我对这次的核心赌注\"+ 1-2 条这次想测的事。\n\n**反事实场景**（4 段，每段对应一个可能的 bucket，写\"如果落在这里，意味着什么 rubric 假设被验证 / 推翻\"）：参考 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 组件 6。\n\n**关键校准假设**（强烈推荐）：\n- 找一个对照样本（最好是上一篇预测）\n- 明确写\"我押本篇 vs 对照 = X 倍\"\n- 写\"如果反过来 / 差距 < N → 哪个 rubric 假设被推翻\"\n\n如 `REQUIRE_HYPOTHESIS=required` → 缺失则不允许落盘。\n\n### Phase 5.5: 用户 review（**核心 — 决定写什么进文件**）\n\nPhase 2-5 全部在内存里做完后，**一次性展示完整草拟版**给用户：\n\n```\n我的预测草稿（写文件前 review）：\n\n📊 7 维分（v0 / v2 / 等当前 rubric）：\n| 维度 | 分 | 理由 |\n|---|---|---|\n| ER | 5 | \"PPT 加油猫猫 + 老板看到 + 大脑空白\"——情感重 |\n| HP | 5 | 开头\"PPT 第七页大屏中央 一只加油猫猫\"具体反差强 |\n| QL | 5 | \"加油猫猫救了我一命\"双金句 |\n| NA | 4 | 单一时间线 + 反思，清晰但不复杂 |\n| AB | 4 | 一人公司题但 AI 焦虑普适 |\n| SR | 3 | AI 焦虑是议题但不是热点对峙 |\n| SAT | 2 | 共情调性，几乎无讽刺 |\n→ composite ≈ 8.00\n\n🎯 押 bucket：30-100w，中枢 ~60w\n   概率分布: <5w 5% / 5-30w 22% / **30-100w 50%** / 100-150w 18% / >150w 5%\n   confidence: 🟢 中（基于 8 个校准样本，中枢 ±25%）\n\n🔍 锚点对比：\n| 对照 | composite | 实绩 | 异同 |\n|---|---|---|---|\n| ... | ... | ... | ... |\n\n🤔 反事实：\n   如果 >100w → 验证 ER 主导假设强化\n   如果 30-100w → 基准线 ok\n   如果 <30w → 推翻 \"AI 焦虑普适\"，AB 偏乐观\n\n🎲 关键校准假设：本篇 vs [对照] 押 1.5x\n\n——————————————————————————————\n\n回 \"ok\" 我直接落盘，\n或指出哪些维度 / 判断不对（如 \"AB 给 3，太乐观\" / \"中枢应该 30w 不是 60w\"）。\n```\n\n用户三种回应：\n\n1. **\"ok\"** / \"可以\" / \"继续\" → 直接 Phase 6 落盘，header 标 `Scored By: claude`\n2. **\"X 不对，应该 Y\"** → Claude 改对应字段（不光改值，要更新 composite + 概率分布等连锁影响），重新展示 → 循环回 Phase 5.5\n3. **\"全部重做\"** → Phase 2-5 重跑（罕见，通常是 Claude 严重误判稿子调性）\n\n**用户挑刺的字段**记录到 prediction header 的 `User Override` 段（Phase 6 写入）：\n- 哪个维度 / 哪个数字被覆盖\n- AI 原值 vs 用户改后的值\n\n复盘时这个字段帮诊断：\n- 用户每次都 ok（claude 一致）→ 没有用户偏见污染\n- 用户经常覆盖某维度 → Claude 在该维度系统性偏离用户实际感觉\n- 覆盖维度被实绩验证 → 用户直觉准 → rubric 可能漏了什么\n\n**用户挑刺的纪律**：\n- 用户**只能改字段值**，不能在 review 阶段塞新理由让 Claude 重写整段——那是把 Claude 当代笔\n- 改完 composite / 概率分布 / 锚点不一致 → Claude 自动连锁更新（不是用户算）\n\n### Phase 6: 落盘\n\n#### Phase 6a: v1 模式（新建预测文件）\n\n文件名约定（[blind-prediction-protocol.md](../../shared-references/blind-prediction-protocol.md) 的\"文件名约定\"段）：\n```\npredictions/YYYY-MM-DD_<id>_<short-title>.md\n```\n- `YYYY-MM-DD`：今天日期（预测写下的日期）\n- `<id>`：12 位 hash，对稿子全文做 sha256 取前 12 位（稳定 ID，重写不变）\n- `<short-title>`：3-8 字，去标点\n\n**第一段标题写 `## 预测 v1`**（不再写裸 `## 预测`——为将来可能的 v2 留 schema 一致性。老用户的 legacy `## 预测` 文件不动，hook 都识别）。\n\n**header 必填字段**：\n- `Article ID`（与 scripts/<id>.md 同 id）\n- `Script Path`（指向 scripts/<id>.md）\n- `Script Hash`（Phase 1 算出的）\n- `Calibration Samples` + `Confidence`（从 state 派生）\n- `Prediction Basis`：`pre_shoot`（v1 默认）\n- `Scored By`：`claude` / `claude+user_override`\n- **`BlindScored By`**：`subagent-v1`（Phase 2 默认）/ `main-claude-self`（`--skip-blind` 时） / `mixed`（Phase 2.5 用户裁定 b/c）\n- **`BlindScore Disagreement`**：JSON 字段列表，每维度 `{dim, blind, self, delta, decided_as}`，**所有维度必记**（即使 delta=0）\n- `User Override`（如有覆盖）：列出哪些字段被用户改了\n- 其他见 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 组件 1\n\n留一个空的 `## 复盘` 段：\n```markdown\n## 复盘\n\n（待填——T+RETRO_WINDOW_DAYS 天后跑 /cheat-retro <对应 video folder>）\n```\n\n#### Phase 6b: v2 模式（append 到既有文件）\n\n**绝不**用 Write 覆盖文件——会被 immutability hook 拦。用 **Edit** 在 `## 复盘` 之前插入 `## 预测 v2` 段：\n\n```python\n# 伪代码\nedit_old = \"## 复盘\\n\"   # 单独一行，确保 hook awk 识别为 v1 段的边界\nedit_new = \"\"\"## 预测 v2 (replaces v1; basis=post_shoot_pre_publish)\n\n**Diff vs v1**: 改了 N 行（X→Y%），主要变化：[摘要]\n**重判触发**: cheat-shoot 检测稿子改动 ≥30%\n**Script Hash (v2)**: <新稿子 hash>\n\n[7 组件 — 与 v1 同 anatomy]\n\n---\n\n## 复盘\n\"\"\"\n```\n\nv1 段**不动**。v2 段头部明确写\"replaces v1\"——读者一眼知道哪段是有效预测。\n\ncheat-retro 复盘时按\"读最后一个 `## 预测 vN`\"逻辑，自然取到 v2 算偏差。\n\n#### 共用规则\n\n**所有阶段都用统一完整版格式**（参考 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) \"完整结构总览\"）。confidence 低不缩格式，只让 header 标 confidence 等级 + 锚点对比段写\"N/A 解释\" + 概率分布更平。\n\n写文件**前**自检 7 个组件齐全（缺锚点 / 关键校准假设 → 写\"N/A 解释段\"，不删段）。\n\n### Phase 7: 更新 state file\n\n更新 `.cheat-state.json`：\n```json\n{\n  \"in_progress_session\": {\n    \"type\": \"prediction\",\n    \"file\": \"predictions/YYYY-MM-DD_<id>_<short>.md\",\n    \"video_folder\": \"videos/YYYY-MM-DD_<id>_<short>/\",\n    \"started_at\": \"<ISO timestamp>\",\n    \"rubric_version\": \"<v0/v2/...>\"\n  },\n  \"last_prediction_self_scored\": <true 仅当 --skip-blind / Phase 2.5 选 b>,\n  \"last_self_scored_at\": <ISO 当 last_prediction_self_scored=true 时>\n}\n```\n\n`video_folder` 为 null 表示用户跑的是裸 .md 文件，没建 video folder。\n\n`in_progress_session` 在 `cheat-publish` 触发时清除。如果用户预测后从未 publish（弃稿），下次 `/cheat-init` 或 `/cheat-status` 检测到陈旧 in_progress 会询问是否清理。\n\n`last_prediction_self_scored`：\n- `true` 仅当本次预测走了 `--skip-blind` 或 Phase 2.5 用户选了 b（信主 Claude 自估）\n- 一旦 `true` → cheat-status 持续 nag：\"上次预测没走 blind sub-agent，已 N 天\"——直到下次 normal `cheat-predict`（走 sub-agent）触发后才清回 `false`\n- `last_self_scored_at` 跟随更新；下次 `cheat-predict` 走 sub-agent → 这两个字段一起被重置\n\n### Phase 8: 控制台总结\n\n**Cold-start-simple 模式**：\n\n```\n✅ 预测落盘（cold-start 简化版）：predictions/2026-05-04_a3f2c1d4e5b6_停止期待.md\n\n7 维打分：ER5 / HP5 / QL4 / NA3 / AB5 / SR2 / SAT4\n方向押注：比上一篇明显好（ER+HP 双 5）\n对比对象：N/A（这是第 1 篇）\n\n⚠️  ## 预测段已 immutable（hook 锁定）。\n⚠️  这是 cold-start 期简化版——没有 bucket 数字。前 5 篇都这样。\n   第 5 篇复盘后会自动解锁完整预测（bucket / 概率 / 锚点 / 反事实）。\n\n进度：第 N 篇 / 共 5 篇 cold-start 期\n\n下一步：\n- 发布后 → \"已发布 https://...\"\n- T+3 天 → \"复盘 predictions/2026-05-04_...\"\n```\n\n**Complete 模式**：\n\n```\n✅ 预测落盘：predictions/2026-05-04_a3f2c1d4e5b6_停止期待.md\n\nbucket 押注：30-100w（中枢 50w）\n关键校准假设：本篇 vs 谁问你了 = 1.5-2x\n\n⚠️  ## 预测 段已 immutable（hook 锁定）。\n⚠️  你不能再向我\"透露\"这条作品的播放数据，否则下次复盘的盲度声明失效。\n   如果你不小心看到了，告诉我——我会在文件里补一个 integrity warning。\n\n下一步：\n- 发布后 → \"已发布 https://...\"\n- T+3 天 → \"复盘 predictions/2026-05-04_...\"\n```\n\n## Key Rules\n\n1. **blind check 是硬门槛**。BLIND_CHECK=strict 模式下，触犯即终止，不允许\"软处理\"。lenient 仅用于演练\n2. **整数维度分**。同 cheat-score\n3. **概率分布 = 100%**。不允许 95% + 8%；要么诚实给 50% + 30% + 15% + 5%，要么承认不知道\n4. **必须有 `## 复盘` 占位空段**——否则 hook 不知道哪里是 immutable 边界\n5. **不允许\"先写文件再讨论分数\"**——文件落盘后预测段就锁了；讨论必须发生在 Phase 2 之后、Phase 6 之前\n6. **id 是稿子 hash，不是时间戳**——重写 _redo.md 时 id 不变，便于跨文件追溯\n\n## Refusals\n\n- 「我已经看过播放数据了，但你假装没看到给我做个预测」 → 拒绝。BLIND_CHECK=strict 直接终止\n- 「我把预测段先写一版，等数据出来再调」 → 拒绝。这是把 immutable 协议反着用\n- 「我改稿了想让你覆盖之前的预测，不要 v2 段」 → 拒绝。v1 是档案，v2 才是当前判断——append 不覆盖。即使你\"主观感觉 v1 完全错了\"，git history 里 v1 还能查，但工作目录里 v1 必须留\n- 「跳过反事实场景，太麻烦」 → 拒绝。反事实是复盘诊断的依据，缺它复盘退化为\"准 / 不准\"\n- 「可不可以只写 bucket，不写概率分布」 → 拒绝。概率分布是逼你诚实的工具\n- 「这次先用 lenient 模式，下次再 strict」 → 询问原因。如果是测试 / 演练 → 允许且文件明确标 reconstructed；如果是想偷懒 → 拒绝\n- 「sub-agent 太慢，你直接打就行」 → 用 `--skip-blind` flag 显式声明。**不接受**主 Claude 自作主张跳过 sub-agent。flag 触发 state.last_prediction_self_scored=true，cheat-status 持续提示直到下次 normal 调用清除\n- 「Phase 2.5 选 b 后我不想标 last_prediction_self_scored=true」 → 拒绝。选 b 等于\"我有意接受主 Claude 自评\"——必须留下污染追踪轨迹\n- 「我是 cold-start 但想跑完整版预测，给我 bucket 数字」 → 拒绝。前 5 篇 bucket 数字是 false precision，给反而误导。等第 5 篇复盘后 cheat-status 会主动提示解锁。如果用户确实想要数字（罕见，自我教育目的）→ 允许但在文件头醒目标 `**Numerical predictions in cold-start are NOT predictive — for self-education only**`\n\n## Integration\n\n- 前置：`/cheat-init` 必须完成 + `rubric_notes.md` 存在\n- 上游可选：`/cheat-score` 反复尝试不同稿子版本\n- 下游：`/cheat-publish`（发布登记）→ `/cheat-retro`（复盘）→ 累计 ≥ MIN_SAMPLES 后 `/cheat-bump`\n- hook 依赖：`hooks/prediction-immutability.sh` 必须已安装在用户 `.claude/settings.json`，否则 immutability 仅靠 SKILL.md 自律——`cheat-status` 会持续提示\n","skills/cheat-publish/SKILL.md":"---\nname: cheat-publish\ndescription: 登记一篇内容已发布，把 URL/平台 ID/发布时间写入对应预测文件 header 和 state file。这是一个轻量动作——只更新元数据，**不动预测段任何字符**。触发词：\"已发布\"/\"I shipped\"/\"发布链接是 X\"/\"刚发完 [url]\"/\"publish registered\"。\nargument-hint: <prediction-file-or-url> [— platform: youtube|bilibili|douyin|...]\nallowed-tools: Bash(*), Read, Edit, Glob\n---\n\n# /cheat-publish — 发布登记\n\n把作品的发布元数据（URL、发布时间、平台）补到预测文件 header 与 state file。**禁止改预测段**——hook 会拦。\n\n## Overview\n\n```\n[用户：已发布 https://...]\n  ↓\n[Phase 0: 找到对应的预测文件]   ← 通过 in_progress_session 或匹配\n  ↓\n[Phase 1: 解析 URL → 平台/发布时间]\n  ↓\n[Phase 2: 更新 prediction 文件 header（仅 metadata 段）]\n  ↓\n[Phase 3: 更新 .cheat-state.json，清除 in_progress_session]\n```\n\n## Constants\n\n- **AUTO_DETECT_PLATFORM = true** — 从 URL 模式自动识别平台\n- **VERIFY_BLIND = true** — 提醒用户：从此刻起看到任何后续数据都会破坏盲度声明的诚信\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<prediction-file>` 或 URL | 用户参数；缺失则用 `.cheat-state.json` 的 `in_progress_session.file` |\n| `.cheat-state.json` | 用户项目根 |\n\n## Workflow\n\n### Phase 0: 找到对应的预测文件\n\n按优先级：\n1. 用户参数明确给了 prediction 文件路径 → 用它\n2. 用户参数只给了 URL → 读 `.cheat-state.json` 的 `in_progress_session.file`\n3. 都没有 → 列出 `predictions/*.md` 中 header 没填 `published_at` 的文件，让用户选\n\n**警告路径**：若 `in_progress_session.file` 与用户给的 URL 时间差超过 14 天 → 提示\"这个预测写于很久之前，确认是这篇？\"\n\n### Phase 1: 解析平台\n\n`AUTO_DETECT_PLATFORM=true` 时按 URL 模式：\n\n| URL 模式 | 平台 |\n|---|---|\n| `youtube.com/*` `youtu.be/*` | youtube |\n| `bilibili.com/*` `b23.tv/*` | bilibili |\n| `douyin.com/*` `iesdouyin.com/*` `v.douyin.com/*` | douyin |\n| `xiaohongshu.com/*` `xhslink.com/*` | xhs |\n| `mp.weixin.qq.com/*` | wechat |\n| `substack.com/*` `*.substack.com/*` | substack |\n| `medium.com/*` `*.medium.com/*` | medium |\n| `twitter.com/*` `x.com/*` | twitter |\n| 其他 | unknown — 询问用户 |\n\n发布时间获取：\n- 不强求自动抓——绝大多数平台需要登录态\n- 询问用户：\"发布时间是？（默认：现在）\" → 接受 ISO 8601 或自然语言（\"今天 14:30\" / \"20 分钟前\"）\n- 解析失败 → 用 now()\n\n### Phase 2: 更新 prediction 文件 header\n\n**绝不**触碰 `## 预测` 段及之后。只动文件最顶部的 metadata 块。\n\n读文件，定位到 metadata 块（在第一个 `##` 之前的所有行）。检查是否已有这些字段——有则警告\"已登记过\"并询问是否覆盖；无则追加：\n\n```markdown\n**Published at**: 2026-05-04T14:32:00+08:00\n**Platform**: douyin\n**URL**: https://v.douyin.com/abc123\n**Video Folder**: videos/2026-05-04_a3f2c1d4_停止期待/\n**Aweme ID**: 7234567890123456789  (douyin / 视频号 等需要的 platform-specific ID)\n```\n\n**关于 platform-specific ID**：\n- 抖音：从 URL 短链 resolve 后提取 `aweme_id`（v.douyin.com → 重定向后含 modal_id 或 item_id 参数）\n- B 站：BV 号\n- 小红书：note_id\n- YouTube：v= 参数后的 video_id\n\n如果用户给的是分享短链（无法立刻 resolve）→ 标 `Aweme ID: pending`，下次 `/cheat-retro` 时由 adapter 解析。\n\n**video folder 处理**：到 cheat-publish 这一步，对应的 `videos/<id>/` 目录**应该已经由 cheat-shoot 创建**（含 script.md）。\n\n- 如 video folder 不存在 → 警告\"你跳过了 cheat-shoot？建议先跑 cheat-shoot 把拍摄稿登记进 video folder 再发\"，**询问用户是否跳过登记直接发**：\n  - 是 → 自动建一个 video folder（fallback），但不询问稿子一致性，标 `ad_hoc_publish: true`\n  - 否 → 让用户先跑 cheat-shoot 再回来 publish\n\n用 Edit 工具（不是 Write 重写整个文件）。\n\n**hook 行为预期**：因为只动 metadata 段（在 `## 预测` 之前），immutability hook 应放行。如果 hook 误拦 → 报告 bug，**不要绕过 hook**。\n\n### Phase 3: 更新 state file\n\n```json\n{\n  \"in_progress_session\": null,\n  \"last_published_at\": \"<ISO timestamp>\",\n  \"last_published_file\": \"predictions/<filename>\",\n  \"last_published_video_folder\": \"videos/<...>/\",\n  \"last_published_platform_id\": \"<aweme_id 或 BV 号 等>\",\n  \"pending_retros\": [\n    \"predictions/<filename>\"\n  ],\n  \"shoots\": [\n    // 移除 video_folder 匹配本次发布的项；buffer -1\n  ]\n}\n```\n\n**`shoots` 队列处理**（buffer 跟踪关键）：\n1. 读 state.shoots[]\n2. 找 `video_folder == 本次发布的 video_folder` 的项 → 移除\n3. 如果没找到 → 警告\"buffer 队列里没有这条视频。是直接发布没经过 /cheat-shoot 吗？\"——不阻塞，但提示用户下次走 /cheat-shoot 让 buffer 跟踪准确\n\n`last_published_platform_id` 是 cheat-retro 调 adapter 时的输入——如 douyin-session 需要 aweme_id 直接抓数据。\n\n`pending_retros` 是待复盘列表——`cheat-status` 会基于这个列表 + RETRO_WINDOW_DAYS 显示\"今天该复盘哪些\"。\n\n### Phase 4: 提醒 + 下一步 + buffer 状态\n\n```\n✅ 登记完成：predictions/2026-05-04_a3f2c1d4e5b6_停止期待.md\n   - Published at: 2026-05-04 14:32\n   - Platform: douyin\n   - URL: https://v.douyin.com/abc123\n\n📦 Buffer：N 篇（颜色 + 含义）\n   按你的 cadence（X）= N×X 天 buffer\n   [如颜色变了，提示\"现在该去拍/暂停拍\"]\n\n⚠️  从此刻起，你看到任何关于这条作品的播放/点赞/评论数据\n    都会破坏盲度声明的诚信。如果不小心看到，告诉我——\n    我会在文件里追加一个 integrity warning。\n\n📅 计划复盘：T+3d，约 2026-05-07\n   到时间说：\"复盘 predictions/2026-05-04_...\"\n```\n\nBuffer 颜色由 [shared-references/cadence-protocol.md](../../shared-references/cadence-protocol.md) 派生。如本次发布让 buffer 跌入红色（断更风险）→ 高亮警告\"今天必须再拍 ≥1 条\"。\n\n## Key Rules\n\n1. **不动预测段**。即使是修复笔误，也不允许在 publish 时改预测段\n2. **不抓数据**。publish 是登记动作，不是数据回收（那是 cheat-retro 的活）\n3. **state 字段名固定**。`pending_retros` / `last_published_at` 是其他子 skill（特别是 cheat-status / cheat-retro）依赖的契约\n4. **平台未知不强报**。无法识别 → 询问用户，允许 `platform: other` 作为兜底\n5. **重复登记需明示**。已有 published_at → 询问\"覆盖？\"，绝不静默覆盖\n\n## Refusals\n\n- 「我顺手把预测段也改一下」 → 拒绝。请走 `_redo.md` 路径\n- 「URL 我等会儿补，先把发布时间记上」 → 允许：URL 字段可后续追加；published_at + platform 必填\n- 「跳过 metadata 更新，直接清 in_progress_session」 → 拒绝。元数据是复盘时的关键上下文（特别是 platform 决定数据回收用哪个 adapter）\n\n## Integration\n\n- 上游：`/cheat-predict`（写出 prediction 文件并设 in_progress_session）\n- 下游：T+RETRO_WINDOW_DAYS 后 → `/cheat-retro`\n- `cheat-status` 用 `pending_retros` 字段计算\"今天该复盘哪些\"\n- 平台字段被 `cheat-retro` 用来路由到对应的 perf-data adapter（manual-paste / youtube-data-api / 等）\n","skills/cheat-recommend/SKILL.md":"---\nname: cheat-recommend\ndescription: 从 candidates.md 里按当前 rubric 排序推荐 top N 选题，每条带 composite + 一句 rationale + 锚点对比。**candidates 不存在时给引导而非报错**。触发词：\"推荐选题\"/\"next topic\"/\"下一篇做什么\"/\"recommend topics\"/\"挑一个选题\"。\nargument-hint: \"[— top: N] [— filter: tier1|all|safe|risky]\"\nallowed-tools: Read, Glob, Grep\n---\n\n# /cheat-recommend — 候选池排序推荐\n\n读 candidates.md → 按 composite 排序 → 输出 top N 推荐，每条带评分细节 + 锚点对比 + 推荐理由。\n\n## Overview\n\n```\n[用户：推荐选题]\n  ↓\n[Phase 0: 检查 candidates.md 存在性]   ← 不存在则引导，不报错\n  ↓\n[Phase 1: 解析 candidates 列表]\n  ↓\n[Phase 2: 过滤（tier / 安全性 / 已发过）]\n  ↓\n[Phase 3: 排序 by composite + 找锚点]\n  ↓\n[Phase 4: 输出 top N + 每条的 rationale + 锚点对比]\n```\n\n## Constants\n\n- **TOP_N = 5** — 默认推荐 top 5\n- **STRATEGY = stable+experimental** — 推 ≥2 时按 [cadence-protocol.md](../../shared-references/cadence-protocol.md) 的\"1 稳分 + 1 实验性\"策略；推 1 时只推 top 稳分\n- **POOL_PATH = candidates.md** — 候选池路径\n- **EXCLUDE_PUBLISHED = true** — 排除已发布的（与 `predictions/*.md` 去重）\n- **EXCLUDE_REJECTED = true** — 排除用户主动跳过的（`tier=skip`）\n- **REQUIRE_SCORED = true** — 只推荐已打分的——避免推没读过的素材\n- **DUPLICATE_CATEGORY_LOOKBACK** — 派生自 `state.target_publish_cadence_days`：max(3, cadence_days × 3) 天内已发同类目候选不推（避免审美疲劳）\n\n> 💡 调用时覆盖：`/cheat-recommend — top: 3 — filter: safe`\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `candidates.md` | 用户项目根 |\n| `predictions/*.md` | 用于去重 |\n| `.cheat-state.json` | 当前 rubric_version |\n\n## Workflow\n\n### Phase 0: 候选池存在性检查\n\n读 `candidates.md`：\n\n| 状态 | 处理 |\n|---|---|\n| 文件不存在 | **不报错**。输出引导：见下方\"无候选池引导\" |\n| 文件存在但空（< 1 个 entry） | 同上 |\n| 文件存在且非空 | 进入 Phase 1 |\n\n**无候选池引导**（核心：不让用户第一次遇到 cheat-recommend 时被劝退）：\n\n```\n你目前没有候选池（candidates.md 不存在或为空）。\n\n绝大部分人没有候选池——这很正常。四个建立方式，挑一个：\n\n1. 🌱 [推荐] 跑 /cheat-seed\n   一次性的种子动作：3 个问题（兴趣 / 调性 / 红线）→ 拉公开热点 + Claude brainstorm\n   → 输出 15 候选让你挑 5 → 默认顺带写 5 个 draft。5 分钟搞定。\n   \n   - 没发过历史的：纯 brainstorm（兴趣 × 热点）\n   - 发过历史的（init 时已 import）：brainstorm 会基于\"你过去做过什么\"给推荐\n   \n   说：\"找选题\" 或 \"seed\"\n\n2. 🔥 [日常补充] 用 /cheat-trends 抓 20 条带打分的候选\n   说：\"抓热点\" — 从 weibo-hot / zhihu-hot / b站热门 / HN / 你配的源各拉 N 条\n   适合已经跑过 /cheat-seed、想日常补充候选池的用户\n\n3. ✍️  手动建：把候选标题贴进 candidates.md，每行一条\n   我会自动给每条粗打分\n\n4. 📋 从 Notion / RSS 导入：跑 /cheat-init --mode add-pool 配置 adapter\n\n你也可以跳过候选池，直接给我具体稿子说\"启动预测\"。\n\n> /cheat-seed vs /cheat-trends 的区别：\n> - seed 是种子动作（含 brainstorm + 可选 draft），适合\"我从零开始没选题\"\n> - trends 是日常多 adapter 抓取（不 brainstorm 不写 draft），适合\"日常补充候选池\"\n```\n\n完成引导 → 退出，不继续后续 phase。\n\n### Phase 1: 解析 candidates\n\n按 [candidate-schema.md](../../shared-references/candidate-schema.md) 的\"Markdown 表示\"格式解析每个 H3 entry：\n\n```markdown\n### [tier1] 标题\n- **id**: a3f2c1d4e5b6\n- **composite (v2)**: 8.47 — ER=4 HP=4 QL=5 NA=3 AB=5 SR=3 SAT=3\n- **predicted bucket**: 5-30w\n...\n```\n\n提取每条的 `id` / `title` / `tier` / `composite` / `dimension_scores` / `note`。\n\n容错：`candidates.md` 格式被用户手改过 → 询问用户 schema，**不要静默忽略不识别的 entry**。\n\n### Phase 2: 过滤\n\n```\n1. EXCLUDE_PUBLISHED=true → 扫 predictions/*.md 的 header，提取所有 id；从候选池过滤掉\n2. EXCLUDE_REJECTED=true → 过滤 tier=skip\n3. REQUIRE_SCORED=true → 过滤 composite=null（未打分的不推荐）\n4. filter 参数：\n   - tier1: 只保留 tier=tier1\n   - all: 不过滤（tier1+2+3）\n   - safe: 排除 tier=risky\n   - risky: 仅显示 tier=risky（用于\"我今天就想发风险议题\"）\n```\n\n### Phase 2.5: Buffer 颜色覆盖（**最高优先级**）\n\n读 `state.shoots` + `state.target_publish_cadence_days` 算 buffer 颜色（[cadence-protocol.md](../../shared-references/cadence-protocol.md)）：\n\n| Buffer 颜色 | 推荐策略覆盖 |\n|---|---|\n| 🔴 红 | **只推 top 1 稳分**——不推实验性。回：\"buffer 已 0/1 篇，下个发布日断更风险高，今天必须拍 ≥1 条稳分。下面是 top 1 稳分（不推实验性）\" |\n| 🟠 橙 | 标准 1 稳 + 1 实验，但提示\"建议优先拍稳分\" |\n| 🟢 绿 | 标准 1+1（默认） |\n| 🔵 蓝 | **拒绝推荐**。回：\"你 buffer 已 N 条，cadence-protocol 规定积压时暂停拍摄。先发存货 + 复盘。手动覆盖请说 '我就要拍'\" |\n| 灵活模式 (`target_publish_cadence_days=null`) | 不应用 buffer 覆盖，标准策略 |\n\n### Phase 3: 排序 + 选 1 稳 + 1 实验（按 STRATEGY）\n\n#### 第 1 条（稳分）\n\n1. 按 `composite` 降序排\n2. 过滤掉 `tier=risky`（稳分要安全议题）\n3. 过滤掉 `category` 与最近 `DUPLICATE_CATEGORY_LOOKBACK` 天已发/已推过的重复（避免审美疲劳）\n4. 取 top 1\n\n#### 第 2 条（实验性）\n\n1. 在 candidates.md 中找：\n   - 维度组合与最近已发样本**差异最大**（增加校准信息量），或\n   - 含明确的 pattern/dimension hypothesis（如 \"MS=5 的 A/B 对照\"），或\n   - tier=risky 但用户主动愿意试（用 `--filter risky` 覆盖）\n2. composite 不一定 top——但有\"信息价值\"\n3. 如 candidates 池里没有合适的实验性候选 → 回：\"候选池里没有明显的实验性样本，给你 2 条稳分\"\n\n#### 剩余 (TOP_N - 2) 条\n\n按 composite 降序补满，标 \"（备选）\"。\n\n#### 锚点\n\n对每条找 1-2 个 composite 接近的**已发布**作品作为锚点（从 `predictions/*.md` 读）。优先**同时长**锚点（按 `state.typical_duration_seconds` ±20%）。\n\n### Phase 4: 输出\n\n```\n🎯 候选池推荐（rubric: v2 / buffer: 🟢 绿 / cadence: 隔日更）\n\n📌 第 1 条 — **稳分**（推荐立即拍）：\n  **[tier1] [👍 9.18] \"为你好\"高密体系**\n   - 维度：ER=5 HP=5 QL=4 NA=4 AB=5 SR=5 SAT=4\n   - 粗预测桶：30-100w（中枢 ~60w）\n   - rationale：ER+SR 双 5 顶配，\"高密度家庭议题\"普适且分享安全\n   - 锚点：仓鼠 (composite 9.41, 实绩 124w) — 同走\"理论框架+具象样本\"路线\n   - 风险：议题厚重，不适合连续 2 篇都打这种\n\n🧪 第 2 条 — **实验性**（验证特定假设）：\n  **[tier1] [👍 8.71] 哈哈长度**\n   - 维度：ER=3 HP=5 QL=5 NA=4 AB=5 SR=4 SAT=5\n   - 粗预测桶：30-100w（中枢 ~55w）\n   - **测试目标**：v2.1 候选维度 MS+TS 双 5 vs 谁问你了同 ER/HP/QL/SR 但 MS+TS 低 3\n   - 信息价值：拍这条能强证据/弱推翻 v2.1 升正\n   - 锚点：谁问你了 (composite 8.24, 实绩 11.7w)\n\n（备选 top 3）：\n  3. ……\n  4. ……\n  5. ……\n\n下一步：\n- 选稳分 + 实验性各拍 1 条 → 改写 script → \"启动预测\"\n- 只拍 1 条 → 选稳分（buffer 颜色越红，越应该选稳分）\n- 想抓更多候选 → 说\"抓热点\"\n- 都不满意 → 说\"过滤改 all\"看其他 tier 或 \"regen\"\n```\n\n如 buffer 颜色为 🔴：\n```\n🔴 buffer 警戒：你 buffer 已 0/1 篇，**下个发布日可能断更**。\n   按节奏协议，只推 top 1 稳分（不推实验性）：\n\n  **[tier1] [👍 9.18] \"为你好\"高密体系**\n   - ...（同上稳分格式）\n\n今天必须拍这条。挑 5 条候选 → \"抓热点\"。\n```\n\n如 buffer 颜色为 🔵：\n```\n🔵 buffer 积压：你 buffer 已 N 条，**暂停推荐**。\n   按节奏协议，先发存货 + 复盘。\n   - 已拍未发：N 条（最早一条 X 天前拍的）\n   - 待复盘：N 条\n   说 \"已发布 ...\" 出队，或 \"复盘\" 处理待复盘项。\n   如果你坚持要拍新的，回 \"我就要拍\"，我会推 top 1 稳分。\n```\n\n每条必有：维度评分（让用户能挑战打分）+ 锚点（让用户校准 composite 的可信度）+ rationale（让用户理解推荐逻辑）。**不允许只输出 composite 排序而无解释**——那是黑箱。\n\n## Key Rules\n\n1. **不报错，给引导**。candidates 缺失是默认状态，不是错误\n2. **不推未打分的**。REQUIRE_SCORED=true 是诚实门槛——推未读过的素材是占星\n3. **必带锚点**。composite 8.47 在不同账号意味不同，锚点把抽象数字 ground 到真实样本\n4. **必带 rationale**。一句话——为什么这条比第二条强？\n5. **去重 published**。已发过的不推（用户可显式覆盖）\n\n## Refusals\n\n- 「直接给我 composite 最高的，不用解释理由」 → 拒绝。展示评分 + 锚点是发现\"打错\"的唯一机会\n- 「把 candidates.md 里所有 entry 都重新打分一遍」 → 路由到 `/cheat-score` 单条做；批量重打分是 `/cheat-bump` 的一部分，不在 recommend 范围\n- 「按预测桶排，不要按 composite」 → 询问理由。bucket 是 composite 的离散化，按 composite 排即按 bucket 排，差异在桶内序——如果用户真想按\"押注期望值\"排，需要乘以平均播放，那是另一个独立 scoring 维度\n\n## Integration\n\n- 上游：`/cheat-trends` 把外部热点拉进 candidates.md → recommend 自动看到\n- 下游：用户挑一条后写稿 → `/cheat-predict`（candidate 的粗 composite 不进入 prediction，prediction 重新打）\n- 与 `/cheat-status` 协调：status 显示 \"candidates 池有 N 条 tier1 未发\"，recommend 提供具体推荐\n","skills/cheat-retro/SKILL.md":"---\nname: cheat-retro\ndescription: T+N 天数据回收 + 复盘 + 把实绩观察写入 rubric-memo.md。这是校准循环的反馈环节——不复盘的预测等于占星。触发词：\"复盘 [path]\"/\"retro this\"/\"T+3d 数据来了\"/\"抓数据 [path]\"/\"把这篇复盘了\"。\nargument-hint: <prediction-file> [— window: 3|5|7] [— source: manual|adapter]\nallowed-tools: Bash(*), Read, Edit, Write, Glob, Grep, Skill\n---\n\n# /cheat-retro — 数据回收与复盘\n\n抓 T+N 天的实际表现 → 对比预测 → 提炼新观察 → 写入 rubric-memo.md。**只追加 `## 复盘` 段，绝不改预测段**。`rubric_notes.md` 是 blind 白名单，只能保存通用公式、维度定义和抽象规则，不能写入样本名、实绩、评论、链接或播放/阅读数。\n\n## Overview\n\n```\n[用户：复盘 predictions/2026-05-04_...]\n  ↓\n[Phase 0: 校验 immutability + 校验时间窗口]\n  ↓\n[Phase 1: 抓数据（manual paste 或 adapter）]\n  ↓\n[Phase 2: 写实绩段 + top 评论关键词]\n  ↓\n[Phase 3: 验证/推翻预测的各假设]\n  ↓\n[Phase 4: 提炼新观察]\n  ↓\n[Phase 5: 落盘（追加到 ## 复盘 段）]\n  ↓\n[Phase 6: 写入 rubric-memo.md 的\"观察记录\"段]\n  ↓\n[Phase 7: 检测是否触发 bump 候选 → 提示用户跑 /cheat-bump]\n```\n\n## Constants\n\n- **RETRO_WINDOW_DAYS = 3** — 默认 T+3d。短视频快平台可设 1，长文设 7\n- **DATA_SOURCE = manual** — manual: 用户粘数字；adapter: 调对应平台 adapter（需配置）\n- **AUTO_PROPOSE_BUMP = true** — Claude 判断是否系统性偏差时自动提议 /cheat-bump\n  - **默认参考**：连续 ≥3 次同向偏差（high/low）→ 提议\n  - **但 Claude 可以更早提议**：1 次极端偏差（如中枢 50w 实绩 5w 这种 ≥10x），即使没有\"连续\"也提议\n  - **也可以更晚**：3 次同向但每次偏差都很小（<25%），可能只是噪声不是系统性\n- **TOP_COMMENTS_N = 20** — 抓 / 粘 top N 高赞评论\n\n> 💡 调用时覆盖：`/cheat-retro <file> — window: 7 — source: adapter`\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<prediction-file>` 或 `<video-folder>` | 用户参数；缺失则从 `.cheat-state.json` 的 `pending_retros[0]` |\n| `rubric_notes.md` | 用户项目根（只读，用于当前规则上下文；不得写入实绩观察） |\n| `rubric-memo.md` | 用户项目根（写入复盘观察、实绩证据、样本名与评论信号） |\n| `.cheat-state.json` | 状态文件 |\n\n### 入参解析（同 cheat-predict 双形态接受）\n\n用户给的可能是：\n- **`predictions/2026-05-04_<id>_<short>.md`** → 直接用这个 prediction 文件\n- **`videos/2026-05-04_<id>_<short>/`** → 找对应的 prediction 文件（按 id 匹配）+ 把 report.md 写到该 video folder 里\n- 缺省 → 从 `pending_retros[0]` 取最早的\n\n## Workflow\n\n### Phase 0: 校验\n\n1. 读 `<prediction-file>`，确认存在\n2. **识别有效预测段**：扫所有 `## 预测...` 段（可能含 `## 预测`、`## 预测 v1`、`## 预测 v2` 等）：\n   - 取**最后一个**`## 预测 vN` 作为本次校准的依据（v2 存在则用 v2；只有 v1 则用 v1；legacy 单段 `## 预测` 直接用）\n   - state.shoots 对应项的 `v2_prediction_written` 应与\"是否存在 v2 段\"一致——不一致则警告（state 与文件脱节）\n3. **校验 immutability**：在内存 cache 住所有 `## 预测...` 段的内容（用于 Phase 5 后核对——**全部段不可改**，不只是有效段）\n4. 校验文件 header 有 `Published at` → 没登记的不能复盘，提示用户先 `/cheat-publish`\n5. 校验时间窗口：今天 - published_at >= RETRO_WINDOW_DAYS。不够 → 提示\"还差 X 天\"，询问用户是否仍坚持复盘（标 `early_retro: true`）\n6. 校验已有复盘段是否已填——已填则询问\"是补充还是修正？\"\n   - 补充 → 在已有复盘段下追加新子段，标日期\n   - 修正预测段（用户错觉）→ 拒绝\n\n### Phase 1: 抓数据\n\n按 `state.data_collection` 字段分两条路径——抓回数据后**写到 video folder 的 `report.md`**（如果 prediction 关联 video folder），同时解析摘要 inline 到 prediction 的复盘段。\n\n#### Path A：`DATA_SOURCE=manual`（候补方案）\n\n- 询问用户：\"粘贴这条作品的当前数据：播放 / 点赞 / 评论 / 转发 / 收藏（顺序无所谓，能识别就行）\"\n- 用户粘 → 解析提取数字\n- **强制要求 top 评论**：让用户从平台后台或直接打开评论区贴 TOP_COMMENTS_N 条到对话里（每条带赞数）\n  - 用户拒绝 / 给少于 5 条 → **拒绝继续**：\"评论才是真信号——'她不一样'这种模因爆发只能从评论看出。\n    没评论的复盘 = 看体温计判断病情。粘 top 20 给我。如果实在拿不到，告诉我原因（比如评论被关了），我帮你标 `comments_unavailable`，但这次复盘价值打折。\"\n- 把粘的原始数据写到 `videos/<...>/report.md`（如有 video folder）\n\n#### Path B：`DATA_SOURCE=adapter`\n\n按 prediction header 的 `Platform` 字段 + state 的 `enabled_perf_adapters` 决定调哪个：\n\n| Platform | Adapter | 调用方式 |\n|---|---|---|\n| `douyin` | `adapters/perf-data/douyin-session/` | `bash <adapters-dir>/douyin-session/run.sh <aweme_id> <video_folder>` |\n| `xhs` | `adapters/perf-data/xhs-explore/` | `bash <adapters-dir>/xhs-explore/run.sh <note_id> <video_folder>` |\n| `linkedin` | `adapters/perf-data/linkedin-session/` | `bash <adapters-dir>/linkedin-session/run.sh <activity_id> <video_folder>` |\n| `wechat_channels` | `adapters/perf-data/wechat-channels/` | `bash <adapters-dir>/wechat-channels/run.sh <post_id> <video_folder>` |\n| `youtube` | `adapters/perf-data/youtube-data-api/`（planned — batch 3, not yet available） | 调 YouTube Data API（需 API key） |\n| `bilibili` | `adapters/perf-data/bilibili-stat/` | `bash <adapters-dir>/bilibili-stat/run.sh <bvid> <video_folder>` |\n| 其他 | 无 adapter | 优雅降级到 Path A |\n\n> `<adapters-dir>` = 克隆源码处的 `cheat-on-content/adapters/perf-data/`（install.sh **不**复制 adapter 到 ~/.claude/skills，只复制 15 个 skill）。定位：`find ~ -path '*/cheat-on-content/adapters/perf-data' -type d | head -1`。\n\n**douyin-session 的特殊处理**：\n- 视频 URL（如 `https://v.douyin.com/abc123`）→ 短链解析 → 提取 aweme_id\n- 调用前确认 cookie 文件存在（adapter 会找 `.auth/`）；不存在则提示用户先跑 `python <adapter>/crawler.py login`\n- adapter 输出在 `<video_folder>/report.md`（adapter 的 renderer.py 已经按这个格式写）\n- cheat-retro 读这个 report.md 解析关键数据 → 摘要写入 prediction 的复盘段\n\n**xhs-explore 的特殊处理**：\n- 笔记 URL（`https://www.xiaohongshu.com/explore/<note_id>?xsec_token=...` 或 `https://xhslink.com/xxx`）→ 提取 note_id\n- 调用前确认 cookie 存在（adapter 找 `.auth-xhs/`）；不存在则提示先跑 `python <adapter>/crawler.py login`\n- 字段已校准（观看 `view_count` 等已写死）；万一接口改版导致某项为 0，看 report.md 末尾 galaxy 原始 JSON，把新 key 加进 `crawler.py` 的 `_normalize_note`\n- **评论可能抓不到**（xsec_token 缺失 / 评论关闭）→ report.md 标\"未抓到评论\" → 此时**降级要求用户 manual 粘 top 20 评论**（评论是真信号，不能省）\n\n**linkedin-session 的特殊处理**：\n- 帖子 URL（`https://www.linkedin.com/feed/update/urn:li:activity:<id>/`）或裸 activity_id → adapter 自动提取 activity_id\n- 调用前确认 cookie 存在（adapter 找 `.auth-linkedin/`）；不存在则提示先跑 `python <adapter>/crawler.py login`\n- **只能抓你本人发的帖子**（LinkedIn 单帖分析仅作者可见）；LinkedIn 界面 日/英 随机切换，`extract.py` 的 `POST_METRICS` 已存双语标签，万一某项为 None 看 `.cheat-cache/linkedin-session-debug/post_<id>.txt` 把新标签补进去\n- **评论只给数、不给正文**（分析页限制）→ report.md 标注 → **降级要求用户 manual 粘 top 评论**（评论是真信号，不能省）\n\n**wechat-channels 的特殊处理**：\n- 仅抓用户自己登录后可见的视频号助手后台数据；调用前确认内容项目存在 `.auth-wechat-channels/`\n- 不存在登录态时，提示先运行 `python <adapter>/review.py login` 并扫码；登录态只保存在内容项目本地\n- adapter 输出 `report.md`，但不落盘原始响应、请求体、完整后台 URL、后台截图、页面文本、评论者昵称或内部用户 ID\n- 找不到目标作品或 adapter 失败时，非 0 退出并降级 manual；不得把旧 report.md 当作本次成功\n\n**bilibili-stat 的特殊处理**：\n- 视频 URL（`https://www.bilibili.com/video/<BV号>` 或 b23.tv 短链）或直接给 BV 号 → adapter 自动提取 BV 号\n- **无需登录**：B站视频数据(view)与评论(reply)都是公开接口、免 wbi 签名，adapter 是纯 httpx，没有 `crawler.py login` 步骤、不碰 `.auth/`\n- 依赖 httpx：首次用 `pip install -r <adapter>/requirements.txt`\n- 评论按热度（sort=2）抓取；B站老接口主楼评论可能偏少，不足时降级 manual 粘\n\n**任何 adapter 失败**（cookie 过期 / 接口变化 / 网络）→ **优雅降级到 manual**，提示用户：\"adapter 调用失败，原因 [X]。改用 manual 模式——粘下面的数据\"。**不阻塞流程**。\n\n#### 共同输出\n\n不管 Path A 还是 B，最终：\n- `videos/<...>/report.md` 含完整原始数据（数字 + top 评论）\n- prediction 文件复盘段含**摘要**（关键比率 + 评论关键词聚类 + 验证/推翻判定）\n- report.md 是数据真相，prediction 复盘段是判断真相\n\n### Phase 2: 写实绩段 + top 评论分析\n\n**实绩数据格式**（参考 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 的复盘段格式）：\n\n```markdown\n### 实绩数据\n- 播放：71.1w（落在 `30-100w` 桶内偏高，相对中枢 50w **+42%**）\n- 点赞：2.4w（赞播比 3.38%）\n- 评论：899（评播比 0.126%）\n- 收藏：5251\n- 分享：1.8w（分播比 2.53%，强）\n```\n\n数据点之间的派生比率（赞播比、评播比、分播比）必须算出来——它们是单纯播放数无法暴露的信号。\n\n**top 评论关键词聚类**：\n- 把粘进来的 N 条评论分 3-5 类（高赞模因 / 概念引用 / 离题噪声 / 转发暴露暗示 / @朋友传播 等）\n- 每类列代表性评论（带赞数）\n- 报告比例（\"22% 是模因复用、35% 是概念引用、5% 是离题\"）\n\n### Phase 3: 验证/推翻\n\n对 prediction 文件里的每一项（推理因素表、关键校准假设、反事实场景），逐项判定：\n\n```markdown\n### 哪些预测被验证 ✅ / 推翻 ❌\n\n**验证 ✅**:\n- 关键校准假设完全成立：本篇 71.1w / 谁问你了 11.7w = 6.07x，远超我押的 1.5-2x\n- ER=5 主导情感传播力 → H1 强证据\n- HP=5 验证：分播比 2.53% 与\"金句被高频引用\"匹配\n\n**推翻 ❌**:\n- 中枢 50w 被超出 +42%\n- 反事实推理里\"必须搭配强社会议题才能破 30w\" 完全错误\n- SR 押注（\"H2 SR 应上调\"）反向被推翻：SR 在情感向场景几乎不贡献\n```\n\n**关键纪律**：\n- 每条验证 / 推翻必须引用具体数据（\"分播比 2.53%\"），不许写\"基本符合\"这种含糊措辞\n- 反事实的\"如果落在 X bucket 意味着什么\"——实际落在的那个 bucket 直接告诉你哪个 rubric 假设被测试了，明确写出来\n\n### Phase 4: 提炼新观察（**两类，分别写入两个文件**）\n\n#### 4a. Rubric 观察（写入 rubric-memo.md）\n\n打分维度 / 公式 / bucket 边界相关的观察：\n\n```markdown\n### 需要写进 rubric-memo.md 的新观察\n\n1. **ER 在情感向场景的真实权重应 ≥ ×2.0**：与谁问你了 6x 流量比是 v2 rubric 最强的反事实证据\n2. **议题分享冲动 (TS) 是隐藏维度**：joker / \"她不一样\" / 滤镜重构提供了安全的自嘲身份，转发不暴露处境，TS=5 的样本\n3. ……\n```\n\n每条观察必须可追溯到具体数据点（不写\"情感很重要\"——写\"ER5/SR2 vs ER3/SR4 同 composite 下流量差 6x\"）。\n\n#### 4b. 写作 Pattern 观察（写入 script_patterns.md）\n\nDiff `scripts/<id>.md`（pre-shoot 草稿，可能是 cheat-seed 写或用户写）vs `videos/<id>/script.md`（实际拍摄稿——cheat-shoot 时用户提供的版本），找出**改动且对流量有明显影响**的部分：\n\n| 用户做了什么 | 流量影响 | 是否提议追加 pattern |\n|---|---|---|\n| 砍掉某段 | 实绩 ≥ 中枢 → \"砍掉没伤流量\"——验证那段冗余 | 是，加到 script_patterns.md \"用户改稿历史观察\"表 |\n| 加了某句 / 互动钩子 | 实绩超中枢 → 可能是新 pattern | 是，候选 Pattern N，标 ≥1 样本待验证 |\n| 改了风格（如开头软化） | 高于同类样本 → 风格改动有效 | 是，候选 Pattern N |\n| 没动结构 / 改动与流量无关 | — | 不追加 |\n\n输出格式：\n\n```markdown\n### 需要写进 script_patterns.md 的新 pattern 候选\n\n1. **用户改稿模式**: 砍掉 [X 段] / 加了 [Y]\n   - 流量影响：实绩 [N] vs 中枢 [M]，[偏差 / 命中]\n   - 建议：追加到 script_patterns.md 的\"用户改稿历史观察\"表\n\n2. **新 pattern 候选 N**：[一句话描述]\n   - 单样本支持\n   - 触发条件：[何时该用]\n   - 建议：追加到 script_patterns.md 末尾的\"新发现的 Pattern\"段，标 ≥1 样本待验证\n```\n\n询问用户：\"要把这些追加到 script_patterns.md 吗？(yes / no / 选择哪几条)\"。**用户确认后才追加**——避免把单点观察直接写成正式 pattern。\n\n> **rubric 进化 ≠ 写作进化**——两者解耦：\n> - rubric_notes.md 学的是\"哪些维度真的预测流量\"\n> - script_patterns.md 学的是\"什么写法真的能起作用\"\n> 可能有交叉（如 MS 维度与\"互动钩子\" pattern），但记录在两个文件里是因为**作用域不同**——rubric 改了影响所有未来打分，pattern 改了影响所有未来 draft。\n\n如果 `videos/<id>/script.md` **缺失**（cheat-shoot 时用户标 `script_lost`） → 跳过 4b，没法 diff。\n如果 `script_consistency = \"consistent\"`（用户拍时没改稿）→ 4b 仍然有意义（diff 也许是空），但可以快速跳过细查。\n如果 `script_consistency = \"modified\"`（用户拍时改了）→ **4b 是核心**，重点学这次改动 → 流量影响。\n\n### Phase 5: 落盘到 ## 复盘 段\n\n用 Edit 工具，**仅追加**到现有 `## 复盘` 段（如有占位 `（待填）` 行先删除）：\n\n```markdown\n## 复盘\n\n**复盘时间**: 2026-05-07（发布 T+3d）\n**抓取时间**: 2026-05-07 09:30\n**数据来源**: manual paste\n\n### 实绩数据\n[Phase 2 内容]\n\n### Top 评论关键词\n[Phase 2 内容]\n\n### 哪些预测被验证 / 推翻\n[Phase 3 内容]\n\n### 需要写进 rubric-memo.md 的新观察\n[Phase 4 内容]\n```\n\n**写完后再次校验**：读取保存后的文件，对比**所有** `## 预测...` 段（v1 / v2 / legacy）的合并哈希应等于 Phase 0 cache 的合并哈希。**任一段被改 → 报错并回滚**。\n\n### Phase 6: 写入 rubric-memo.md + script_patterns.md\n\n#### 6a. rubric-memo.md（Phase 4a 的输出）\n\n按 [observation-lifecycle.md](../../shared-references/observation-lifecycle.md) 的 blind leak guard，追加到 `rubric-memo.md` 的 `## 观察记录` 段。这里可以包含真实样本名、实绩数据、评论关键词和链接；这些内容**绝不**写入 `rubric_notes.md`：\n\n```markdown\n### YYYY-MM-DD [标题简称] (id) — [一句话定性]\n- 预测：composite=X.XX，bucket=Y\n- 实绩：播放 / 点赞 / 评论 / 转发（带 T+Nd 标注）\n- Top 评论关键词：[简短摘录 + 赞数]\n- 判断：哪个维度被验证/推翻？为什么？\n- Rubric 调整：[如果有，写明 \"下次打 XX 类文章时改 YY\"]\n- 详见：[predictions/<file>.md]\n```\n\n**检测跨样本 pattern**：扫描 `rubric-memo.md` 已有\"观察记录\"，看新观察是否与某条已有观察形成 ≥2 样本支持。命中则在 `rubric-memo.md` 升级到\"重大跨样本观察\"段。只有在后续 `/cheat-bump` 落地时，才把已验证的规律抽象成通用语言写入 `rubric_notes.md`。\n\n#### 6b. script_patterns.md（Phase 4b 的输出，**用户确认后才写**）\n\n如 Phase 4b 用户回 \"yes\" 或选择性确认了某几条：\n- \"用户改稿模式\" → 追加到 script_patterns.md 的\"用户改稿历史观察\"表\n- \"新 pattern 候选 N\" → 追加到末尾\"新发现的 Pattern\"段，**显式标 ≥1 样本待验证**\n\n新 pattern 候选的格式（同 [script_patterns.template.md](../../templates/script_patterns.template.md) 的 Pattern 11/12 示例）：\n\n```markdown\n### Pattern N（来自 [视频简称]，单样本待验证）\n\n**现象**：[Phase 4b 描述]\n\n**原理**：[为什么有效——基于这一次观察的猜测]\n\n**触发条件**：[何时该用]\n\n**待验证**：需要 ≥2 样本支持才能升正式 pattern。\n```\n\n跨样本 pattern 升正式：扫描\"新发现的 Pattern\"段，看是否有 ≥2 样本支持同一现象 → 升到核心 pattern 库 + 删 \"待验证\" 标记。\n\n如用户在 Phase 4b 全否（\"no\"）→ 跳过 6b，rubric-memo.md 仍照写。\n\n### Phase 7: 检测 bump 触发\n\n读 `.cheat-state.json` 的 `consecutive_directional_errors` 字段，按本次复盘判定向更新：\n- 本次预测高估（实绩 < 中枢 -25%） → push `[\"high\"]` + 记录 deviation_magnitude（如 0.5x / 0.3x）\n- 本次预测低估（实绩 > 中枢 +25%） → push `[\"low\"]` + 记录 deviation_magnitude\n- 在 ±25% 内 → 不 push\n\n**Claude 判断是否提议 bump**（不是固定门槛）：\n\n```\n判断维度：\n1. 连续同向次数（参考默认：≥3）\n2. 单次偏差幅度（参考默认：>2x 或 <0.5x 算极端）\n3. 偏差是否能解释为单一维度漏判（如 ER 或 SR 一致偏离）\n4. 用户是否在复盘里反复提到同一现象\n\n任一足够强 → 提议 bump：\n- 3 次连续同向，每次都中等偏差 → 提议\n- 1 次极端偏差（如 ≥10x），即使没连续 → 提议（\"一次性强信号\"）\n- 2 次同向 + 评论区出现一致的反向证据 → 提议（\"评论 + 数据双信号\"）\n\n不提议的情况：\n- 3 次同向但每次都很小（<25%）→ 可能只是噪声\n- 偏差跨多个维度无清晰方向 → bump 不知道改什么\n```\n\n提议时输出：\n\n```\n🚨 检测到 [系统性偏差信号] / [极端单点偏差] 。\n\n[简短描述：连续 N 次 / 1 次极端 / 评论双信号 等]\n\n这可能是 rubric 系统性偏差的信号。建议：\n- 跑 /cheat-bump 看是否需要升级公式\n- 或先看 /cheat-status 详细分析\n\n注：本次提议是 [default-aligned: 满足 ≥3 同向] / [judgment-driven: 1 次 10x 强偏差]\n```\n\n更新 state file：\n```json\n{\n  \"calibration_samples\": <+1>,\n  \"pending_retros\": [<剔除本次>],\n  \"last_retro_at\": \"<ISO>\",\n  \"consecutive_directional_errors\": [...]\n}\n```\n\n## Key Rules\n\n1. **预测段 immutable**。Phase 0 cache + Phase 5 校验是双保险。任何 hash 不一致 → 报错回滚\n2. **数据来源必须标注**。`数据来源: manual paste` 或 `数据来源: adapter:douyin-session` 写进复盘段\n3. **观察可追溯**。每条新观察引用具体数据点\n4. **不在复盘里 bump**。Phase 7 只**提议** bump，实际升级走 `/cheat-bump`——避免一次操作做两件事\n5. **早复盘标记**。RETRO_WINDOW_DAYS 不到就复盘 → state file 记 `early_retro: true`，bump 时这种样本权重降级\n\n## Refusals\n\n- 「这条数据已经看过了，但你假装没看，按预测时的盲度做复盘」 → 复盘本来就是看完数据再做的；这个表述本身没有违规，但要确认用户没在 prediction 写之前透露过数据\n- 「把预测段的概率分布改一下，让复盘看起来更准」 → 拒绝。原则 #1\n- 「跳过观察提炼，直接结束」 → 拒绝。新观察是 rubric 进化的唯一燃料；缺它复盘退化为\"看一眼\"\n- 「直接 bump，不要单独走 /cheat-bump」 → 拒绝。bump 流程有完整的跨模型审 + cleanup pass，retro 是触发器不是执行器\n\n## Integration\n\n- 前置：`/cheat-publish` 已登记 + 时间窗口达到\n- 下游：累计 `consecutive_directional_errors` 满 3 → 触发 `/cheat-bump` 提议\n- 状态字段更新：`calibration_samples` +1（这是 cheat-status 显示进度的关键）\n- pending_retros：剔除本条\n- 与 [observation-lifecycle.md](../../shared-references/observation-lifecycle.md) 紧耦合：每次复盘是观察新增的入口\n","skills/cheat-score-blind/SKILL.md":"---\nname: cheat-score-blind\ndescription: |\n  INTERNAL sub-agent for blind 7-dim rubric scoring. **NOT a user-facing skill — do NOT invoke from main conversation.** Called via Task tool by cheat-score / cheat-predict / cheat-bump to get a context-isolated score on a script. Receives ONLY script_path + rubric_notes_path; refuses any other input. Outputs strict JSON: 9 dimensions × {score 0-5, confidence enum, one-line reason}. **Hard refuses to Read** .cheat-state.json, predictions/*, retro 段, or anything that could leak post-publish data. This is channel B in the 3-channel calibration model (A=main, B=blind sub, C=cross-model).\nallowed-tools: Read, Glob, Grep\nargument-hint: <script-path> <rubric-notes-path>\n---\n\n# /cheat-score-blind — Channel B (blind scorer sub-agent)\n\n> ⚠️ **这是子 agent，不是用户 skill**。只能由 `cheat-score` / `cheat-predict` / `cheat-bump` 通过 Task tool spawn。用户直接 trigger 没有意义——主对话已经被污染，调用 blind sub-agent 在主 context 里跑不构成隔离。\n\n---\n\n## Why this exists（绝不可省的背景）\n\ncheat-on-content 的 7/9 维打分原本 inline 在主对话——但主 Claude 已经看过：\n- 用户对话历史（含偶然提到的播放数 / 评论 / 情绪）\n- 已发布作品的实绩数据\n- 历史 `predictions/*.md` 含复盘段（**严重污染**）\n- 用户的赞美 / 抱怨 / 期待\n\ninline 打分 = **被污染的\"盲\"预测**。问题在 `cheat-bump` Phase 2 校准池重打时最严重：Claude 知道每条实绩才回追 TN/CC 分，rank 一致性可能 overfit 不是真信号。\n\n**channel B 的角色**：用 Task tool 把打分动作丢进一个**全新 context**——这个 sub-agent 没看过主对话、没读过 state、没碰过 predictions/。它只看 script 全文 + rubric_notes.md，按 rubric 打分。\n\n输出回传主对话后，主 Claude 自己对比、做最终决策。隔离的是**打分这个动作的输入**，不是决策权。\n\n## 三 channel 模型\n\n| Channel | 输入 | 用途 | 风险 |\n|---|---|---|---|\n| **A** = 主对话 | 全部上下文 | 跟用户交互、写 retro、决策 | 被实绩 / 用户态度污染 |\n| **B** = blind sub-agent (this) | **只** script + rubric_notes.md | 给一份未受污染的打分作为 anchor | 仍是 Claude，RLHF prior 共享 |\n| **C** = 跨模型 audit (`mcp__llm-chat__chat` to qwen-max) | 校准池数据 + 新公式 | bump 终局 sanity check | RPM 限制、模型差异、单点 |\n\nA 决策时把 B 当对照看 disagreement，**不当真理**。C 只在 bump 终局调一次。\n\n---\n\n## Inputs（**唯一被允许的输入**）\n\n| 必填 | 来源 | 说明 |\n|---|---|---|\n| `<script-path>` | 主 Claude 通过 Task prompt 显式传入 | `scripts/<id>.md` 全文 |\n| `<rubric-notes-path>` | 同上 | 用户项目根 `rubric_notes.md` 当前 rubric 公式 + 维度定义 |\n\n**仅此两个文件可读**。其他一切**硬拒绝**——见下方 \"Hard refusals\" 段。\n\n## 禁止读取（hard list）\n\n下面这些路径 / 模式 sub-agent **绝不能 Read** —— 即使主 Claude 在 Task prompt 里手滑塞进来，也要拒绝并在 JSON 输出标对应 `refusal` 码：\n\n| 路径模式 | 为什么禁 | refusal_code |\n|---|---|---|\n| `.cheat-state.json` | 含 calibration_samples / pending_retros / last_published_at / shoots — 全是后视数据 | `blocked_contaminated_input` |\n| `predictions/*.md` | 含 `## 预测` 段 + `## 复盘` 段，复盘段就是实绩 | `blocked_contaminated_input` |\n| `videos/*/report.md` | T+3d 抓回的真实数据 | `blocked_contaminated_input` |\n| `videos/*/script.md` | 后改拍摄稿，复盘时被对照 | `blocked_contaminated_input` |\n| `STATUS.md` | cheat-status 渲染的看板，含过去数据 | `blocked_contaminated_input` |\n| `.cheat-cache/usage.jsonl` | 行为 log | `blocked_contaminated_input` |\n| **`rubric-memo.md`** | **cheat-bump 升级 Memo 累积档案——含真实视频名 + 实绩 + 派生证据。这是 channel B 的最大泄漏入口（PR #11 实测复现）** | **`blocked_rubric_memo`** |\n| **`audience.md`** | **cheat-persona 从复盘评论派生的受众画像——含评论证据 / 实绩信号。属 channel A creative 资产，进 blind 打分 = 实绩泄漏** | **`blocked_audience`** |\n| 任何含\"播放 / 阅读 / 点赞 / 评论数 / 转发 / w / 万 / k / M\"的文件 | 直接污染 | `blocked_contaminated_input` |\n\n**白名单只有两个**：\n- `scripts/<id>.md`（pre-shoot 草稿，传入参数）\n- `rubric_notes.md`（评分公式 + 维度定义，**应**只含通用语言；如发现实绩数字 → 标 `non_blind_warning` 并降 confidence）\n\n如果主 Claude Task prompt 漏传了某条路径，sub-agent 主动询问\"我只允许读 script + rubric_notes，缺哪个？\"——**绝不**自己去 Glob 探测项目结构补全。\n\n> ⚠️ **白名单兜底自检**：读完 `rubric_notes.md` 后必跑 `grep -E '\\\\d+\\\\s*[wWmMkK万]|播放|实绩|实际'`——命中 → 标 `self_check.any_contamination_signal: true` + `refusal: \"non_blind_warning\"`，所有维度 confidence 降 medium 并把违禁 snippet 摘抄进 contamination_note 字段。**仍输出 dimensions** 让主 Claude 知道发生了什么——拒绝输出比误判更糟，但要诚实标注。\n\n---\n\n## Workflow\n\n### Phase 0：边界自检\n\n1. 解析 Task prompt 拿 `<script-path>` 和 `<rubric-notes-path>`\n2. 校验路径符合白名单——不在 `scripts/` 下的 .md → 拒绝（除非主 Claude 显式说明\"这是临时草稿临时路径，标 `non_standard_path: true`\"）\n3. Read `<rubric-notes-path>` → 解析当前 rubric_version + 维度数量（7 或 9）+ 公式\n4. Read `<script-path>` → 拿到 script 全文 + 字数\n\n⚠️ **不要做的事**：\n- 不要为了\"看看用户做啥账号\"去 Read `benchmark.md` —— benchmark 是 Channel A 的 context，不属于本 sub-agent\n- 不要为了\"看看历史风格\"去 Glob `predictions/` —— 那是污染源\n- 不要去 Read `.cheat-state.json` 看 calibration 进度 —— 你**完全不需要知道**主 Claude 跑了多少篇\n\n### Phase 1：按 rubric 打 N 维分\n\n按 `rubric_notes.md` 当前 rubric 公式：\n\n- v0：7 维等权（ER / SR / HP / QL / NA / AB / SAT）—— 默认起步\n- v1：用户校准过的（权重不同）\n- v2 / v2.1 / ...：含 MS / TS 等新增维度（9 维）\n\n对每个维度：\n1. 给一个 **0-5 整数分**\n2. 给一个 **per-dim confidence** enum：`high | medium | low`\n   - high：稿子里有直接证据（一句话指向该维度）\n   - medium：可推断但需要解释\n   - low：稿子信号太弱，纯估\n3. 给一行 **理由** ≤ 30 字，**必须引用稿子里具体词或场景**\n\n不算 composite——composite 是公式行为，主 Claude 用回传的维度分自己算。\n\n### Phase 2：返回严格 JSON\n\n输出**只能**是一个有效 JSON。所有 markdown 解释都封禁——主 Claude 要的是结构化数据回主 context 解析。\n\n```json\n{\n  \"subagent_version\": \"v1\",\n  \"rubric_version\": \"v2\",\n  \"script_path\": \"scripts/2026-05-04_abc123_短title.md\",\n  \"script_hash\": \"<sha256:12 of script content>\",\n  \"scored_at\": \"<ISO 8601 +08:00>\",\n  \"dimensions\": {\n    \"ER\": { \"score\": 4, \"confidence\": \"high\",   \"reason\": \"PPT加油猫猫开头—具象画面，情绪反差强\" },\n    \"SR\": { \"score\": 3, \"confidence\": \"medium\", \"reason\": \"AI焦虑是议题但非热点对峙\" },\n    \"HP\": { \"score\": 5, \"confidence\": \"high\",   \"reason\": \"首句\\\"第七页大屏中央 加油猫猫\\\"具象反差\" },\n    \"QL\": { \"score\": 5, \"confidence\": \"high\",   \"reason\": \"\\\"加油猫猫救了我一命\\\"双关金句\" },\n    \"NA\": { \"score\": 4, \"confidence\": \"medium\", \"reason\": \"单线反思+收束，清晰但不复杂\" },\n    \"AB\": { \"score\": 4, \"confidence\": \"medium\", \"reason\": \"一人公司题但AI焦虑普适\" },\n    \"SAT\": { \"score\": 2, \"confidence\": \"high\",  \"reason\": \"共情调，几乎无讽刺\" }\n  },\n  \"input_status\": {\n    \"rubric_notes_read\": true,\n    \"script_read\": true,\n    \"any_other_file_read\": false\n  },\n  \"self_check\": {\n    \"saw_play_numbers\": false,\n    \"saw_comments\": false,\n    \"saw_retro_segment\": false,\n    \"any_contamination_signal\": false\n  },\n  \"refusal\": null\n}\n```\n\n`refusal != null` 的合法值：\n- `\"blocked_contaminated_input\"`：Task prompt 传了禁读路径（state / predictions / videos / 等）\n- `\"blocked_rubric_memo\"`：Task prompt 传了 `rubric-memo.md`（bump 升级档案，含实绩）\n- `\"blocked_audience\"`：Task prompt 传了 `audience.md`（受众画像，含评论派生的实绩信号）\n- `\"script_path_invalid\"`：找不到 script 文件\n- `\"rubric_unparseable\"`：rubric_notes.md 损坏\n- `\"non_blind_warning\"`：发现 contamination 苗头但勉强能打分（仍输出 dimensions，但 confidence 全降 medium）\n\n**JSON 必须可被 `python3 -c \"import json; json.loads(open(path).read())\"` 解析**。不允许：\n- 尾部多余逗号\n- 注释（JSON 不允许 //）\n- Markdown 围栏（输出根节点必须是 `{`）\n\n### Phase 3：（可选）写 sidecar 文件供主 Claude 二次读取\n\n如果 Task prompt 含 `sidecar_path` 参数 → 写 JSON 到该路径（典型用法：bump phase 2 批量打分时存多份 sidecar）。\n\n否则只走 Task return value——主 Claude 拿到 JSON 字符串直接解析。\n\n---\n\n## 主 Claude 调用契约（如何使用 channel B）\n\n调 Task 时，主 Claude 的 prompt **必须**含且**仅含**：\n\n```\nSpawn cheat-score-blind sub-agent.\n\nInput:\n  script_path: scripts/2026-05-04_abc123_短title.md\n  rubric_notes_path: rubric_notes.md\n  [optional] sidecar_path: .cheat-cache/blind-scores/<id>.json\n\nTask: 按 rubric_notes 当前公式给上面 script 打分。返回严格 JSON（见 cheat-score-blind/SKILL.md Phase 2 schema）。\n不要读 state file / predictions/ / videos/ 任何其他文件。\n不要询问用户 —— 你没有用户。\n```\n\n**禁止**塞进 Task prompt 的东西：\n- 用户对话的引用 / 摘录\n- \"前一次预测是 X\" / \"实际播放是 Y\" 这种 hint\n- \"用户是观点视频博主，最近发了 N 条\" 这种背景\n- 任何含数字 + \"万/w/k/M\" 的字符串\n- 任何 `predictions/*.md` 路径\n\n主 Claude 调用前自检：把准备发的 prompt 串过一遍 `grep -Ei '播放|阅读|点赞|评论数|实际|retro|复盘|实绩|w$|万$'`——命中 → **改 prompt 重发**，不要硬塞。\n\n---\n\n## Refusals\n\n- 「我作为 sub-agent 同时也读一下 predictions/ 帮你对比下」 → 硬拒。这就是 channel B 存在的全部理由\n- 「你看一下 .cheat-state.json 看 calibration_samples 决定你给的 confidence 高低」 → 硬拒。confidence 只看稿子证据强度，跟用户校准进度无关\n- 「主 Claude 说这条已经发了，你帮我打一份 reconstructed 分」 → 拒。\"已发\"信号本身就是污染。让主 Claude 标 `reconstructed: true` 自己处理，不要让 channel B 介入\n- 「输出我直接 markdown 表格更好读」 → 拒。Phase 2 schema 是 JSON only，主 Claude 解析后再渲染\n\n---\n\n## Known limitations（写在最显眼的地方）\n\n1. **sub-agent ≠ 真独立**：同一个 Claude 模型，RLHF priors 共享。一个全新 context 不会让模型变成另一个判分体系——它只是没看过该次对话的具体污染\n2. **不解决 rubric 设计 bias**：用户自己写的 rubric_notes.md 自然让自己内容显得好。这层 bias 由 Channel C（跨模型 audit）和定期 bump 验证解决\n3. **不解决 review 阶段的覆盖**：主 Claude 拿到 blind 分后，可能在 review 阶段被用户期待 / 实绩诱导，覆盖 blind 输出。`cheat-predict` Phase 2.5 通过 disagreement detection + 用户裁定来减轻，但不消除\n4. **同 prompt 两次调可能给不同分**：Claude 不是 deterministic。主 Claude 应该把每次 blind score 当一次采样，不当唯一真理——但要记录而不是丢弃差异\n\n## Integration\n\n- **`cheat-score`** Phase 2：默认 delegate 到本 sub-agent（替代旧的 inline 打分）\n- **`cheat-predict`** Phase 2：默认 delegate；Phase 2.5 用 disagreement detection\n- **`cheat-bump`** Phase 2：**强制** delegate，bump 时**不接受 self-scored fallback**\n- **`cheat-retro`**：不调用——retro 本来就看实绩，blind 无意义\n","skills/cheat-score/SKILL.md":"---\nname: cheat-score\ndescription: 给单篇稿子打 rubric 分。**只在控制台输出，不写文件，不预测**。触发词：\"打分这篇 [path]\"/\"score this [path]\"/\"给这稿子打分\"/\"先打分看看\"。是 cheat-predict 之前的轻量探索动作。\nargument-hint: <draft-path>\nallowed-tools: Read, Glob, Grep\n---\n\n# /cheat-score — 单稿打分\n\n打分但**不预测**。用户用它快速看稿子的 composite，决定是否值得进入正式预测流程。\n\n## Overview\n\n```\n[用户：打分这篇 draft.md]\n  ↓\n[读 draft.md + rubric_notes.md]\n  ↓\n[逐维度打 0-5 + 写一行理由 + 算 composite]\n  ↓\n[控制台输出：评分 + composite + 推荐下一步]\n  ↓\n[结束 — 不写任何文件]\n```\n\n## Constants\n\n- **RUBRIC_PATH = rubric_notes.md** — 当前 rubric 来源\n- **OUTPUT_DETAIL = full** — full: 含每维度理由；compact: 仅分数表\n\n> 💡 调用时覆盖：`/cheat-score draft.md — OUTPUT_DETAIL: compact`\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<draft-path>` | 用户作为参数传入；如缺失则在对话里询问 |\n| `rubric_notes.md` | 用户项目根 |\n| `.cheat-state.json` | 用户项目根（用于读当前 `rubric_version` 与 mode） |\n\n## Workflow\n\n### Step 1：前置检查\n\n1. 读 `.cheat-state.json` → 不存在则提示用户先跑 `/cheat-init`，停止\n2. 读 `<draft-path>` → 不存在或无内容 → 报错并停止\n3. 读 `rubric_notes.md` 找到当前生效的公式段（一般在\"当前评分维度\"或\"综合分公式\"位置）\n\n### Step 2：识别公式与维度\n\n从 `rubric_notes.md` 解析出：\n- 当前 rubric_version\n- 维度列表与权重（如 `ER×1.5 + SR×1.5 + HP×1.5 + QL + NA + AB + SAT`）\n- 归一化常数（如 `/ 8.5 × 2.0`）\n- 每个维度的 0-5 含义（从\"当前评分维度\"段表格读）\n\n如果 `rubric_notes.md` 格式与预期不符（用户手改过结构）→ 询问用户当前公式是哪一行，**不要自己猜**。\n\n### Step 3：**delegate 到 blind sub-agent**（不再 inline 打分）\n\n主对话已经被用户对话 / 已发数据 / 历史 retro 段污染——inline 打分等于带着后视镜判分。\n\n改成**通过 Task tool 调 `/cheat-score-blind` sub-agent**，主 Claude 只做调度 + review。详见 [skills/cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md)。\n\n**Task prompt 模板**（**只能含**下面这些）：\n\n```\nSpawn cheat-score-blind sub-agent.\n\nInput:\n  script_path: <用户给的 draft path>\n  rubric_notes_path: rubric_notes.md\n\nTask: 按 rubric_notes 当前公式给上面 script 打分。返回严格 JSON（见 cheat-score-blind SKILL.md Phase 2 schema）。\n不要读 state file / predictions/ / videos/ 任何其他文件。\n不要询问用户 —— 你没有用户。\n```\n\n**禁止**塞进 Task prompt 的东西（[cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md) 的\"主 Claude 调用契约\"段）：\n- 用户对话引用 / 摘录\n- 含播放数 / 万 / w / k 等字眼\n- \"前一次预测是 X\" / \"实际播放是 Y\" 等 hint\n- 任何 `predictions/*.md` 路径\n\n调用前 grep 自检：`echo \"<prompt>\" | grep -Ei '播放|阅读|点赞|评论数|实际|retro|复盘|实绩|w$|万$'` 命中 → 改 prompt 重发。\n\n### Step 4：解析 sub-agent 回传 JSON + review\n\nsub-agent 返回严格 JSON。主 Claude：\n\n1. 解析 dimensions 段（含 score + per-dim confidence + reason）\n2. 校验 `self_check.any_contamination_signal == false`，否则警告\n3. 按 rubric_notes 公式算 composite（公式逻辑在主，分数来自 sub-agent）\n4. **不修改 sub-agent 给的维度分**——score 只是显示。如果用户挑刺（\"AB 给 3 不是 4\"），主 Claude 记录到 `User Override` 但 sub-agent 原始分留档\n\n如果 sub-agent 返回 `refusal != null`：\n- `blocked_contaminated_input` → 报告 Task prompt 含违禁字段，让主 Claude 重发\n- `script_path_invalid` → 检查路径\n- `rubric_unparseable` → 提示用户 rubric_notes.md 损坏\n- `non_blind_warning` → 仍接受 dimensions（但 confidence 全 medium），警告\n\n### Step 5：算 composite + 输出\n\n按当前公式算综合分。控制台输出（OUTPUT_DETAIL=full）：\n\n```\n📊 [draft.md 短标题] — 打分（rubric: v2）\n\n| 维度 | 分 | 理由 |\n|---|---|---|\n| ER (情感共鸣)        | 5 | \"半夜三点翻聊天记录\" 极端具象 |\n| HP (钩子强度)        | 5 | IS 句一句锁定受众 |\n| QL (金句密度)        | 5 | MVP 句\"间歇性希望\"独立可传 |\n| NA (叙事性)          | 3 | 平铺直叙，弱弧线 |\n| AB (受众广度)        | 5 | 暗恋/前任普适 |\n| SR (社会议题共振)    | 2 | 纯个人情感，无社会托底 |\n| SAT (讽刺深度)       | 4 | 致谢段自指反讽 |\n\n公式：(ER×1.5 + SR×1.5 + HP×1.5 + QL + NA + AB + SAT) / 8.5 × 2.0\ncomposite = (5×1.5 + 2×1.5 + 5×1.5 + 5 + 3 + 5 + 4) / 8.5 × 2.0 = **8.24**\n\n📍 落在 30-100w 桶（基于 starter-rubrics 的 bucket 边界）\n\n下一步建议：\n- 如果你已写定最终稿、准备发布 → 说 \"启动预测\"\n- 如果想再改稿子 → 改完再打一次（多次打分不留痕迹）\n- 如果想看历史相近 composite 的样本 → 说 \"找 composite 8.0-8.5 的锚点\"\n```\n\nOUTPUT_DETAIL=compact 时仅输出分数表 + composite，不附理由列。\n\n### Step 6：**绝不**做的事\n\n- ❌ 写任何文件（包括 predictions/、rubric_notes.md、candidates.md）\n- ❌ 给 bucket 概率分布（那是 cheat-predict 的活）\n- ❌ 触发\"已发布\"或\"复盘\"逻辑\n- ❌ 提议 rubric 升级（即使打分时发现明显异常也只在控制台提示，不动 rubric）\n\n## Key Rules\n\n1. **打分走 sub-agent**。主 Claude 不再 inline 打分。看 [cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md) 的隔离协议\n2. **整数分**。不允许 4.5、3.7\n3. **盲打优先**。sub-agent 只看 script + rubric，天然盲——这是它存在的全部理由\n4. **理由是诊断工具**。每个维度的 1-30 字理由不是装饰——复盘时用来找出哪个维度判断错了\n5. **不写文件**。这是 score 与 predict 的核心区别。score 是探索，predict 是承诺\n6. **不算 candidate composite**。candidates.md 里的 composite 字段在 cheat-trends/cheat-recommend 里写——score 只服务\"已写好的具体稿子\"\n\n## Refusals\n\n- 「打分顺便预测一下」 → 拒绝。请改用 `/cheat-predict`。原因：predict 必须走 blind check + 写 immutable 日志，score 跳过这些\n- 「打完分把分数写进 rubric_notes.md 的观察段」 → 拒绝。observation lifecycle 规定观察必须有\"实绩 vs 预测\"对比，光有打分不构成观察\n- 「能不能直接告诉我会不会爆」 → 拒绝。给具体 composite + bucket 的判定要求走 predict 流程；score 只输出当前 rubric 下的机械计算\n- 「跳过 blind sub-agent 让主 Claude 直接打」 → cheat-score **不接受**这种 escape hatch（与 cheat-predict 不同；cheat-predict 有 `--skip-blind`）。score 是轻量探索，没理由放弃隔离。如真的 Task tool 不可用 → 提示用户配置后再试\n\n## Integration\n\n- 是 `cheat-predict` 的前置探索：用户可以反复 score 不同稿子版本，确定一份再 predict\n- score 不更新 `.cheat-state.json`——这是无副作用操作\n- 如果用户连续 score 同一稿子 ≥3 次 → 控制台温和提示\"反复打分会引入决策疲劳，差不多可以决定了\"\n","skills/cheat-seed/SKILL.md":"---\nname: cheat-seed\ndescription: 跟用户对话讨论选题——**默认一次一个**，用户主动给主题或经历，AI 围绕用户的输入深挖、提炼角度、写一份 draft。不是 AI 拿三个开放问题追用户，也不是一次给 5 个候选。触发词：\"找选题\"/\"我想做一条 X\"/\"最近有个想法\"/\"seed\"/\"启动种子\"。可选 batch 模式：`/cheat-seed --batch 5` 走旧的 brainstorm 5 候选 + 写 5 draft 流程。\nargument-hint: \"[— batch: N] [— sources: <comma-separated>]\"\nallowed-tools: Bash(*), Read, Write, Edit, Glob, WebFetch, Skill\n---\n\n# /cheat-seed — 选题对话（默认）/ 批量 brainstorm（可选）\n\ncheat-seed 的核心是**跟用户讨论选题**，不是机械地 brainstorm。好内容来自用户的真实经历 + 观察 + 情绪——这些是 AI 不可能凭空 brainstorm 出来的。AI 的角色是**听用户讲 → 帮提炼角度 → 写一份 draft**，不是 dump 15 候选让用户挑。\n\n**默认模式**：对话式一次一个。\n**Batch 模式**（`--batch N`）：保留旧的 brainstorm N 候选 + 写 N 份 draft 流程，给\"完全没想法 + 想批量初始化\"的用户。\n\n## 三种 Mode（自动识别）\n\n```\nMode A — 用户主动给主题（**最常见**）：\n  用户：\"/cheat-seed\" + 直接说\"我想做一条关于 X 的\"\n       或：\"/cheat-seed 我最近开会被领导...\"\n  ↓\n  AI 围绕 X / 这件事**深挖**——什么瞬间触发？最让你 [情绪 / 不爽 / 觉得有意思] 的是哪点？\n  ↓\n  收敛到一个具体角度 → 提议 → 用户认可 → 写 1 份 draft → 完成\n  ↓\n  问\"下一篇？\" 或用户说\"今天就这样\"\n\nMode B — 用户给方向但不具体：\n  用户：\"最近想做点关于 [职场 / 婚恋 / AI / ...] 的\"\n  ↓\n  AI：\"[范围] 太广。最近你接触到的具体哪件事让你想做这个方向？\"\n  ↓\n  收敛到 Mode A 的具体经历\n\nMode C — 用户完全没想法（少见）：\n  用户：\"我不知道做什么\" / \"帮我想个题\"\n  ↓\n  AI：\"好，进 brainstorm 模式——先抓热点 + 你之前的兴趣方向，给你 1 个建议\"\n  ↓\n  跑 trend-sources 抓热点 + 读 candidates.md / predictions/ 看用户历史\n  ↓\n  提议 1 个角度（不是 5 个） → 用户认可 → 写 draft\n\nBatch Mode — 用户显式要批量（`/cheat-seed --batch 5`）：\n  按旧版 brainstorm 流程：3 问题 → 15 候选 → 用户挑 → 写 5 draft。\n  给\"今天想一次性把未来 2 周的选题搞定\"的用户。\n```\n\n**关键纠正**（与旧版的区别）：\n- AI **不主动开放问**——等用户给输入再深挖\n- 一次一个选题，不是 5 个\n- 默认对话式 + 一次一个，batch 是 escape hatch\n\n## Constants\n\n- **DEFAULT_TREND_SOURCES = [\"manual-paste\"]** — 仅 Mode C / Mode A 灰色场景 / Batch 用到。用户可在 state 里加 aihot / trendradar-mcp\n- **TREND_TOOL_ROUTING** — 按 `content_form` 路由数据源，详见 [shared-references/data-source-routing.md](../../shared-references/data-source-routing.md)\n- **MODE_B_MAX_REPROBE_TURNS = 2** — Mode B \"为什么\" 反问最多 2 轮；超过则转 Mode C\n- **MAX_DEEP_DIVE_TURNS = 4** — Mode A 收敛阶段最多 4 轮反问，避免 AI 过度盘问\n- **WITH_DRAFT = yes** — 默认确认角度后立刻写 draft；用户可说 \"等下，我自己写\" 跳过\n- **DRAFT_LENGTH** — 派生自 `state.typical_duration_seconds`：30s→100-200字 / 90s→250-500字 / 240s→600-1000字 / 450s→1100-2000字 / 900s→2200+字\n- **HUMANIZE_DRAFT = on**（默认）/ off —— 写完 draft 后用 `humanizer` skill 过一遍，去掉 AI 写作 tells（em-dash 滥用 / rule of three / inflated 词汇 / 空泛归因等）。off 时直接出原始 AI draft。**只 humanize 正文，不动 header 的\"必须改写\"警告**\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `.cheat-state.json` | 读 calibration_samples / typical_duration / cadence |\n| `rubric_notes.md` | 读当前 rubric（粗打分用） |\n| `script_patterns.md` | 读已有 pattern（写 draft 时按 cheat sheet 选结构） |\n| `predictions/*.md`（如有） | 已发历史，brainstorm 时作为 context |\n| `audience.md`（如有） | 受众画像——选题 / 写稿时的\"谁在看\"镜子（由 `/cheat-persona` 派生） |\n\n## Workflow\n\n### Phase 0: 前置检查 + 加载所有 context（**核心：3 个 context 来源**）\n\n1. 读 `.cheat-state.json` → 不存在则提示先跑 `/cheat-init`\n2. 读 `rubric_notes.md` 拿当前公式（粗打分用）\n3. 读 `script_patterns.md`——写 draft 时按 cheat sheet 选结构\n4. **读已有 prediction 文件**（含 init 时 import 的 reconstructed）作为 **context 来源 A**（用户自己历史）：\n   - 0 个 → A 为空\n   - ≥1 个 → A 有内容，提取 (title / 7 维 / 实绩)\n5. **读 `benchmark.md`**（如存在）作为 **context 来源 B**（对标账号）：\n   - `state.benchmark_status = imported` → B 有内容，提取对标账号的样本主题分布、调性、Pattern\n   - `state.benchmark_status = none / pending` → B 为空\n6. **读 `audience.md`**（如存在且非空骨架）作为 **受众镜子**：\n   - Confidence 🟡 以上（有真实复盘数据派生）→ 选题 / 写 draft 时作为\"这个 persona 会在乎吗\"的检验视角\n   - Confidence 🔴/🟠（空 / 仅 benchmark seed）→ 当弱参考，不当硬约束\n   - **不进粗打分**——audience.md 是 creative lens，粗打分仍只用 rubric。persona 影响\"写什么 / 哪个角度\"，不影响分数\n7. 检查用户的入参——是否含具体 topic / 经历，决定走 Mode A/B/C/Batch\n\n**brainstorm 时的 context 优先级**（**Claude 判断**——下面是参考默认）：\n\n- **A 主导**（用户自己数据）：当 Claude 判断用户数据已能驱动方向时（参考默认：`calibration_samples ≥ 10`，但 Claude 可以更早——如 N=5 但出现 ≥3 个与 benchmark 明显不一致的强样本）\n- **B 主导**（benchmark）：用户数据少 + benchmark 有内容时\n- **B 缺席**（benchmark 为空）+ 用户数据少：纯靠用户 input + 抓热点；明确告诉用户\"没 benchmark 也没足够自己数据，建议跑 /cheat-learn-from 后再回来 brainstorm\"\n\n判断依据**不是死磕样本数**，而是看：\n- 用户最近 N 个样本的实绩**是否与 benchmark 的高表现样本类型一致**——一致说明 benchmark 仍有借鉴价值；不一致说明用户已经走自己的路\n- 用户的样本**多样性**——3 篇都是同类内容不算成熟；3 篇覆盖不同类目反而比 10 篇同类更可信\n\n### Phase 1: Mode 分流\n\n读用户输入，识别：\n\n**含具体名词 + 情绪 / 经历词**（\"我昨天开会...\" / \"我看到 X 让我...\" / \"我对 Y 觉得...\"）→ **Mode A**（深挖；如话题是时事，Phase 2A.5 询问要不要拉外部数据）\n\n**含方向词但无具体内容**（\"想做职场\" / \"AI 方向\" / \"婚恋\"）→ **Mode B**（单问\"为什么想做这个\"——用户内省窗口，**不调任何热点工具**）\n\n**显式说没想法**（\"不知道做什么\" / \"帮我想\" / \"随便给个\"）→ **Mode C**（按 [data-source-routing.md](../../shared-references/data-source-routing.md) 调热点工具 + 用户挑 + 回到内省；都不行则走\"聊经历\"三选项兜底）\n\n**显式 `--batch N`**（用户主动批量）→ **Batch Mode**\n\n**纯 `/cheat-seed` 无附加内容** → **询问入口问题**：\n\n```\n你今天想干嘛？\n\n- 有想做的主题 / 经历 → 直接告诉我（\"我想做一条 X\" / \"我最近 X...\"）\n- 知道大致方向 → 告诉我（\"想做职场\" / \"AI 方向\"）→ 我会单问你为啥想做\n- 完全没想法 → 说\"帮我想\"→ 我用 [aihot / trendradar] 拉今天的热点给你看\n- 批量搞定 → 说 \"batch <N>\"\n\n(我不会拿一堆开放问题追你——你给一句话我就开始)\n```\n\n注意这是**唯一的开放式问题**——只在用户**纯触发** `/cheat-seed` 时问。如果用户已经在触发词里给了内容（\"/cheat-seed 我想做...\" 或 \"找选题 我最近开会...\"），直接进 Mode A/B/C 不再问这一句。\n\n### Phase 2A: Mode A 深挖（用户给了具体 topic / 经历）\n\n**核心原则**：围绕用户给的内容**深挖**，**不要切到别的话题**。\n\n**反问类型（按场景挑）**：\n\n- 触发瞬间：\"你说 X 这件事，最初是哪个具体瞬间触发你想做的？\" / \"是什么让你觉得这值得讲一条视频？\"\n- 情绪锚点：\"这里面最让你 [生气 / 觉得荒唐 / 觉得有意思] 的是哪个细节？\"\n- 角度选择：\"你想说的是 [角度 a：现象批判] 还是 [角度 b：自我反思] 还是 [角度 c：泛化到普遍]？\"\n- 受众想象：\"你心里想着是说给哪种人听？她/他听完会怎么想 / 怎么转发？\"\n- 反对意见探测：\"如果有人反驳说 [反方观点 X]，你会怎么回？\"——逼用户先想清楚立场\n\n**反问纪律**：\n- 一次只问 **1 个**问题（不要塞 3 个连珠炮）\n- 最多 `MAX_DEEP_DIVE_TURNS` 轮（默认 4）——超过就主动收敛：\"OK 我感觉够了，帮你提议一个角度试试\"\n- 用户的回答如果含 emoji / 简短 / 不耐烦 → 立刻收敛，不要逼\n\n**收敛输出**：\n\n```\n我感觉这个角度能做：\n\n[一句话立意：50 字以内]\n\n走法：\n- 用 [Pattern X 结构]（来自 script_patterns.md）\n- 钩子：[具体场景 / 句子]\n- 主体：[3 个观察是什么]\n- 收尾：[MVP 句方向]\n\n粗打分（v0 等权 7 维）：ER=X HP=X QL=X NA=X AB=X SR=X SAT=X → composite ≈ X.X\nConfidence: 🔴 极低 (你才校准 0/N 篇)\n\n要不要让我先写一份 draft？(yes / 换角度 / 我自己写)\n```\n\n用户回 yes → Phase 4 写 draft。\n用户说\"换角度\" → 回 Phase 2A 深挖更多。\n用户说\"我自己写\" → 把 candidate 加进 candidates.md 标 tier1，结束。\n\n### Phase 2B: Mode B — 单问\"为什么\"，触发用户内省\n\n用户给了方向但不具体（\"想做职场\" / \"AI 方向\" / \"婚恋\"）。**这阶段不调任何热点工具**——是用户内省的窗口，外部信息会污染。\n\n只问一个问题，**单刀直入**：\n\n```\n为什么想做这个主题？\n```\n\n不要问\"a/b/c 三种例子\"——那是 dump 选项让用户挑，破坏内省。让用户自己组织语言。\n\n**根据用户回答分流**：\n\n| 用户答 | 分类 | 行为 |\n|---|---|---|\n| 含具体经历 / 个人卡点（\"我自己经常加班\" / \"我看到 X 让我...\"） | **真动机** | 转 Mode A 深挖（Phase 2A） |\n| 抽象热度归因（\"这话题最近热\" / \"别人都在做\" / \"听说能涨粉\"） | **空动机** | 反问\"那你自己对这个话题最有感觉的角度是啥？\"——逼出个人 stake；继续空 → 转 Mode C |\n| \"我也不知道\" / \"朋友说赚钱\" / 模糊推搡 | **真没想法** | 直接转 Mode C |\n\n**反问纪律**：最多 2 轮。第 2 轮还问不出真动机 → 直接转 Mode C，**不要无限挖**。\n\n> 设计意图：Mode B 是\"过滤器\"，不是\"工厂\"。用户来这里要么暴露真实动机（→ 进 Mode A）要么暴露自己其实没想法（→ 进 Mode C）。两条都比硬要在 Mode B 里产出选题更好。\n\n### Phase 2C: Mode C — 外部素材 + 用户挑 + 回到内省\n\n用户完全没想法（直接显式说 / 从 Mode B 转过来）。**这是唯一默认调热点工具的入口**。\n\n按 [shared-references/data-source-routing.md](../../shared-references/data-source-routing.md) 的路由规则：\n\n1. **拉外部素材**（按 `content_form` 选 trend source）：\n   - `tutorial-builder` / AI 类 → 调 aihot skill\n   - `opinion-video` / `long-essay` / `podcast` / `other` → 调 trendradar-mcp（如启用）\n   - `mixed` → 两个都调\n   - 都不可用 → 走 manual-paste（询问用户：\"今天看到啥可以拍的？粘几条 URL/标题\"）\n\n2. **聊经历兜底**（用户拒绝看外部素材 / 外部素材都不感兴趣）：\n\n   ```\n   外部素材你都没感觉，那回到你自己。三个开口，挑一个开始讲：\n\n   a) 你最近真实接触到的某件具体事？（\"上周我看到我同事 X...\"）\n   b) 你最近读到 / 看到的某条让你想吐槽的内容？（\"知乎上有个回答...\"）\n   c) 你长期琢磨的某个 unsolved 困惑？（\"我一直没想明白为啥 X...\"）\n\n   随便挑一个开始讲。\n   ```\n\n3. **拉到外部素材后**，用 rubric 粗筛 + 按 content_form 过滤，留 5 条最契合的：\n\n   ```\n   今天这 5 个跟你形态契合：\n   1. [标题 A]（来源: trendradar / 微博热搜 / hot_score: 8.5）\n   2. [标题 B]（来源: aihot / 模型类 / 精选）\n   3. ...\n\n   你哪个最有感觉？没感觉就回 '都没'，我换方向问。\n   ```\n\n4. **用户挑了一条 → 回到内省**：\n\n   ```\n   OK [标题 X]。你为啥对这条最有感觉？\n   是 [angle1] 还是 [angle2] 还是别的？\n   ```\n\n   → 用户答 → 转 Mode A 深挖。\n\n5. **用户回 \"都没\"** → 转回 Mode C 第 2 步的\"聊经历兜底\"。\n\n**关键**：热点不是\"塞 5 候选让用户挑\"，是\"给材料 + 强制问用户的个人 stake\"——AI 不替用户决定哪条最值得做。\n\n### Phase 2A.5: Mode A 灰色场景 — 用户讲了时事话题\n\nMode A 默认深挖用户经历。但如果**用户讲的本身是时事话题**（产品名 + 时间词 / 人名 + 事件词），按 [data-source-routing.md](../../shared-references/data-source-routing.md) 的\"时事判定\"规则，**询问**用户要不要拉外部数据作参考：\n\n```\n💡 [话题] 是时事——我可以拉一下今天的舆论风向（各平台情绪 + 主要 angles）作参考。\n\n要看吗？回 '看' 我调；回 '不用' 我直接跟你深挖你的角度。\n```\n\n用户回 \"看\" → 调对应 trend source → 把数据 inline 到深挖 context；\n用户回 \"不用\" → 标准 Mode A 深挖，不动外部数据。\n\n**永远不主动调**——用户的 angle 优先于外部数据，避免外部信息**带偏**用户视角。\n\n### Phase 2D: Batch Mode（用户显式 `--batch N`）\n\n**保留旧 brainstorm 流程**：\n\n1. 问 3 个清单问题（兴趣 / 调性 / 红线）—— Batch 模式才问这些\n2. 抓热点 + Claude brainstorm 15 候选\n3. 用户挑 N\n4. 写 N 份 draft 到 scripts/——**每份都走 Phase 4 的段落版格式 + Phase 4.5 自检**（line-format + humanizer），不因为批量就跳过\n\n详见 commit history（旧 cheat-seed 的 Phase 1-3）。这是 escape hatch，不是默认。\n\n### Phase 3: 计算 candidate ID + 落候选池\n\n不管 Mode A/B/C 哪条路径，确认角度后：\n\n1. 算 candidate id：`sha256(\"seed-\" + 立意 + 触发时间)[:12]`\n2. 写一行 entry 到 `candidates.md`（按 [candidate-schema.md](../../shared-references/candidate-schema.md) 格式）\n3. 标 `tier=tier1` + `read_status=deep_read`（已经讨论过，不是 skim）\n\n### Phase 4: 写 draft\n\n`WITH_DRAFT=yes` → 顺次写到 `scripts/<YYYY-MM-DD>_<id>_<short-title>.md`：\n\n**写 draft 前必读** `script_patterns.md` —— 按\"结构选型 cheat sheet\"对应用户的 topic 选合适结构。如果文件还在抽象骨架阶段（用户没填几个 pattern），就用 starter rubric 对应的通用框架。\n\n**字数**：按 `DRAFT_LENGTH` 派生（基于 `typical_duration_seconds`）。\n\n#### ⚠️ 正文必须是段落版，不是字幕格式（**最常见的生成跑偏**）\n\n模型的训练先验会把\"视频脚本\"默认写成提词器/字幕的短行格式。**这是错的**——cheat-seed 的 draft 是给用户**改写**的散文稿，不是拍摄字幕。字幕是剪映拍后自动断的，不是写作时的形态。\n\n生成正文时，眼睛盯住下面这个对照：\n\n```\n❌ 字幕格式（不要这样写）：\n你有没有发现\n所有审稿人都在说一样的话\n你的研究太老套了\n但你仔细看\n他们引用的全是 5 年前的反应\n\n✅ 段落版（必须这样写）：\n你有没有发现，所有审稿人都在说一样的话——你的研究太老套了。但你仔细看，他们引用的全是 5 年前的反应。AI 不是新东西，新的是这次大家集体觉醒了。\n```\n\n规则：\n- **每段 100-300 字**，逗号 / 句号 / 破折号自然连，**不在句子边界硬断行**\n- 段与段之间空一行（自然的主题切换才换段）\n- 一份 draft 正文一般 3-6 段，**不该有几十个单句行**\n\n#### 格式：\n\n```markdown\n# [立意标题]\n\n> ⚠️ **Draft by Claude — 你必须改写后再拍**\n>\n> 这是脚手架，不是成品。你的语气 / 节奏 / 个人经历无法 AI 生成。\n> 改写流程：\n> 1. **直接在本文件改写**（同 path：scripts/<...>.md）\n>    - 加你的语气、个人经历、真实金句\n>    - 砍铺垫、砍模型缩写、砍学术包装\n> 2. 改完后跑 `/cheat-predict scripts/<本文件>.md`\n> 3. 拍完跑 `/cheat-shoot scripts/<本文件>.md`\n\n**Article ID**: <12 位 hash>\n**调性**: [基于讨论得出的，不是清单 Q]\n**目标时长**: <state.typical_duration_seconds 转换> 分钟\n**目标字数**: <按时长派生>\n**结构选型**: [按 script_patterns.md 的 cheat sheet 显式标，如 \"metaphor 优先\" / \"数据反转开场\"]\n**用到的 patterns**: [编号 + 简短说明]\n**讨论种子**: [一句话回顾 deep dive 出来的核心]\n\n---\n\n[draft 正文 —— **段落版**，3-6 段，每段 100-300 字，不是单句碎行]\n```\n\n`WITH_DRAFT=no`（用户说\"我自己写\"）→ 跳过 Phase 4 + Phase 4.5。\n\n### Phase 4.5: draft 自检 pass（版式 + 去 AI 味）\n\ndraft 写完落盘后、**在展示给用户前**跑两步自检。**顺序固定：先 4.5a 修版式，再 4.5b 去 AI 味**——humanizer 处理散文，喂它字幕格式的碎行会乱。\n\n**为什么安全**（不污染校准）：cheat-seed 的 draft 不是被预测/发布的东西——用户改写后、cheat-predict 打分的是**用户最终稿**。这两步只是给用户更干净的起点。\n\n#### Phase 4.5a: line-format 自检（字幕格式 → 段落版）\n\nPhase 4 的散文指令 + ❌/✅ 对照已经在生成时压先验，但生成仍可能跑偏。这一步是**确定性兜底**：\n\n```bash\n# 只看正文段（--- 分隔线之后）\nbody=$(awk '/^---$/{f=1;next} f' scripts/<id>.md)\nline_count=$(printf '%s\\n' \"$body\" | grep -c .)        # 非空行数\nchar_count=$(printf '%s' \"$body\" | wc -m | tr -d ' ')\navg_chars_per_line=$(( char_count / (line_count > 0 ? line_count : 1) ))\n```\n\n判定：**`avg_chars_per_line < 15` 且 `line_count >= 8`** → 判定为字幕格式 → **自动重排**：\n- 把句子边界的硬断行合并回自然段落\n- 按主题切换分 3-6 段，每段 100-300 字\n- 用 Edit 替换正文段（header 不动）\n- 在 Phase 5 输出里标一行：\"📐 检测到字幕格式，已重排为段落版\"\n\n不命中 → 跳过，正文已经是段落版。\n\n#### Phase 4.5b: humanizer 去 AI 味\n\n`HUMANIZE_DRAFT=on`（默认）—— 用 `humanizer` skill 过一遍。Claude 自己写的初稿天然带 AI tells（em-dash 滥用 / rule of three / \"inflated\" 词汇 / 空泛归因 / -ing 浅层分析），这一步把它们清掉。\n\n步骤：\n\n1. 检查 `humanizer` skill 是否可用（`~/.claude/skills/humanizer/` 存在）：\n   - 不可用 → 跳过 4.5b，在 Phase 5 输出里加一行\"（humanizer 未装，draft 是原始 AI 版——`git clone https://github.com/blader/humanizer` 到 ~/.claude/skills/ 可启用自动去 AI 味）\"\n2. 可用 → 通过 Skill tool 调 `humanizer`，**只传 draft 正文**（`---` 分隔线之后、4.5a 已重排好的段落版），**绝不传 header**：\n   - header 的 `⚠️ Draft by Claude — 你必须改写后再拍` 警告是**有意的脚手架标记**，不是要 humanize 的散文\n   - **voice calibration**：如果用户有历史脚本（`videos/*/script.md`）或填过 `script_patterns.md`，把最近 1-2 份作为 humanizer 的 voice 参考样本一起传——让它往\"**这个用户的声音**\"靠，而不是\"通用人声\"\n3. humanizer 返回去 AI 味的正文 → 用 Edit 替换 draft 文件的正文段（header 不动）\n4. 记录 humanizer 报告的\"修了哪些 tell\"（如 `em-dash 滥用 ×3 / rule of three ×2 / inflated 词汇: \"深刻\" \"本质上\"`），Phase 5 输出里展示\n\n**纪律**：\n- humanizer 是**去 AI 味**，不是**替用户改写**。它让 draft 不那么像机器写的，但**仍不是用户的声音**——header 的\"必须改写\"警告依然成立，Phase 5 输出要重申\n- 如果 humanizer 把某句改得偏离了 `结构选型` / 用到的 pattern → 以 pattern 为准，那句回滚（pattern 是和用户讨论定的，humanizer 不该推翻）\n- humanizer **不负责版式**——断行问题在 4.5a 已经修完，humanizer 拿到的已是段落版\n\n### Phase 5: 输出\"下一步\" + 询问继续\n\n```\n✅ Draft 写完：scripts/2026-05-04_<id>_<short>.md\n📐 版式自检：通过（段落版）  ← 或\"检测到字幕格式，已重排为段落版\"\n🧹 humanizer 过了一遍：修了 em-dash 滥用 ×3 / rule of three ×2 / inflated 词汇 2 处\n   （draft 现在不那么\"机器味\"了——但这仍是脚手架，不是你的声音）\n\n接下来你可以：\n- 改写这份 draft（直接在原文件改）—— 加你的语气、经历、真实金句\n- 改完跑 \"打分这篇 scripts/<...>.md\" 看 7 维评分\n- 决定要拍 → \"启动预测 scripts/<...>.md\"\n\n下一篇你想做什么？\n（直接告诉我具体经历 / topic，或者说\"今天就这样\"结束）\n```\n\n> humanizer 那行只在 `HUMANIZE_DRAFT=on` 且 skill 可用时出现。未装时换成一行提示如何启用。\n\n用户说\"今天就这样\" → 结束 cheat-seed。\n用户给新 topic → 回 Phase 1 重新分流。\n\n## Key Rules\n\n1. **AI 不主动开放问**——只在用户纯触发 `/cheat-seed` 时问一次入口问题，其他时候**等用户给输入再深挖**\n2. **一次一个选题**——默认 Mode A/B/C 都给 1 个建议；用户主动要批量才走 Batch\n3. **反问纪律**：一次问 1 个，最多 4 轮，用户不耐烦立刻收敛\n4. **深挖围绕用户给的话题**，不要切到别的——你说\"开会被领导骂\"，AI 不该问\"那你最近有没有觉得 AI 让大家...\"这种平行话题\n5. **写 draft 必须读 script_patterns.md**——按用户已有 pattern 选结构\n6. **draft 是脚手架**——header 加醒目警告\"必须改写\"\n7. **humanizer 只去 AI 味，不替用户改写**——Phase 4.5b 让 draft 不那么机器味，但它仍不是用户的声音；\"必须改写\"的警告不因为过了 humanizer 就失效\n8. **正文是段落版不是字幕格式**——生成时盯 Phase 4 的 ❌/✅ 对照；Phase 4.5a 用 `avg_chars_per_line < 15 且行数 ≥ 8` 做确定性兜底，命中就重排。字幕是剪映拍后自动断的，不是写作时的形态\n\n## Refusals\n\n- 「跳过深挖，直接写 draft」 → 询问\"你想直接给主题让我写吗？OK 但 draft 质量可能差——我不知道你的角度。给我一句话立意我就写\"\n- 「AI 替我决定 topic」 → 拒绝。Mode A/B/C 路径里 AI 永远只**呈现外部素材** + **问用户角度**，不替用户拍板\"做哪条\"\n- 「Mode B 我懒得回答为什么，直接给我 5 个候选吧」 → 拒绝。Mode B 的\"为什么\"是过滤器——你答不出来就不该用 Mode B 的方向。要么进 Mode A 给具体经历，要么进 Mode C 我帮你找素材\n- 「Mode A 时直接帮我拉热点，不问我同意」 → 拒绝。Mode A 用户已经有 angle，未经允许拉外部数据会污染他的视角。详见 [data-source-routing.md](../../shared-references/data-source-routing.md)\n- 「一次写 5 个 draft」 → 不在默认流程。用户必须显式 `--batch 5`\n- 「我懒得改写，直接拍 AI draft」 → 警告\"AI 直接生成的稿子拍出来 ER 偏低，会污染你的校准数据\"，但用户坚持也允许（标 `unmodified_ai_draft: true`）\n\n## Integration\n\n- 上游：`/cheat-init` Phase 5 末尾在 `pool_status=none + calibration_samples=0` 时主动询问\"现在跑 /cheat-seed？\"\n- 上游：`/cheat-recommend` 在 candidates 空时引导文案中提及 `/cheat-seed`\n- 上游：`/cheat-status` 在 `pool_status=none + 距 init >24h` 时提示\"还没拍——跑 /cheat-seed？\"\n- 下游：用户的 candidate → candidates.md（tier1，已 deep_read）\n- 下游：（默认）draft → Phase 4.5 humanizer 去 AI 味 → scripts/<id>.md → 用户改写 → /cheat-predict\n- 可选依赖：[`humanizer`](https://github.com/blader/humanizer) skill（MIT，外部项目）。装在 `~/.claude/skills/humanizer/` 时 Phase 4.5 自动启用；未装则优雅跳过。**不打包进 cheat-on-content**——用户自己 clone\n- 与 `/cheat-trends` 区别：cheat-seed 是**讨论 + 写 draft**（重 conversation）；cheat-trends 是**多 adapter 抓 + 粗打分**（重 fetch）。两者目的不同，不互相替代。\n","skills/cheat-shoot/SKILL.md":"---\nname: cheat-shoot\ndescription: 登记一条视频已拍摄。**建 video folder + 询问实际拍摄稿是否与 scripts/<id>.md 一致 + buffer +1**。与 cheat-publish 配对：拍了进队列，发了出队列。触发词：\"拍了\"/\"拍了 X\"/\"shot\"/\"shot it\"/\"已拍 X\"/\"录完了\"。\nargument-hint: <scripts-path-or-id>\nallowed-tools: Bash(*), Read, Write, Edit, Glob\n---\n\n# /cheat-shoot — 登记拍摄完成 + 建 video folder + (改稿则) 触发 v2 预测\n\n把视频从\"已写预测、未拍摄\"状态推进到\"已拍摄、未发布\"状态。这一步：\n1. **建 `videos/<同 id>/`** 目录（之前没有的话）\n2. **询问用户**：\"实际拍摄时用的稿子和 `scripts/<id>.md` 一致吗？\"\n3. 算 diff——超过 V2_TRIGGER_THRESHOLD (默认 30%) → **delegate 到 `/cheat-predict — mode: v2`** 在原 prediction 文件 append `## 预测 v2` 段\n4. 把 video folder 加进 state.shoots 队列，buffer +1\n\ncheat-shoot 自己**不**写预测内容——所有预测落盘逻辑在 cheat-predict。cheat-shoot 只负责检测改稿 + 派发。\n\n为什么单独一个 skill：\n- buffer 警戒系统需要明确区分\"拍了\" vs \"发了\"。视频可以批量拍（一天拍 5 条），分散发（每天发 1 条）\n- \"实际拍摄稿\" ≠ \"pre-shoot 草稿\"是常态。这一步是把 diff 显式化、触发 v2 重判、采集\"用户改稿 pattern\"信号的入口\n- v2 预测 vs v1 预测的差异本身就是 rubric 升级证据——比如 v1 给 ER=4，v2 给 ER=5（用户改稿改高了 hook 强度），就告诉 rubric \"这个用户的 ER 阈值跟我现在公式不一致\"\n\n## Overview\n\n```\n[用户：拍了 scripts/2026-05-04_abc123_停止期待.md]\n  ↓\n[Phase 0: 解析路径 + 验证 prediction 已存在]\n  ↓\n[Phase 1: 检查是否已登记（避免重复）]\n  ↓\n[Phase 2: 建 videos/<id>/ + 询问\"实际拍摄稿一致吗？\"]\n  ↓\n[Phase 3: 写 videos/<id>/script.md]\n  ↓\n[Phase 4: append state.shoots]\n  ↓\n[Phase 5: 输出 buffer 状态]\n```\n\n## Constants\n\n- **REQUIRE_PREDICTION = true** — 拍前必须先有 v1 prediction 文件\n- **V2_TRIGGER_THRESHOLD = 0.30** — normalize 后 char-level diff 超过 30% → 默认建议 v2 重判；低于 30% 询问用户是否仍要 v2\n- **DIFF_METRIC = char_levenshtein_normalized**（**默认**）—— 通过 [`tools/diff_pct.py`](../../tools/diff_pct.py) 调用：先 normalize（去 markdown header / 分隔线 / 列表标记 / 装饰标点 / 折叠所有空白），再算 char-level Levenshtein / max(len_a, len_b)。preferred backend `rapidfuzz`，fallback `difflib.SequenceMatcher`（stdlib，永远可用）。**旧版 line-level 在口语化转录场景误报严重**（draft 长 markdown 句 vs whisper 转录的短断句，内容几乎不变但 line-level 算出 ~200% diff）—— PR #14 修复\n- **DIFF_METRIC=lines** —— legacy fallback：当 python3 完全不可用或 tools/diff_pct.py 找不到时降级到 `diff -u | grep '^[+-]' | wc -l` 算法\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<scripts-path-or-id>` | 用户参数；缺失则询问 |\n| `.cheat-state.json` | 状态文件 |\n| `scripts/*.md` | pre-shoot 草稿 |\n| `predictions/*.md` | 验证对应预测存在 |\n\n## Workflow\n\n### Phase 0：解析 + 验证\n\n1. 解析用户给的路径——支持几种形态：\n   - 完整路径 `scripts/2026-05-04_abc123_停止期待.md`\n   - 简写 `2026-05-04_abc123_停止期待`\n   - id 简写 `abc123` → glob `scripts/*_abc123_*.md` 找匹配\n2. 验证 `scripts/<id>.md` 存在：不存在 → 报错\"找不到 pre-shoot 草稿\"\n3. 验证有对应 prediction `predictions/<同名>.md`：\n   - 不存在 → **拒绝登记**，提示\"先跑 /cheat-predict 写预测，否则违反盲预测原则——你不能拍完才写预测，那等于事后看了画面写\"\n   - 存在 → 通过\n\n### Phase 1：检查重复\n\n读 `.cheat-state.json`，检查 `shoots[]` 是否已含此 id：\n- 已存在 → 警告\"已登记过（X 天前）。是要重新登记，还是要用 /cheat-publish 发布？\"\n- 不存在 → 进入 Phase 2\n\n### Phase 2：建 video folder + 询问稿子一致性\n\n1. 建目录 `videos/<id>_<short>/`（同 scripts/ + predictions/ 的命名）\n2. **询问用户**：\n\n```\n拍 「<title>」 的时候，你实际用的稿子和 scripts/<id>.md 一致吗？\n\na) 一致——按草稿拍的\nb) 改了一些——你能给我看下实际拍摄稿吗？我重新打分一次（v2 预测）\nc) 大改了，基本是另一条 → 走 _redo 流程：\n   scripts/<id>_redo.md → 重新 cheat-predict → 再 cheat-shoot（原 prediction 留档脱钩）\n```\n\n### Phase 3：写 videos/<id>/script.md + (b 路径) 触发 v2 预测\n\n**a 路径（一致）**：\n- `cp scripts/<id>.md → videos/<id>/script.md`\n- `script_consistency = consistent`\n- 不重判，进 Phase 4\n\n**b 路径（改了）**：\n1. 询问用户实际拍摄稿——粘贴文本 / 文件路径 / 转录文件\n2. 若用户提供 → 写入 `videos/<id>/script.md`\n3. 若用户没保留（即兴）→ 标 `script_lost`，写占位文件 + 警告\"v2 重判跳过——下次建议留稿（哪怕 voice memo 转录）\"，进 Phase 4\n4. 提供了的话：算 diff\n   ```bash\n   # 解析 cheat-on-content 源码根（cheat-shoot 是 symlink 装的）\n   SKILL_REAL=\"$(readlink -f ~/.claude/skills/cheat-shoot 2>/dev/null || readlink ~/.claude/skills/cheat-shoot 2>/dev/null)\"\n   if [[ -n \"$SKILL_REAL\" ]]; then\n     REPO_ROOT=\"$(cd \"$SKILL_REAL/../..\" && pwd)\"\n     DIFF_TOOL=\"$REPO_ROOT/tools/diff_pct.py\"\n   fi\n\n   if [[ -n \"${DIFF_TOOL:-}\" && -f \"$DIFF_TOOL\" ]] && command -v python3 >/dev/null 2>&1; then\n     # 默认 char-level Levenshtein on normalized text（rapidfuzz preferred, difflib fallback）\n     diff_pct=$(python3 \"$DIFF_TOOL\" \"scripts/<id>.md\" \"videos/<id>/script.md\")\n   else\n     # legacy line-level fallback——只在 python3 或 diff_pct.py 都不可用时用\n     added=$(diff -u scripts/<id>.md videos/<id>/script.md | grep -c '^+')\n     removed=$(diff -u scripts/<id>.md videos/<id>/script.md | grep -c '^-')\n     total_orig=$(wc -l < scripts/<id>.md)\n     diff_pct=$(( (added + removed) * 100 / total_orig ))\n     echo \"⚠️  fallback 到 line-level diff——口语化转录会 inflate diff_pct，可能误触发 v2\"\n   fi\n   ```\n\n   **为什么 normalize + char-level**：line-level diff 在创作者真实场景（draft 是 markdown 长句、拍摄稿是 whisper 转录的口语化短行）算出 ~200% 差异但内容几乎不变。char-level Levenshtein 在 normalize 后稳定反映**内容**差异，而非格式差异。详见 [`tools/diff_pct.py`](../../tools/diff_pct.py) + `tools/diff_pct_test.sh`（3 fixture 在两个 backend 上全过）。\n5. **判定 v2 触发**：\n   - `diff_pct >= 30` → 默认建议 v2 重判，**主动调用** `/cheat-predict — mode: v2 — prediction-file: predictions/<id>.md` 传 `videos/<id>/script.md` 作 input。cheat-predict 走 v2 模式 append `## 预测 v2`\n   - `diff_pct < 30` → 询问用户：\"只改了 N% 的内容，要重判吗？默认不（v1 预测仍有效）\"。用户说要 → 同上调用；用户说不 → 跳过 v2，继续 Phase 4\n6. cheat-predict 完成 v2 落盘后，控制权回到 cheat-shoot 进 Phase 4\n\n**c 路径（大改）**：\n- 不写 `videos/<id>/script.md`，提示走 `_redo` 流程\n- 退出 cheat-shoot（不进 Phase 4）\n\n### Phase 4：state 更新\n\n```json\n{\n  \"shoots\": [\n    ...,\n    {\n      \"video_folder\": \"videos/2026-05-04_abc123_停止期待/\",\n      \"prediction_file\": \"predictions/2026-05-04_abc123_停止期待.md\",\n      \"scripts_path\": \"scripts/2026-05-04_abc123_停止期待.md\",\n      \"shot_at\": \"<ISO timestamp>\",\n      \"script_consistency\": \"consistent\" | \"modified\" | \"lost\",\n      \"script_diff_pct\": <0-100 int 或 null>,\n      \"v2_prediction_written\": <true/false>,\n      \"script_hash_at_shoot\": \"<sha256:12 of videos/<id>/script.md>\"\n    }\n  ]\n}\n```\n\n`v2_prediction_written: true` 表示 prediction 文件里现在有 `## 预测 v2` 段，cheat-retro 应读 v2 算偏差；`false` 表示沿用 v1。\n\n### Phase 5：输出 buffer 状态\n\n读完 state 后立即算 buffer + 颜色（按 [cadence-protocol.md](../../shared-references/cadence-protocol.md) 的派生规则）：\n\n```\n✅ 已登记拍摄：videos/2026-05-04_abc123_停止期待/\n   预测文件：predictions/2026-05-04_abc123_停止期待.md\n\n📦 当前 buffer：3 篇（🟢 绿色，正常）\n   按你的 cadence（隔日更）= 6 天 buffer，节奏稳定。\n\n下一步：拍其他候选 / 等下个发布日 / 不动\n```\n\n如果 buffer 颜色变了（如从绿到蓝）→ 高亮提醒：\n```\n📦 当前 buffer：6 篇（🔵 蓝色，**积压**）\n⚠️  建议暂停拍摄，全力发布存货 + 复盘。\n   按你的 cadence（日更）= 6 天预备，已超过健康上限。\n```\n\n## Key Rules\n\n1. **不写 prediction**——拍了 ≠ 发了。预测在 /cheat-predict 锁，拍只是事件\n2. **不动 video folder 内容**——script.md / draft-v0.md 都不改\n3. **必须先有 prediction**——否则违反盲预测（拍完看了画面再写预测 = 数据泄漏到判断）\n4. **buffer 计算实时**——每次 shoot / publish 后立刻重算，state.shoots 是真值\n5. **支持批量**：用户可以一天连说 \"拍了 X / 拍了 Y / 拍了 Z\" 三次连续登记\n\n## Refusals\n\n- 「拍了 X，但我从来没跑过 cheat-predict」 → 拒绝。v1 预测**必须拍前写**——拍完才写预测会被画面诱导事后修改。请先 /cheat-predict 写 v1 再来 /cheat-shoot。（v2 重判是另一回事——v1 已存在 + 拍后改稿才允许）\n- 「我没有 video folder，我直接拍的」 → 询问用户 → 帮他建 video folder + 提示下次走完整流程；登记时标 `ad_hoc: true`\n- 「我改稿了但你直接覆盖 v1 吧，别留 v2 段」 → 拒绝。v1 是档案，v2 才是当前判断——append 不覆盖。两段一起留是 rubric 学习的关键证据\n\n## Integration\n\n- 上游：`/cheat-predict` 写完 prediction → 用户拍摄 → `/cheat-shoot` 登记\n- 下游：`/cheat-publish` 发布时把对应项从 state.shoots 移除\n- `/cheat-status` 看板的 buffer 数字直接来自 `state.shoots.length`\n- `/cheat-recommend` 看 buffer 颜色调推荐策略\n- SessionStart hook 看 buffer 颜色决定报告第一行\n\n## state.shoots 数据结构\n\n```json\n{\n  \"shoots\": [\n    {\n      \"video_folder\": \"videos/2026-05-04_abc123_停止期待/\",\n      \"prediction_file\": \"predictions/2026-05-04_abc123_停止期待.md\",\n      \"shot_at\": \"2026-05-04T18:30:00+08:00\",\n      \"ad_hoc\": false  // true if user shot without going through full flow\n    }\n  ]\n}\n```\n\n按 `shot_at` 升序——最早拍的在前面。`/cheat-status` 显示最早一项的 days-since-shoot 警告（避免有视频拍了 30 天没发）。\n","skills/cheat-status/SKILL.md":"---\nname: cheat-status\ndescription: cheat-on-content 的状态看板。显示当前模式 / rubric 版本 / 校准进度 / 待复盘 / pool 状态 / 是否该升级 SQLite / 是否该 bump rubric。**任何时候都可调，无副作用**。触发词：\"状态\"/\"看板\"/\"status\"/\"我现在该做什么\"/\"进度怎么样\"。\nallowed-tools: Bash(*), Read, Glob, Grep\n---\n\n# /cheat-status — 状态看板\n\n读 state file + 扫描用户项目 → 汇总当前进度 → 输出\"今天该做什么\"清单。\n\n## Overview\n\n```\n[用户：状态]\n  ↓\n[Phase 1: 读 .cheat-state.json + 扫文件系统]\n  ↓\n[Phase 2: 计算派生指标]\n  ↓\n[Phase 3: 检测建议触发器（升级 / bump / 清算）]\n  ↓\n[Phase 4: 输出看板]\n```\n\n## Constants\n\n- **SQLITE_UPGRADE_THRESHOLD = 30** — calibration_samples 达到 N 时建议升 SQLite\n- **CLEANUP_LINE_THRESHOLD = 600** — rubric_notes.md 行数超 N 时建议清算\n- **STALE_PREDICTION_DAYS = 30** — in_progress prediction 超 N 天未发布提示清理\n\n## Inputs\n\n| 来源 | 用途 |\n|---|---|\n| `.cheat-state.json` | 主要状态 |\n| `predictions/*.md` | 校准样本数 / pending retros |\n| `candidates.md` | 候选池规模 |\n| `rubric_notes.md` | 行数 / 当前版本 |\n| `.cheat-cache/usage.jsonl`（如有） | meta-logging 数据，用于\"距上次 bump 多少次预测\" |\n\n## Workflow\n\n### Phase 1: 读状态\n\n```python\nstate = read_json('.cheat-state.json')\nif not state:\n    return \"你还没初始化。请先跑 /cheat-init。\"\n\npredictions = glob('predictions/*.md')\ncandidates_count = parse_candidates_md_entries()\nrubric_lines = wc -l rubric_notes.md\n```\n\n### Phase 2: 派生指标\n\n| 指标 | 算法 |\n|---|---|\n| **Buffer 数** | `len(state.shoots)` |\n| **Buffer 颜色** | 按 [cadence-protocol.md](../../shared-references/cadence-protocol.md) 派生：`buffer_days = buffer_count × target_publish_cadence_days`，`<1 红 / 1-2 橙 / 3-5 绿 / >5 蓝`。如 `target_publish_cadence_days=null` → 颜色禁用 |\n| **Confidence 等级** | 按 [state-management.md confidence 表](../../shared-references/state-management.md) 派生：从 `calibration_samples` 整数派生 emoji + 标签 |\n| **最早一拍至今天数** | `now - state.shoots[0].shot_at`，用于警告\"拍了 N 天没发\" |\n| 校准样本数 | predictions 中含完整复盘段（实绩数据非空）的文件数 |\n| 待复盘 | state.pending_retros 中已过 RETRO_WINDOW_DAYS 的 |\n| 池大小 | candidates.md 中 tier!=skip 的 entry 数 |\n| 上次 bump 至今几次预测 | predictions 中 published_at > state.last_bump_at 的数量 |\n| 同向偏差队列 | state.consecutive_directional_errors |\n| in_progress 陈旧度 | now - state.in_progress_session.started_at（如有） |\n\n### Phase 3: 检测建议触发器\n\n按优先级（高→低）逐项检查：\n\n1. **Buffer 颜色 = 🔴 红** → 第一行高优先级警戒：\"buffer 已 0/1 篇，下个发布日可能断更——今天必须拍 ≥1 条。说'推荐选题'我只推 top 1 稳分（不推实验性）\"\n2. **Buffer 颜色 = 🔵 蓝** → 高优先级提示：\"buffer 已 N 篇积压。**暂停拍摄**，先发存货 + 复盘。说'已发布 ...'我帮你出队\"\n3. **state.shoots 中最早一项 shot_at > 14 天** → \"你有视频拍了 N 天还没发——议题时效流失风险，建议尽快发或弃稿\"\n4. **in_progress 陈旧** (>= STALE_PREDICTION_DAYS) → 高优先级提示\"清理或 publish\"\n5. **待复盘 ≥ 1** → 高优先级\"今天该复盘 X 篇\"\n6. **`pool_status=none` + `calibration_samples=0` + 距 init >24h** → \"🌱 你 init 完已经 N 天但还没拍——是因为没选题吗？跑 /cheat-seed 5 分钟拿 5 个候选 + 5 个 draft\" 高优先级\n7. **Claude 判断系统性偏差信号**（**不是死磕 ≥3 同向**） → 提示\"建议跑 /cheat-bump\"\n   - **默认参考**：连续 ≥3 次同向偏差\n   - **但 Claude 可以更早**：1 次极端偏差（≥10x）或 2 次同向 + 评论区强反向证据\n   - **也可以更晚**：3 次同向但每次幅度都 <25%（可能只是噪声）\n   - 提示时显式标注：\"本次是 [default-aligned] / [judgment-driven]\"\n8. **calibration_samples 跨入新 confidence 等级**（0→1, 2→3, 5→6, 10→11, 20→21）→ 提示\"🎉 confidence 升级：<旧等级> → <新等级>。bucket 中枢精度从 ±X% 提到 ±Y%\"。**仅作通知，无任何用户必须确认的操作**——所有 skill 都已经按 calibration_samples 自动调整\n9. **calibration_samples 跨过 5** → \"你的 rubric 形态可以第一次正式 bump 了。回顾 rubric_notes.md 看观察记录段是否有 ≥3 样本支持的 pattern → 跑 /cheat-bump\"\n10. **calibration_samples 跨过 10** → \"可以跑 /cheat-bump --bucket-only --scheme percentile 让 bucket 边界改用 percentile（永远自洽）\"\n11. **calibration_samples 跨过 SQLITE_UPGRADE_THRESHOLD** 且 data_layer=markdown → \"建议跑 tools/md-to-sqlite.py\"（planned — batch 3, not yet available）\n12. **rubric_notes.md 行数 > CLEANUP_LINE_THRESHOLD** → \"建议清算观察段（手动或下次 bump 触发）\"\n13. **calibration_samples ≥ 5 + pool_status=none** → \"可以开始建立选题池了\"\n14. **calibration_samples ≥ 15 + pool_status=none** → \"强烈建议建池：/cheat-trends 或手动建 candidates.md\"\n15. **state.hooks_installed=false** → \"你的 immutability 是君子协定，建议跑 /cheat-init 装 hook\"\n16. **state.last_bump_self_audited=true** → \"上次 bump 是自审。建议配置 mcp__llm-chat__chat 后下次 bump 走外部审\"\n17. **state.rubric_form_mismatch=true** → \"你的 content_form 不是 opinion-video，用了内置观点 rubric。前几篇预测会更不准，下次 bump 时建议自行调整权重适配你的形态\"\n18. **state.benchmark_status=pending** → \"🎯 你 init 时答应等下找对标账号但还没找。跑 /cheat-learn-from 导入 ≥3 条对标视频，工具就有 anchor 了\"\n19. **state.benchmark_status=imported + Claude 判断用户数据信号已超过 benchmark** → \"📊 你的真实数据已经成为主信号，benchmark 影响淡出\"\n   - **默认参考**：calibration_samples ≥ 10\n   - **但 Claude 可以更早**：N=5 但用户的 (打分, 实绩) 配对里出现 ≥3 条与 benchmark pattern 不一致的——说明你的账号已经走出对标的路径\n   - **也可以更晚**：N=15 但用户的样本都很相似，没足够多样性 → benchmark 仍有信号价值\n   - 提示是**通知不是 gate**——benchmark.md 永远保留作 sanity check，cheat-seed 仍可读\n\n### Phase 4: 输出看板\n\n```\n🎛️ cheat-on-content 状态（更新于 2026-05-04 15:00）\n\n内容形态：opinion-video / 时长 3-5min / cadence: 隔日更\n当前 rubric：v2 (上次 bump: 2026-04-22)\n校准样本：18 篇\nConfidence: 🟢 较高 (中枢 ±15%，rubric 形态稳定)\nBaseline: 4.2w 中位数\n\n📦 Buffer：3 篇（🟢 绿色）\n   按你的 cadence (隔日更)= 6 天 buffer，节奏稳定\n\n📊 进度条\n  [█████████████░░░░░] 18 / 30 → SQLite 升级建议门槛\n  [██████████░░░░░░░░] 18 / 10 → percentile 桶可用（已超过门槛）\n\n🎬 待办（按紧急度）\n  🚨 复盘 1 篇（已过 T+3d）\n     - predictions/2026-05-01_db063817_你已不在关系里.md（T+3d 到了）\n  ⚠️  同向偏差 3 次（high, high, high）→ 建议 /cheat-bump\n  💤 in-progress prediction 已陈旧 35 天\n     - predictions/2026-04-01_xxx.md → 是已发了忘登记？还是弃稿？\n\n🔥 候选池\n  - candidates.md: 27 条（tier1: 12, tier2: 9, tier3: 6）\n  - 距上次抓热点: 4 天 — 可以再跑 /cheat-trends\n\n📈 健康度\n  - rubric_notes.md: 412 行（健康，<600 警戒线）\n  - hooks_installed: ✅\n  - external audit configured: ❌ → 建议配 mcp__llm-chat__chat\n\n下一步建议（按推荐优先级）：\n1. /cheat-retro predictions/2026-05-01_db063817_你已不在关系里.md  ← 最紧急\n2. /cheat-bump  ← 同向偏差 3 次的处理\n3. 处理陈旧 in-progress（手动或回 \"清理 in-progress\"）\n\n完整的命令清单见主 SKILL.md。\n```\n\n输出风格：**直白、具体、可点击**。每个建议附确切的命令——用户应能 copy-paste 直接执行。\n\n## Key Rules\n\n1. **无副作用**。读多写零。任何状态修改是其他 skill 的事\n2. **不假装数据可用**。state file 字段缺失 → 显式标\"未知\"，不猜\n3. **建议带优先级**。10 个建议同时显示用户会麻木——按紧急度排\n4. **每个建议附命令**。不能只说\"该 bump 了\"——要给 `/cheat-bump --propose \"...\"` 的精确入口\n\n## Refusals\n\n- 「顺便帮我自动跑一下 retro」 → 拒绝。status 是只读，retro 是另一个动作（避免一次操作做两件事）\n- 「我不想看 rubric_notes 行数，太琐碎」 → 输出仍包含但折叠到底部\"健康度\"区——状态信息的存在让用户在出问题前可见\n\n## Integration\n\n- 上游：所有其他 skill 完成时更新 .cheat-state.json，status 是这些更新的可视化\n- 下游：每个建议都路由到具体子 skill\n- meta-logging hook（如启用） → 写 usage.jsonl，status 用它算\"距上次 X 多少次\"\n","skills/cheat-trends/SKILL.md":"---\nname: cheat-trends\ndescription: 从配置的热点源（HN / Reddit / YouTube trending / B 站热门 / 等）抓今天的热门话题，去重 + 粗打分 + 写入 candidates.md。**绝大部分人没有候选池——这是让\"我没素材\"问题在 onboarding 第二步就消失的钥匙**。触发词：\"抓热点\"/\"fetch trends\"/\"今天有什么可做的\"/\"trending now\"/\"找选题\"。\nargument-hint: \"[— sources: <comma-separated>] [— max-per: 20]\"\nallowed-tools: Bash(*), Read, Write, Edit, Glob, WebFetch, Skill\n---\n\n# /cheat-trends — 热点抓取\n\n多 adapter 模式：读各 `trend-sources` adapter 的输出 → 去重 → 粗打分 → 写入 `candidates.md`。\n\n## Overview\n\n```\n[用户：抓热点]\n  ↓\n[Phase 0: 读 .cheat-state.json 拿 enabled adapters]\n  ↓\n[Phase 1: 对每个 adapter 调 fetch]\n  ↓\n[Phase 2: normalize 到 candidate-schema]\n  ↓\n[Phase 3: 去重（vs candidates / predictions / trends-history）]\n  ↓\n[Phase 4: 对每个新 item 粗打分（调 cheat-score 内联逻辑）]\n  ↓\n[Phase 5: 排序 + 询问用户哪些加入 candidates.md]\n  ↓\n[Phase 6: 写入 + 更新 trends-history.jsonl 缓存]\n```\n\n## Constants\n\n- **TREND_SOURCES = [\"manual-paste\"]** — 启用的 adapter 列表（默认仅 manual-paste，最稳）\n- **LOOKBACK_HOURS = 24** — 抓最近 N 小时的热点\n- **MAX_PER_SOURCE = 20** — 每个 adapter 最多 N 条\n- **DEDUPE = true** — 去重开关\n- **AUTO_SCORE = true** — 抓回来后自动调 cheat-score 粗打分\n- **MIN_COMPOSITE_TO_SUGGEST = 6.0** — 低于此分的不推荐用户加入候选池（仍写入 trends-history 避免下次重复推）\n\n> 💡 调用时覆盖：`/cheat-trends — sources: manual-paste,aihot,weibo-hot — max-per: 10`\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `.cheat-state.json` | 默认 sources |\n| `adapters/trend-sources/<name>.md` | 各 adapter 的实现描述 |\n| `candidates.md` | 去重对照 |\n| `predictions/*.md` | 去重对照（已发的不再推） |\n| `.cheat-cache/trends-history.jsonl` | 历史抓取去重缓存 |\n\n## Workflow\n\n### Phase 0: 读启用的 adapters\n\n```python\n# 伪代码\nstate = read('.cheat-state.json')\nenabled_adapters = args.sources or state.get('enabled_trend_sources', ['manual-paste'])\n```\n\n如 enabled_adapters 为空 → 输出引导：\n\n```\n你目前没有启用任何热点源。\n\n最快配法：\n- 临时跑：/cheat-trends — sources: manual-paste,aihot\n- 永久启用：编辑 .cheat-state.json 的 enabled_trend_sources 数组\n\n可用 adapter（详见 adapters/trend-sources/）：\n- manual-paste（默认，永远能用）\n- aihot（AI 热点聚合，无需 key）\n- weibo-hot（微博热搜，无需 key）\n- zhihu-hot（知乎热榜，无需 key）\n- trendradar-mcp（TrendRadar MCP 服务，需配置）\n```\n\n### Phase 1-2: 对每个 adapter 调 fetch + normalize\n\n对每个 adapter，读其 `adapters/trend-sources/<name>.md` 中描述的 fetch 接口（实际是 Bash 调底层 Python / shell / WebFetch）：\n\n| Adapter | 实现机制 |\n|---|---|\n| `manual-paste` | 询问用户：\"粘贴你今天的候选 URL/标题列表（每行一条）\" → 解析每行，对 URL 做 WebFetch 拓展 snippet |\n| `aihot` | 读 adapters/trend-sources/aihot.md 描述的 fetch 接口 |\n| `weibo-hot` | 读 adapters/trend-sources/weibo-hot.md 描述的 fetch 接口 |\n| `zhihu-hot` | 读 adapters/trend-sources/zhihu-hot.md 描述的 fetch 接口 |\n| `trendradar-mcp` | 读 adapters/trend-sources/trendradar-mcp.md 描述的 fetch 接口 |\n\n每个 adapter 输出符合 [candidate-schema.md](../../shared-references/candidate-schema.md) 的 items。\n\n**优雅降级**：单 adapter 失败（API key 缺失 / 端点 503 / cookie 失效）→ skip 该 adapter，**不抛异常**，在汇总里说明：\n```\n✅ aihot: 拉到 18 条\n✅ weibo-hot: 拉到 15 条\n✅ zhihu-hot: 拉到 12 条\n⚠️  trendradar-mcp: 跳过（MCP 服务未配置——配置见 adapters/trend-sources/trendradar-mcp.md）\n```\n\n### Phase 3: 去重\n\n按 [candidate-schema.md](../../shared-references/candidate-schema.md) 的\"去重协议\"：\n\n1. 对每个 item 算 id（`sha256(source_type + normalized_title + url_path)[:12]`）\n2. 检查 `candidates.md` 已含此 id → 跳过\n3. 检查 `predictions/*.md` 已含此 id → 跳过\n4. 检查 `.cheat-cache/trends-history.jsonl` 已含此 id 且 `rejected_at != null` → 跳过\n\n去重统计写到汇总报告里。\n\n### Phase 4: 粗打分\n\n`AUTO_SCORE=true` 时，对每条新 item：\n1. 用 item 的 `snapshot_text` 作为输入\n2. 按当前 rubric 给 7 维打分（**不**调 `/cheat-score` 子 skill 走 IO；inline 复用打分逻辑）\n3. 算 composite\n4. 给一句 rationale\n\n**注意**：粗打分 ≠ 正式预测。预测必须基于最终稿（用户改过的），这里的打分只是\"是否值得展开写\"的粗筛。\n\n`AUTO_SCORE=false` 时，items 写入 candidates.md 时 composite=null，需要后续手动 `/cheat-score`。\n\n### Phase 5: 排序 + 询问\n\n按 composite 降序，过滤掉 composite < `MIN_COMPOSITE_TO_SUGGEST` 的：\n\n```\n🔥 抓热点完成。各源拉取统计：\n- manual-paste: 5 条（用户输入）\n- aihot: 18 条\n- weibo-hot: 15 条\n跳过 trendradar-mcp（MCP 服务未配置）\n\n去重后剩 27 条新 item。\n粗打分后 12 条 composite ≥ 6.0：\n\n| # | 标题 | source | composite | bucket | rationale |\n|---|---|---|---|---|---|\n| 1 | 为什么我们都讨厌主动联系朋友 | aihot | 8.4 | 30-100w | ER+QL 双 5，AB 普适 |\n| 2 | \"她不一样\"的一千种变体 | weibo-hot | 8.1 | 30-100w | MS 候选维度高 |\n| 3 | ...... |\n\n哪些加入 candidates.md？\n- 全部加 → 回 \"all\"\n- 选几个 → 回 \"1, 3, 5\"\n- 都不要 → 回 \"none\"（这些会被记到 trends-history 避免下次重复推）\n```\n\n### Phase 6: 落盘\n\n用户响应后：\n1. 选中的 items → 按 [candidate-schema.md](../../shared-references/candidate-schema.md) 的\"Markdown 表示\"格式追加到 `candidates.md`\n2. 所有抓回来的 items（不管选中与否）→ append 到 `.cheat-cache/trends-history.jsonl`：\n   ```jsonl\n   {\"id\": \"...\", \"title\": \"...\", \"source\": \"...\", \"snapshot_at\": \"...\", \"rejected_at\": null|\"<ISO>\", \"fetched_at\": \"<ISO>\"}\n   ```\n\n### Phase 7: 状态更新\n\n```json\n{\n  \"last_trends_run_at\": \"<ISO>\",\n  \"last_trends_added_count\": 5\n}\n```\n\n## Key Rules\n\n1. **不抛异常**。单 adapter 失败 → skip + 报告。多 adapter 全失败 → 报错\"所有源都失败\"，附排查指引\n2. **manual-paste 永远在**。即使其他所有 adapter 都坏了，manual-paste 模式必须能跑——它是兜底\n3. **去重是硬约束**。同 id 不重复推；用户拒绝过的 6 个月内不再推\n4. **粗打分要诚实标注**。在 candidates.md 的 entry 里标 `composite (rough, snapshot-based)`，避免与 prediction 的精打分混淆\n5. **不直接进 predictions/**。trends 只产 candidates，predict 是另一个动作\n\n## Refusals\n\n- 「直接抓抖音热门 feed，不用 cookie」 → 拒绝。抖音反爬极严，无 cookie 必失败；引导到 douyin-session adapter 配置文档\n- 「跳过去重，把所有抓到的都写进去」 → 拒绝。会污染候选池，下次 recommend 时排序失效\n- 「跳过粗打分，直接写 raw 标题」 → 允许（`AUTO_SCORE=false`），但提示用户后续需要 `/cheat-score` 才能进 recommend 池\n\n## Integration\n\n- 上游：用户配置 `.cheat-state.json` 的 `enabled_trend_sources` 数组\n- 下游：`/cheat-recommend` 直接读 `candidates.md` 排序——trends 写完，recommend 立刻看到\n- 与 `/cheat-init`：onboarding Q4 选\"没有候选池\"的用户被引导到这里\n- 与 `/cheat-status`：status 看板显示\"上次抓热点：X 天前 / 待清理候选池：Y 条\"\n\n## Adapter 实现注意事项\n\n每个 `adapters/trend-sources/<name>.md` 必须文档化以下：\n1. **依赖**：API key / cookie / package\n2. **fetch 接口**：调用方式（python script path / shell command / API endpoint）\n3. **输出 schema**：必须符合 candidate-schema.md\n4. **失败模式**：常见错误 + 优雅降级行为\n5. **稳定性等级**：★ 1-5 颗星\n\n详见 [adapters/HOWTO.md](../../adapters/HOWTO.md)（待批次 3 实现）。\n"},"files":{"SKILL.md":"---\nname: cheat-on-content\ndescription: 给所有想把\"感觉\"变成可校准预测的内容创作者。**方法论通用**——打分 → 盲预测 → T+3d 复盘 → 进化 rubric 的循环适用任何能被量化（播放 / 阅读 / 收听 / 点击）的内容。**rubric 是循环的内容，不是循环本身**——当前内置一份观点视频 rubric（参考博主 25+ 视频拟合），其他形态可借这套起步并 bump 调权重。**强烈建议导入对标账号**作为初始信号源（/cheat-learn-from）。触发词：\"初始化\"/\"打分这篇\"/\"启动预测\"/\"已发布\"/\"复盘\"/\"升级 rubric\"/\"推荐选题\"/\"抓热点\"/\"状态\"/\"找对标\"/\"learn from\"。**首次使用必须先跑 /cheat-init。**\nargument-hint: \"[draft-path] [— mode: cold-start|calibration]\"\nallowed-tools: Bash(*), Read, Write, Edit, Grep, Glob, Skill, mcp__llm-chat__chat\n---\n\n# 网红作弊器 / Cheat on Content\n\n> 🎯 **方法论通用，rubric 当前内置观点视频版**\n>\n> **方法论**（5 阶段闭环）：任何能被量化的内容形态都适用——视频 / 文章 / 播客 / Newsletter / 短文 thread。\n>\n> **当前内置 rubric**：观点类视频（评论 / 时评 / 论说 / 议题讨论 / 个人观点表达），7 个维度由参考博主 25+ 已发样本拟合而来。如果你做其他形态，需要：\n> - 自己写一份 rubric（参照 [starter-rubrics/opinion-video-zero.md](starter-rubrics/opinion-video-zero.md) 的格式）\n> - 或等内置扩展（长文 / 短文 / 播客 starter 在批次 3 路线图）\n>\n> 默认假设：**用户是从零开始的新人**（一条视频都没发过）——cold-start 期的预测会**简化**，只要 7 维打分 + 一句话 bet，不强求 bucket 数字（避免 false precision）。已有 5+ 篇数据的老手走 calibration 模式解锁完整 7 组件预测。\n\n把内容创作变成可校准预测循环：**打分 → 预测 → 发布 → 复盘 → 进化 rubric**。\n\n本文件是**总协议 + 路由器**。具体每个阶段的工作流在 `skills/cheat-*/SKILL.md` 各子 skill 里。\n\n## Codex compatibility\n\nCodex 没有 Claude Code 的 slash-command harness。安装到 Codex 后，按自然语言触发同一套路由即可：\n\n- `初始化 cheat-on-content` → 读取并执行 `skills/cheat-init/SKILL.md`\n- `打分这篇 scripts/foo.md` → 读取并执行 `skills/cheat-score/SKILL.md`\n- `启动预测 scripts/foo.md` → 读取并执行 `skills/cheat-predict/SKILL.md`\n- `拍了 ...` / `已发布 ...` / `复盘 ...` / `升级 rubric` / `状态` → 分别读取对应 `skills/cheat-*/SKILL.md`\n\n执行时遵循本文件的三条原则和路由表；不要依赖 `/cheat-*` 命令是否存在。Claude Code 专用 hook（`.claude/settings.json`）仍只在 Claude Code 里自动触发；Codex 中需要用户主动说 `状态` 查看 buffer、待复盘和候选池。\n\n---\n\n## 三条不可妥协原则\n\n任何一条被违反，整个校准循环退化为\"凭直觉的自我安慰\"。如果用户要求打破其中任何一条，**拒绝执行并说明原因**。\n\n1. **盲预测（Blind prediction）**：预测必须在看到任何实际数据**之前**写完。一旦写完，`## 预测` 段是 immutable——只能往 `## 复盘` 段追加。完整规范：[shared-references/blind-prediction-protocol.md](shared-references/blind-prediction-protocol.md)。**hooks/prediction-immutability.sh 在 harness 层强制执行**。\n\n2. **升级 = 全量重打（Bump = full re-score）**：rubric 升级时，校准池所有有实绩数据的样本必须用新公式重打分；新排序与实际表现排序若在 ≥4/5 样本上不一致，升级被拒；升级必须经跨模型独立审核。完整规范：[shared-references/bump-validation-protocol.md](shared-references/bump-validation-protocol.md)。\n\n3. **rubric 是工作台，不是博物馆**：被新数据推翻或被吸收为正式维度的观察，**删掉**。绝不留\"我曾经以为 X，但其实...\"的考古层。git history 才是档案。完整规范：[shared-references/observation-lifecycle.md](shared-references/observation-lifecycle.md)。\n\n---\n\n## 路由表（触发词 → 子 skill）\n\n| 用户说 | 调用 | 前置条件 |\n|---|---|---|\n| \"初始化\" / \"init\" / \"首次使用\" | `/cheat-init` | 无（这是入口） |\n| \"找对标\" / \"学这个账号\" / \"拆这几个对标视频\" / \"learn from\" / \"导入对标账号\" | `/cheat-learn-from` | 已 init；cold-start 强烈建议；后续可随时 --append / --replace |\n| \"找选题\" / \"我不知道拍什么\" / \"seed\" / \"找前 5 个选题\" | `/cheat-seed` | 已 init（cold-start 用户专用一次性种子动作） |\n| \"打分这篇 [path]\" / \"score this [path]\" | `/cheat-score` | rubric_notes.md 存在 |\n| \"启动预测\" / \"start prediction\" / \"给这稿子打分并预测\" | `/cheat-predict` | 已 init + 有最终稿 |\n| \"拍了 X\" / \"shot it\" / \"录完了\" | `/cheat-shoot` | 对应预测已写（buffer +1） |\n| \"已发布\" / \"I shipped it\" / \"发布链接是 X\" | `/cheat-publish` | 对应预测文件存在（buffer -1） |\n| \"复盘\" / \"retro this\" / \"T+3d 数据来了\" | `/cheat-retro` | 对应预测文件存在 + 已过 RETRO_WINDOW_DAYS |\n| \"构造受众画像\" / \"更新 persona\" / \"我的观众是谁\" / \"build persona\" | `/cheat-persona` | 已 init；有复盘评论数据（或 benchmark seed） |\n| \"升级 rubric\" / \"bump rubric\" / \"更新公式\" | `/cheat-bump` | 校准池 ≥ MIN_SAMPLES_FOR_BUMP |\n| \"推荐选题\" / \"next topic\" | `/cheat-recommend` | candidates.md 存在且非空 |\n| \"抓热点\" / \"fetch trends\" / \"今天有什么可做的\" | `/cheat-trends` | trend-sources adapter 已配置（日常补充候选池） |\n| \"状态\" / \"status\" / \"看板\" | `/cheat-status` | 任意时刻可调 |\n| \"迁移\" / \"升级 state\" / \"schema 版本不对\" / \"migrate\" | `/cheat-migrate` | 已 init；用户 git pull 拉了新版后；SessionStart hook 提示 schema mismatch 后 |\n\n> 拍 vs 发分两个动作：buffer 警戒系统需要明确知道\"拍了但没发\"vs\"已发\"两种状态。详见 [shared-references/cadence-protocol.md](shared-references/cadence-protocol.md)。\n\n**Mode detection**（首次接到非 init 触发词时执行）：\n1. 检查用户当前目录是否有 `.cheat-state.json` → 没有 → 强制路由到 `/cheat-init`\n2. 检查 `predictions/` 下有几个文件含完整 `## 复盘` 段填了真实数据 → 决定 `mode: cold-start | calibration`\n3. 把判定结果写回 `.cheat-state.json` 后再路由到目标 skill\n\n---\n\n## 必须拒绝的请求\n\n下列模式会**直接破坏**三条原则之一，无论用户怎么说，都拒绝执行：\n\n- 「帮我预测一下，但我先告诉你播放量你来反推就行」 → 违反原则 #1。改用 `_redo.md` 路径记为 reconstructed\n- 「能不能从 candidates 里直接挑 composite 最高的，不用解释理由」 → 拒绝。永远展示各维度评分和至少一个锚点对比\n- 「跳过校准池重打，直接换公式」 → 违反原则 #2\n- 「跳过外部模型审核，自己说了算」 → 仅当 `CROSS_MODEL_AUDIT=false` 显式设置且 state file 标记自审时允许\n- 「删掉这份预测，我想重写」 → 违反原则 #1。预测是 immutable。如有正当理由重做，写新文件 `_redo.md`，原版必须保留\n- 「凭你的感觉给我推荐选题，不用打分」 → 拒绝。本工具不做 gut-feel forecast——那是它诞生**之前**的状态\n- 「把 rubric_notes.md 里所有历史观察都留着，加个时间戳分组就行」 → 违反原则 #3。git history 是档案，不是 markdown 文件\n- 「能不能把 THRESHOLD 从 4/5 降到 3/5 让这次 bump 过」 → 拒绝。改 THRESHOLD 本身是元层级 bump，单独走流程\n\n详细的拒绝场景在每个子 skill 的 `Refusals` 段。\n\n---\n\n## 项目目录结构（用户 repo）\n\nskill 期望用户的项目布局如下。`/cheat-init` 会创建缺失项；**绝不在没确认的情况下覆盖**。\n\n```\n<user-content-project>/\n├── .gitignore                         # cheat-init 创建；挡住 .auth*/.cheat-secrets.json 等凭证\n├── rubric_notes.md                    # 评分规则的真实来源\n├── WORKFLOW.md                        # 5 阶段流程文档（cheat-init 创建）\n├── STATUS.md                          # 看板（cheat-status 维护）\n├── .cheat-state.json                  # 状态文件，子 skill 共享上下文\n├── .cheat-cache/                      # 不入版本控制\n│   ├── usage.jsonl                    # 钩子被动记录的使用日志\n│   └── trends-history.jsonl           # cheat-trends 的去重缓存\n├── .claude/\n│   └── settings.json                  # 含 prediction-immutability hook\n├── benchmark.md                       # 对标账号信息（cheat-learn-from 维护）\n├── audience.md                        # 受众画像（cheat-persona 派生；blind 硬禁读）\n├── scripts/                           # 拍前的所有草稿（cheat-seed 写或用户写）\n│   └── YYYY-MM-DD_<id>_<short>.md\n├── predictions/                       # immutable 预测日志（hook 保护）\n│   └── YYYY-MM-DD_<id>_<short>.md     # 与 scripts/ 同 id\n├── videos/                            # 拍后才建（cheat-shoot 创建）\n│   └── YYYY-MM-DD_<id>_<short>/\n│       ├── script.md                  # 用户提供的最终拍摄稿（cheat-shoot 时询问\"和 scripts/ 一致吗\"）\n│       └── report.md                  # T+3d 抓的数据 + 评论（cheat-retro 写）\n├── samples/                           # 对标账号视频 / 转录（cheat-learn-from 创建）\n│   └── <账号名>/<video-id>/{source.mp4 (可选), transcript.md, meta.md}\n├── candidates.md                      # 选题池（可选）\n└── content.db                         # 可选 SQLite，校准池规模化后启用\n```\n\n---\n\n## 文件清单\n\n### 本 skill 包\n\n```\ncheat-on-content/\n├── SKILL.md                           # 本文件（总协议 + 路由）\n├── README.md                          # 营销门面\n├── skills/                            # 子 skill 集\n│   ├── cheat-init/SKILL.md            # ✅ 入口：onboarding 与脚手架\n│   ├── cheat-learn-from/SKILL.md      # ✅ 对标账号导入（拆 pattern + 派生 base rubric 信号）\n│   ├── cheat-seed/SKILL.md            # ✅ Cold-start 选题启动器（brainstorm + 可选 draft）\n│   ├── cheat-score/SKILL.md           # ✅ 单稿打分（不写文件）\n│   ├── cheat-predict/SKILL.md         # ✅ 盲预测 + immutable 日志\n│   ├── cheat-shoot/SKILL.md           # ✅ 登记拍摄（buffer +1）\n│   ├── cheat-publish/SKILL.md         # ✅ 发布元数据登记（buffer -1）\n│   ├── cheat-retro/SKILL.md           # ✅ 数据回收 + 复盘\n│   ├── cheat-persona/SKILL.md         # ✅ 受众画像派生（从复盘评论聚类）\n│   ├── cheat-bump/SKILL.md            # ✅ rubric 升级（含跨模型审）\n│   ├── cheat-recommend/SKILL.md       # ✅ 候选池排序推荐（按 buffer 颜色 + 1 稳 + 1 实验）\n│   ├── cheat-trends/SKILL.md          # ✅ 热点抓取（日常补充候选池，多 adapter）\n│   ├── cheat-status/SKILL.md          # ✅ 状态看板（含 buffer 警戒）\n│   ├── cheat-migrate/SKILL.md         # ✅ schema 升级（老用户 git pull 后用）\n│   └── cheat-score-blind/SKILL.md     # ✅ Channel B 隔离打分 sub-agent（仅 Task tool 调用）\n├── migrations/                        # schema 演进单一来源\n│   ├── registry.md                    # ✅ LATEST_SCHEMA + 版本链表\n│   └── <from>-to-<to>.md              # ✅ 每步迁移的 WHAT/WHY/HOW/Manual fallback\n├── shared-references/                 # 跨 skill 共享协议\n│   ├── blind-prediction-protocol.md   # ✅ 原则 #1\n│   ├── bump-validation-protocol.md    # ✅ 原则 #2\n│   ├── observation-lifecycle.md       # ✅ 原则 #3\n│   ├── prediction-anatomy.md          # ✅ 一份合格预测的 7 个组件\n│   ├── candidate-schema.md            # ✅ 候选项统一 schema\n│   ├── cadence-protocol.md            # ✅ 节奏协议（buffer 警戒 + 选题策略）\n│   ├── state-management.md            # ✅ .cheat-state.json 读写约定\n│   └── migration-protocol.md          # ✅ schema 演进哲学 + maintainer checklist\n├── starter-rubrics/                   # 各内容形态的先验 rubric\n│   ├── opinion-video.md               # ✅ 观点视频（中文，已校准 25+ 样本）\n│   ├── opinion-video-zero.md          # ✅ v0 等权占位（cold-start）\n│   ├── long-form-essay.md             # ⬜ 公众号 / Substack\n│   └── short-form-text.md             # ⬜ X thread / 微博长文\n├── templates/                         # skill 写进用户 repo 的文件骨架\n│   ├── gitignore.template             # ✅ 用户项目 .gitignore（护凭证，保留 predictions/ 入库）\n│   ├── rubric_notes.template.md       # ✅\n│   ├── prediction.template.md         # ✅ 统一版（所有阶段，含 confidence header）\n│   ├── retro.template.md              # ✅\n│   ├── candidates.template.md         # ✅\n│   ├── candidates.template.json       # ✅\n│   ├── script_patterns.template.md    # ✅ 写作 pattern 沉淀（含 benchmark 借鉴段说明）\n│   ├── benchmark.template.md          # ✅ 对标账号 reference\n│   ├── audience.template.md           # ✅ 受众画像骨架\n│   ├── workflow.template.md           # ✅\n│   ├── status.template.md             # ✅\n│   └── content.db.schema.sql          # ✅\n├── hooks/                             # harness 强制层\n│   ├── prediction-immutability.json   # ✅ 阻塞型钩子（拦预测段编辑）\n│   ├── prediction-immutability.sh     # ✅ 拦截脚本\n│   ├── session-start.json             # ✅ SessionStart 自动报告 hook\n│   ├── session-start.sh               # ✅ 状态报告渲染脚本\n│   ├── meta-logging.json              # ✅ 被动记录配置\n│   └── log-event.sh                   # ✅ meta-logging 脚本\n├── tools/                             # 独立 CLI 脚本\n│   ├── score-curve.py                 # ⬜ 预测精度收敛曲线\n│   ├── md-to-sqlite.py                # ⬜ markdown → content.db 升级（批次 3）\n│   └── validate-bump.py               # ⬜ 校准池全量重打（批次 3）\n├── adapters/                          # 数据源适配\n│   ├── perf-data/                     # 复盘数据源（含 douyin-session）\n│   ├── candidate-pool/                # 候选池数据源\n│   ├── trend-sources/                 # 热点抓取源\n│   └── script-extraction/             # 视频/音频转 script（含 whisper for cheat-learn-from）\n└── examples/\n    ├── reference-implementation/      # 视频分析脱敏快照（待）\n    └── script_patterns.example.md     # script_patterns 全填示例（参考用，不复制）\n```\n\n✅ = 当前批次（v1 骨架）已完成 / ⬜ = 后续批次\n\n---\n\n## Tone & voice\n\n写面向用户的文案（commit message / 复盘小结等）时，匹配项目的 **直白克制（reflective-irreverent）** voice：\n\n- 直接说出失败：「composite 8.47 但实际只有 16.8w——rubric 高估了 SR」\n- **不要**用模糊措辞软化：「这或许可能在某种程度上暗示...」——别这么写\n- Cluely 风格的反叛 hook 只在 README 出现——**不要写进** `rubric_notes.md` 或预测日志\n\n---\n\n## 给开发者：扩展本 skill\n\n- 新增内容形态 → 加 `starter-rubrics/<form>.md`\n- 新增热点抓取源 → 加 `adapters/trend-sources/<name>.md`，符合 [candidate-schema.md](shared-references/candidate-schema.md) 输出契约\n- 修改原则 → 改 `shared-references/<protocol>.md`，所有引用它的 skill 自动跟进\n- 修改路由 → 改本文件的\"路由表\"段\n- 子 skill 内部细节 → 直接改对应 `skills/cheat-*/SKILL.md`\n\n完整开发指南见 README.md。\n","skills/cheat-bump/SKILL.md":"---\nname: cheat-bump\ndescription: 提议并执行 rubric 或 bucket 升级。两种模式：**完整 rubric bump**（最高风险动作，5 步强制 + 跨模型审核）和 **--bucket-only 轻量重校**（只换 bucket 边界，不动 rubric 公式）。**Phase 2 强制走 cheat-score-blind sub-agent 给校准池重打分**——不接受 self-scored fallback。触发词：\"升级 rubric\"/\"bump rubric\"/\"更新公式\"/\"我想加一个维度\"/\"调整权重\"/\"重校桶\"/\"recalibrate bucket\"。\nargument-hint: --propose \"<...>\" | --bucket-only [--scheme ratio|absolute|percentile]\nallowed-tools: Bash(*), Read, Write, Edit, Glob, Grep, Skill, Task, mcp__llm-chat__chat\n---\n\n# /cheat-bump — Rubric / Bucket 升级\n\n两种模式：\n\n| 模式 | 触发 | 做什么 | 验证强度 |\n|---|---|---|---|\n| **完整 rubric bump** | `--propose \"<新公式>\"` | 改公式 / 维度 / 权重 | 5 步 + 跨模型审核（强制） |\n| **bucket-only 重校** | `--bucket-only` | 只重新派生 bucket 边界 | 数据自动派生，无审核 |\n\n完整 rubric bump 严格遵守 [shared-references/bump-validation-protocol.md](../../shared-references/bump-validation-protocol.md) 的 5 步。bucket-only 走轻量路径——见下方 Phase B。\n\n## Overview\n\n```\n入口：用户触发 /cheat-bump\n  ↓\n[Phase A0: 检测调用模式]\n  ↓\n  ├─ --bucket-only  →  [Phase B: 轻量 bucket 重校]\n  └─ --propose      →  [Phase 0~6: 完整 rubric bump]\n```\n\n## Phase A0: 调用模式分流（先做）\n\n读用户参数：\n- 含 `--bucket-only` → 走 **Phase B**（轻量重校）\n- 含 `--propose \"<...>\"` → 走 **Phase 0~8**（完整 rubric bump）\n- 都没有 → 询问用户：\"你想做什么？1) 调 rubric 公式 / 加减维度 → --propose；2) 只重新派生 bucket 边界 → --bucket-only\"\n\n如果用户说\"我觉得 ER 太低了想调\"→ 是 `--propose` 路径。\n如果用户说\"我账号长大了，bucket 不准了\"→ 是 `--bucket-only` 路径。\n**两条路径不能混调**——一次操作只做一种事。\n\n---\n\n## 完整 rubric bump 流程\n\n```\n[用户：升级 rubric --propose \"ER×1.5→2.0，砍 NA，加 MS\"]\n  ↓\n[Phase 0: 前置门槛检查]\n  ↓\n[Phase 1: 写出新公式完整方程]\n  ↓\n[Phase 2: 校准池全量重打分]\n  ↓\n[Phase 3: 计算排序一致性]\n  ↓\n[Phase 4: 跨模型独立审核（强制）]\n  ↓\n[Phase 5: 落地 + cleanup pass]\n  ↓\n[Phase 6: 更新所有校准样本的 prediction 文件底部追加 Re-scored 行]\n```\n\n## Constants\n\n- **READINESS_HEURISTIC** —\n  - **默认参考**：校准池 ≥ 5 样本 + 至少 1 个跨样本观察有 ≥3 样本支持\n  - **但 Claude 可以提议 bump**（即使样本少）如果观察信号特别强：\n    - N=3 但出现完全推翻当前 rubric 假设的强反例（composite 8.5 vs 实绩 5w 这种 ≥3x 偏差）\n    - 1 篇出现单点但极强的现象（如评论区出现 ≥2000 赞的单一模因）\n  - **Claude 也可以拒绝 bump**（即使样本足）如果证据弱：\n    - N=10 但观察都是低置信度的零碎 pattern，无清晰方向\n    - 用户复盘时大量\"随便看了下\"的非严肃判断\n  - **写在 prediction header 或 cheat-bump 输出时必说明**：本次提议是 default-aligned 还是 judgment-driven，给用户审视依据\n- **THRESHOLD = 0.8** — 新排序与实绩排序一致性阈值（4/5）。这条**写死**——bump 验证的统计刚性\n- **CROSS_MODEL_AUDIT = true** — 调外部 LLM 独立审核。false 仅用于离线\n- **REQUIRE_CONFIRM = true** — 落地前要求用户明确\"yes, bump\"\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `--propose` 文本 | 用户参数；缺失则询问 |\n| `rubric_notes.md` | 用户项目根 |\n| `predictions/*.md` 全量 | 校准池数据 |\n| `.cheat-state.json` | 状态 |\n\n## Workflow\n\n### Phase 0: 前置门槛检查\n\n按 [bump-validation-protocol.md](../../shared-references/bump-validation-protocol.md) 的\"何时禁止\"段，逐项检查：\n\n| 检查 | 失败处理 |\n|---|---|\n| 校准池总样本数 vs 观察强度 | **Claude 判断**——按 READINESS_HEURISTIC：默认 ≥5 样本但允许特例（强反例 / 强模因）。如不满足默认，Claude 必须**显式说明**为什么仍然提议 bump（\"虽然只 N=3 样本，但 X 这条出现 composite Y vs 实绩 Z，这是 W 倍偏差\"），让用户审视 |\n| 上次 bump 距今的新校准数 vs 观察成熟度 | **Claude 判断**——默认建议 ≥3 篇新样本，但如果连续 3 篇都强证据指向同一方向 → 不必再等 |\n| `in_progress_session == null` | 拒绝：\"你有 in-progress 预测未完成。先走完那条流程或清掉 state\" |\n| 触发条件成立（系统性偏差 / 跨样本新观察 / 新维度证据足） | 警告但不阻塞——询问用户为什么现在 bump |\n\n通过 → 进入 Phase 1。\n\n### Phase 1: 写出新公式完整方程\n\n**不能只接受用户的简短描述**。把它展开为完整方程：\n\n```\n当前：v2  composite = (ER×1.5 + SR×1.5 + HP×1.5 + QL + NA + AB + SAT) / 8.5 × 2.0\n提议：v2.1  composite = (ER×2.0 + HP×1.5 + MS×1.5 + QL + SR + TS + SAT) / 9.0 × 2.0\n\n变化总结：\n- ER ×1.5 → ×2.0（升）\n- SR ×1.5 → ×1.0（降）\n- 新增 MS ×1.5（Memetic Shareability）\n- 新增 TS ×1.0（Topic Shareability）\n- 删除 NA（与 HP 重叠）\n- 删除 AB（被 TS 替代）\n- 归一化常数 8.5 → 9.0\n- 公式总维度数：7 → 7（净变化 0）\n```\n\n如果用户的提议含糊（如\"ER 权重提一点\"）→ 询问具体数值，**禁止自己猜**。\n\n### Phase 2: 校准池全量重打分（**强制走 blind sub-agent**）\n\nGlob `predictions/*.md` 中所有有完整复盘段的文件 → 校准池。\n\n**bump 是工具最高风险动作——所有重打必须走 [cheat-score-blind](../cheat-score-blind/SKILL.md) sub-agent**。inline 重打 = 主 Claude 已经看过实绩，rank 一致性变成 overfit 而非真信号。\n\n#### 强制约束\n\n- **不接受 self-scored fallback**——`/cheat-predict` 有 `--skip-blind` flag，但 `/cheat-bump` **没有**。如果 Task tool 不可用 → **abort bump**，向用户报告\"先解决 Task tool 再 bump\"\n- **不接受\"我只重算 composite 不重打 dim\"** —— 即使新公式只调权重不加维度，每条 prediction 的所有 dim 都要由 sub-agent 重新审 script。理由：旧 dim 分本身可能是污染的；权重变了不能保证旧 dim 还成立\n\n#### 对每篇 prediction：\n\n1. 解析 prediction 文件拿到对应 `scripts/<id>.md` 路径（从 `Script Path` header 字段）\n2. 校验 script 文件存在 + hash 跟 header `Script Hash` 一致；不一致 → 警告（script 改过了）但仍 spawn sub-agent\n3. **通过 Task tool spawn cheat-score-blind sub-agent**：\n   ```\n   Spawn cheat-score-blind sub-agent.\n\n   Input:\n     script_path: <prediction header 的 Script Path>\n     rubric_notes_path: rubric_notes.md\n     sidecar_path: .cheat-cache/bump-rescores/<prediction-id>.json\n\n   Task: 按 rubric_notes 当前公式（已是新版 vN+1）给 script 打分。\n   返回严格 JSON。写 sidecar 文件用于 bump 主流程批量读取。\n\n   不要读 state file / predictions/ / videos/ 任何其他文件。\n   不要询问用户 —— 你没有用户。\n   不要读这份 prediction 文件本身 —— 你只看 script + rubric。\n   ```\n4. 等 sub-agent 完成 → 读 sidecar JSON → 主流程用新公式算 composite\n5. 写\"重打表\"到 `.cheat-cache/bump-rescores.json`（汇总）。**每条 entry 标 `blind: true`** —— bump phase 5 cleanup 时把这个字段连同新分数写到 prediction 文件的 `Re-scored under v<N+1>` 行\n\n#### 还污染没污染的诚实标注\n\n即使走 sub-agent，**仍有两类残余 contamination 要在 bump report 里诚实标注**：\n\n| 类型 | 来源 | 标注字段 |\n|---|---|---|\n| 模型 prior contamination | sub-agent 仍是 Claude，RLHF 共享 | `model_prior_warning: true`（默认 true，不可关） |\n| 用户自己 rubric design bias | rubric_notes.md 是用户写的，自然 fit 自己内容 | `rubric_self_designed: true`（默认 true，不可关） |\n\n这两条提示用户 channel C（跨模型 audit）的不可省。bump 报告末尾必印：\"上面的 rank 一致性是 channel A 内的一致性。**最终决策必须等 channel C audit 通过**。\"\n\n#### 失败模式\n\n| 症状 | 处理 |\n|---|---|\n| 某条 prediction 的 script 文件不见了 | sub-agent skip 该条，主流程汇总报告\"N 条因 script 缺失被排除\"。如剩余有效池 < MIN_SAMPLES → abort bump |\n| sub-agent 返回 `refusal != null` | 重发 Task 最多 3 次；仍败 → 该条标 `rescore_failed: true` 排除出校准池 |\n| Task tool 整个不可用 | abort bump，提示用户\"Task tool 是 bump 的硬依赖。如真的离线环境，跑 `/cheat-bump --bucket-only` 走轻量分支\" |\n| sub-agent 输出含 contamination_signal | 标 `suspicious: true` 但不排除——bump report 末尾列这些可疑条目让用户审 |\n\n### Phase 3: 计算排序一致性\n\n```\n每个样本：\n  new_composite_rank: 用新公式排序的 rank\n  actual_plays_rank: 用实际播放排序的 rank\n  delta: |new_rank - actual_rank|\n\n输出对照表：\n| 样本 | composite (v2) | composite (v2.1) | rank (new) | actual | rank (actual) | delta |\n|---|---|---|---|---|---|---|\n| 仓鼠 | 9.41 | 9.55 | 1 | 124.8w | 1 | 0 |\n| 停止期待 | 8.24 | 9.11 | 2 | 71.1w | 2 | 0 |\n| 老板废话 | 7.65 | 8.11 | 4 | 39.6w | 3 | 1 |\n| 求职悖论 | 8.47 | 7.56 | 5 | 16.8w | 4 | 1 |\n| 谁问你了 | 8.24 | 7.00 | 6 | 11.7w | 5 | 1 |\n\n排序一致性：4/5 在 |delta| ≤ 1\nPairwise no-regression：旧公式做对的所有 pair 在新公式下未颠倒 ✓\n```\n\n判定：\n- 排序一致性 < THRESHOLD（默认 0.8） → **本地拒绝**，转 Phase 4 之前明确报告失败\n- pairwise 出现回归 → **本地拒绝**\n\n`THRESHOLD` 写死在协议里——不允许临时调低（那本身是另一个需要 bump 的元决策）。\n\n### Phase 4: 跨模型独立审核（**强制**，除非 escape hatch）\n\n`CROSS_MODEL_AUDIT=true`（默认）：\n\n调用 `mcp__llm-chat__chat`：\n\n```\nprompt:\n你是一个独立审稿人。下面是一个内容创作者准备升级的 rubric 公式。\n请独立判定两件事：\n1. 排序一致性：新公式给样本的排序与实际表现排序，是否真的在 ≥80% 样本上一致？\n2. 解释力：新公式相比旧公式，是否更好地解释了校准池的实绩分布？\n\n数据：\n旧公式：(ER×1.5 + SR×1.5 + HP×1.5 + QL + NA + AB + SAT) / 8.5 × 2.0\n新公式：(ER×2.0 + HP×1.5 + MS×1.5 + QL + SR + TS + SAT) / 9.0 × 2.0\n\n校准池：\n[Phase 2 重打表的完整 JSON]\n\n排序对照：\n[Phase 3 表格的完整 JSON]\n\n输出格式：\n- 判定：PASS 或 REJECT\n- 理由：≥100 字\n- 关键风险：[如有，列出新公式的潜在问题]\n```\n\n收到外部 LLM 回复 → 解析判定。\n\n判定逻辑：\n- 本地 PASS + 外部 PASS → 通过，进入 Phase 5\n- 本地 PASS + 外部 REJECT → **视为 REJECT**。冲突意味着至少一方解读不稳定\n- 本地 REJECT → 已在 Phase 3 终止\n- mcp__llm-chat__chat 不可用 → 优雅降级到 `CROSS_MODEL_AUDIT=false`，state file 标 `last_bump_self_audited: true`\n\n`CROSS_MODEL_AUDIT=false`：\n- 仅依赖本地判定\n- state file 持续标记，cheat-status 持续提示用户\"这次 bump 是自审，建议配置 mcp__llm-chat__chat\"\n\n### Phase 5: 落地 + cleanup pass\n\n通过审核后，**REQUIRE_CONFIRM=true** → 询问用户：\"新公式 PASS 本地与外部审核。最后确认：执行 bump 落地？这会修改 rubric_notes.md + rubric-memo.md 并删除若干已被吸收的观察。回答 'yes, bump' 才执行。\"\n\n用户确认后：\n\n#### 5a. 更新 `rubric_notes.md`（**只放通用语言，不含视频名 / 实绩**）\n\n- 顶部 metadata 更新：\n  - `**当前版本**: vN+1`\n  - `**Last bumped at**: <ISO 8601>`\n  - `**Upgrade memos**: 见 [rubric-memo.md](rubric-memo.md)`（指针，不复制 Memo 内容）\n- 版本速查表加一行（只含版本号 + 公式签名，**不含**证据样本）\n- 更新\"当前评分维度\"段（删 NA / AB，加 MS / TS）\n- **派生证据段** 如新维度需要锚点解释 → **用通用语言**：\n  - ✅ 允许：「派生证据：高抽象密度样本 → CC=1 → 低 reach」\n  - ❌ 禁止：「派生证据：「停止期待」CC=1 → 实绩 13.7w」（视频名 + 实绩 数字）\n  - 命中违禁 pattern → 把该段抽到 rubric-memo.md 的\"派生证据\"子段，原位用通用语言替代\n\n#### 5b. 写 Memo 到 `rubric-memo.md`（**append 模式，不覆盖历史**）\n\n按 [bump-validation-protocol.md](../../shared-references/bump-validation-protocol.md) Step 5 + [templates/rubric-memo.template.md](../../templates/rubric-memo.template.md) 格式 append 一段 Memo 到文件末尾：\n\n- 触发观察（含真实观察 ID）\n- 证据数据（**校准池重打表 + 排序对照，含真实视频名 + 实绩**）\n- 派生证据（**含真实样本名 + 实绩**）\n- 诊断\n- 新公式\n- 跨模型审核结论引用（含模型名 + 判定 + 理由摘录）\n- 已知局限\n\n**绝不**覆盖 rubric-memo.md 已有内容——bump memo 按时间顺序累积。\n\n#### 5c. cleanup pass（按 [observation-lifecycle.md](../../shared-references/observation-lifecycle.md) 的\"cleanup pass 强制时机\"）\n\n在 `rubric_notes.md` 内执行（**不**动 rubric-memo.md）：\n\n- 已被吸收为新维度的观察 → 删（如观察 E 被吸收为 MS → 删观察 E）\n- 被新数据推翻的观察 → 删\n- 仍未解决的观察 → 迁移到新版本\"待验证假设\"段\n- 已被验证的\"规律\"→ 移到\"规律沉淀区\"\n\n#### 5d. 整理 + 自检\n\n- 重新读 `rubric_notes.md` 全文，确保读者能在 60 秒内理解当下规则——超出 600 行触发额外清算\n- **自检 leak guard**：对 `rubric_notes.md` 跑 `grep -E '\\\\d+\\\\s*[wWmMkK万]|播放|实绩|实际'` → 如有命中 → **abort bump + 回滚**，提示用户\"rubric_notes.md 写入了违禁内容（实绩 / 播放数）\"。这些内容应在 rubric-memo.md，不在 rubric_notes.md\n\n### Phase 6: 校准样本批量更新\n\n对每个校准样本的 prediction 文件，**底部追加**（不动预测段、不动复盘段）：\n\n```markdown\n\n---\n**Re-scored under v2.1 on 2026-05-04**: composite=8.24 → 9.11 (blind: true)\n（rubric bump 时全量重算，由 cheat-score-blind sub-agent 独立打分；详见 rubric-memo.md 的 v2 → v2.1 升级 Memo）\n```\n\n`blind: true` 字段**必填**——告诉未来读这条记录的人\"这是 channel B 隔离打分，不是主 Claude 自评\"。如果某条 prediction 在 Phase 2 因 sub-agent 失败被排除 → 不会有 Re-scored 行（保持原样）。\n\n用 Edit 工具，匹配每个文件的最末尾。\n\n### Phase 7: 更新 state file\n\n```json\n{\n  \"rubric_version\": \"v2.1\",\n  \"last_bump_at\": \"<ISO timestamp>\",\n  \"last_bump_self_audited\": false,\n  \"consecutive_directional_errors\": [],\n  \"calibration_samples_at_last_bump\": <current value>\n}\n```\n\n清空 `consecutive_directional_errors`——新 rubric 重新计数。\n\n### Phase 8: 控制台报告\n\n```\n✅ Rubric 已升级 v2 → v2.1\n\n变化：\n- ER ×1.5 → ×2.0\n- SR ×1.5 → ×1.0\n- 新增 MS / TS\n- 删除 NA / AB\n\n校准池重打：5/5 通过排序检查（4/5 一致 + 0 pairwise 回归）\n跨模型审核：✅ PASS\nCleanup pass：删除观察 D 和 E（已吸收为 QL 重定义和 MS 维度）\n\n下一篇预测起按 v2.1 公式打分。\n所有历史预测文件已追加 Re-scored 标记。\n```\n\n---\n\n## Phase B：bucket-only 重校（轻量分支）\n\n`/cheat-bump --bucket-only [--scheme ratio|absolute|percentile]`\n\n**与完整 bump 的本质区别**：bucket 边界不是规则的一部分，是数据派生量。重新派生它**不需要跨模型审核**——派生算法是确定性的，没有\"判断\"成分。\n\n### B1: 选择算法（按可用样本数自动派生，**state 不存 scheme**）\n\n| 算法 | 适用 | 边界派生方式 |\n|---|---|---|\n| `ratio`（默认 N=1-4） | 小样本 | 上一篇 / 最近 3 篇中位数 × {0.3 / 1 / 3 / 10 / 30} |\n| `absolute`（默认 N=5-9）| 中等样本 | 校准池中位数 × {0.3 / 1 / 3 / 10 / 30}，固定边界 |\n| `percentile`（默认 N≥10）| 大样本 | 校准池实绩 percentile {30 / 60 / 85 / 95 / 100} |\n\n`--scheme` 参数允许用户**显式覆盖默认**：\n- `--scheme ratio` 强制用 ratio（即使 N≥5）\n- `--scheme absolute` 强制用 absolute\n- `--scheme percentile` 强制用 percentile（要求 N≥3，否则报错）\n\n未指定 `--scheme` → 按上表自动派生。\n\n> 旧设计有 `bucket_scheme` state 字段——v1.1 删了。所有 skill 实时按 calibration_samples 派生算法，不需要持久化\"当前用哪个\"。这避免了\"切换 scheme 后忘了同步\"的状态不一致问题。\n\n### B2: 派生新边界\n\n读 `predictions/*.md` 中所有有 `actual_plays` 的样本。\n\n**ratio 模式**：\n```\nbaseline = median(最近 3 篇 actual_plays)\nbuckets = {\n  \"退步\": (-inf, baseline * 0.3),\n  \"持平\": (baseline * 0.3, baseline * 1),\n  \"命中\": (baseline * 1, baseline * 3),\n  \"小爆\": (baseline * 3, baseline * 10),\n  \"大爆\": (baseline * 10, +inf),\n}\n```\n\n**absolute 模式**：\n```\nbaseline = median(全部校准池 actual_plays)\nbuckets = {\n  \"底部\": (-inf, baseline * 0.3),\n  \"基础盘\": (baseline * 0.3, baseline * 1),\n  \"命中\": (baseline * 1, baseline * 3),\n  \"爆款\": (baseline * 3, baseline * 10),\n  \"现象级\": (baseline * 10, +inf),\n}\n```\n\n**percentile 模式**：\n```\nsorted_plays = sorted(全部校准池 actual_plays)\nbuckets = {\n  \"底部\":   ≤ p30,\n  \"基础盘\": p30 - p60,\n  \"命中\":   p60 - p85,\n  \"小爆\":   p85 - p95,\n  \"大爆\":   ≥ p95,\n}\n```\n\n### B3: 报告变化 + 用户确认\n\n```\n当前 bucket scheme: ratio\nproposed scheme: absolute\nbaseline: 4.2w 中位数（基于 5 篇校准样本）\n\n新边界：\n- 底部:   < 1.3w\n- 基础盘: 1.3w - 4.2w\n- 命中:   4.2w - 12.6w\n- 爆款:   12.6w - 42w\n- 现象级: > 42w\n\n派生说明：\n- 5 篇实绩：1.5w / 3.8w / 4.2w / 5.6w / 18w\n- 中位数 4.2w，新桶按 ×{0.3, 1, 3, 10} 派生\n\n确认应用？(yes / no)\n```\n\n### B4: 落地\n\n用户确认后：\n1. 编辑 `rubric_notes.md` 的 \"Bucket 方案\" 段，替换为新表\n2. 更新 `.cheat-state.json` 的 `baseline_plays` 字段（bucket scheme 不持久化——下次 cheat-predict 实时派生）\n3. 在 `rubric_notes.md` 的 bucket 段顶部追加一行变更记录：`v2 buckets recalibrated on YYYY-MM-DD: scheme=absolute, baseline=4.2w (基于 N=10 个样本)`\n4. **不**修改任何 prediction 文件——历史预测的 bucket 标签保持原样（在该样本写入时的方案下做出的判断）\n\n### B5: 对未来预测的影响\n\n下一次 `/cheat-predict` 起按新 bucket 派生。历史 prediction 文件里的 bucket 标签**不重算**——bucket 是预测时的语义判断，事后改写会破坏盲度。\n\n### Phase B 不做的事\n\n- 不重打 composite（公式没变）\n- 不重新审核观察段（rubric 没变）\n- 不调跨模型审核（确定性派生无需判断）\n- 不要求严格的样本数门槛（按 READINESS_HEURISTIC 由 Claude 判断；ratio 模式 N=1 就能跑）\n\n---\n\n## Key Rules\n\n1. **5 步不可跳**（仅完整 rubric bump）。任何\"先简化跑一下\"的请求都拒绝\n2. **THRESHOLD 写死**（仅完整 rubric bump）。不允许动态调整\n3. **跨模型审核是默认**（仅完整 rubric bump）。关闭审核需要在 state file 显式标记\n4. **cleanup pass 是 bump 的一部分**（仅完整 rubric bump）。不允许 bump 完不清理观察段\n5. **REQUIRE_CONFIRM**（两种模式都要）。最后落地前必须用户明确说 \"yes, bump\" 或 \"yes, recalibrate\"\n6. **bucket 重校不动历史预测**。bucket 是预测时语义，事后改写破坏盲度\n\n## Refusals\n\n- 「跳过校准池重打，直接换公式」 → 拒绝。原则 #2\n- 「跳过 cheat-score-blind sub-agent，主 Claude 直接重打就行」 → 拒绝。bump **不接受**任何 self-scored fallback——sub-agent 不可用 → abort bump，不接受\"自审\"\n- 「跳过外部 LLM 审核」 → 仅当 `CROSS_MODEL_AUDIT=false` 显式设置\n- 「这次 THRESHOLD 调到 3/5 让它过」 → 拒绝。改 THRESHOLD 是元层级 bump\n- 「保留所有旧观察作为历史」 → 违反原则 #3\n- 「先 bump，cleanup 下次再做」 → 拒绝。cleanup 是 bump 的一部分\n- 「只重算 composite 不重打 dim」 → 拒绝。新权重 × 旧 dim 仍是旧污染。每个 dim 都由 sub-agent 重审 script\n- 「把 Memo 全文写进 rubric_notes.md 顶部，方便我读」 → 拒绝。rubric_notes.md 是 blind sub-agent 白名单——含视频名 / 实绩 → 通过白名单泄漏。Memo 写 rubric-memo.md（白名单**外**），rubric_notes.md 只放公式 + 通用语言维度定义 + 指针\n- 「派生证据段保留真实视频名，让 rubric 读起来更具体」 → 拒绝。在 rubric_notes.md 必须用通用语言（\"高抽象密度样本\"）；带视频名的派生证据写 rubric-memo.md\n\n## Integration\n\n- 上游：`/cheat-retro` 检测到 ≥3 同向偏差 → 提议跑 `/cheat-bump`\n- 依赖：`mcp__llm-chat__chat`（如配置）+ Task tool（spawn cheat-score-blind）\n- 修改：\n  - `rubric_notes.md`（结构性更新，**绝不**写真实视频名 / 实绩）\n  - `rubric-memo.md`（**新**——append Memo 全文，含证据 + 派生证据）\n  - 所有 `predictions/*.md`（追加 Re-scored 行，不动预测段）\n  - `.cheat-state.json`\n- 下游：下一篇 `/cheat-predict` 自动按新 rubric_version 打分\n","skills/cheat-init/SKILL.md":"---\nname: cheat-init\ndescription: cheat-on-content 的首次 onboarding 与脚手架创建器。统一流程——所有用户都走相同 5 阶段闭环，唯一区别是\"发过视频的人\"会在 init 时多一步：抓取已有视频建立历史 context（用于后续 cheat-seed 给更贴合的选题、更准的 baseline）。触发词：\"初始化\"/\"init\"/\"首次使用\"/\"我是新用户\"/\"setup cheat-on-content\"。**必须在用户第一次会话执行；其他子 skill 在 .cheat-state.json 不存在时自动路由到此。**\nargument-hint: \"[— form: opinion-video|long-essay|short-text|podcast]\"\nallowed-tools: Bash(*), Read, Write, Edit, Glob, WebFetch, Skill\n---\n\n# /cheat-init — 首次 onboarding\n\n让用户从零到能跑第一篇预测，全程 ≤ 5 分钟（没发过历史的）或 ≤ 10 分钟（已发过、要 import 历史的）。\n\n## Overview\n\n```\n[用户首次说\"初始化\"]\n  ↓\n[Phase 0: 检测当前状态]\n  ↓\n[Phase 1: 首屏文案 — 适用性 + 期望管理]\n  ↓\n[Phase 2: 6 个问题（Q1-Q5 都问；Q2 决定是否走 user-history import）]\n  ↓\n[Phase 2.5: 对标账号 — 强烈建议（cold-start 必须问，已发用户可选）]\n  ↓\n[Phase 3: 创建脚手架（含 scripts/ + videos/ + samples/ 空目录 + 模板文件含 benchmark.md）]\n  ↓\n[Phase 3.5: user-history import 流程（仅 Q2=有发过历史 + 用户同意）]\n  ↓\n[Phase 4: 测试 hook 是否生效]\n  ↓\n[Phase 5: 给\"下一步该说什么\"清单]\n```\n\n## Constants\n\n- **DEFAULT_RETRO_WINDOW_DAYS = 3**\n- **INSTALL_HOOKS = ask** — 默认询问；用户选 `auto` 直接装；`skip` 不装\n- **TREND_DEFAULT_SOURCES = [\"manual-paste\"]**\n\n## Inputs\n\n无。所有信息从 6 个对话问题里收集。\n\n## Workflow\n\n### Phase 0: 检测当前状态\n\n1. 读用户当前工作目录（**用户的 content project，不是 cheat-on-content 自己**）\n2. 检查是否已存在 `.cheat-state.json`：\n   - 存在 → 提示\"项目似乎已初始化（state file 存在）。要重新初始化会覆盖现有配置——确认？\" 等用户明确确认才继续\n   - 不存在 → 进入 Phase 1\n3. 检查是否已存在 `rubric_notes.md` / `predictions/` 等核心文件——存在但 state file 不存在 → 是\"半初始化\"状态，提示用户并询问\"要从现有文件推断状态还是重置？\"\n\n### Phase 1: 首屏直白告知期望（含适用性验证）\n\n向用户输出（一字不漏，不要软化）：\n\n```\n🎯 Cheat on Content / 网红外挂 — 初始化\n\n你的下一条内容已经在改写 3 个月后的你。\n规律是客观存在的，区别是你**看见**还是**没看见**。\n这套让你看见。\n\n接下来 5-10 分钟我会问你 5-6 个问题搞清楚你做什么、有什么、怎么用。\n两件事先说在前面：\n\n1. **早期预测会不准**——前 5 篇精度大概 ±50%，这是数学事实。\n   工具用 🔴🟠🟡🟢🔵 标 confidence 等级，不藏数字——\n   你自己判断这次能不能信。\n\n2. **强烈建议导对标账号**——5-10 条对标视频，工具立刻有 anchor。\n   不然第一批预测基本是占星。后面 Q5 会再问一次。\n\n准备好开始吗？\n```\n\n如果用户答\"继续\"或类似肯定回应 → Phase 2。\n不再因为 content_form 拒绝继续——任何形态都允许，只是 `rubric_form_mismatch` 字段标真，cheat-status 后续会持续提示用户\"你的形态需要 bump 调权重\"。\n\n### Phase 2: 6 个问题（一问一答，**不**批量提问）\n\n**Q1: 内容形态**\n\n> \"你的内容更接近哪一种？\n> a) **观点视频**（评论 / 时评 / 论说 / 议题讨论 / 个人观点）— 直接匹配内置 rubric\n> b) **长文 essay**（公众号 / Substack / Medium）— 可借观点视频 rubric 起步，bump 时调权重\n> c) **短文 / thread**（X / 微博 / 即刻）— 同上\n> d) **播客 / 视频长内容**（YouTube 长片 / 播客）— 同上\n> e) **教程 / 工具教学 / Builder**（教别人怎么用 X 工具 / 怎么做 Y 项目）— 同上\n> f) **其他**（游戏 / 美食 / 妆教 / 新闻 / 剧情）— 工作流通用，但 rubric 维度需要调\n>      （ER / SR / HP 这套对你形态可能不太预测，需要自己拆出适合的维度）\n> g) **混合**\"\n\n记录到 `content_form` + `rubric_form_mismatch`。\n\n**Q1 → `content_form` enum 映射**（**必须存 enum 值，不是字母**）：\n\n| 用户答 | `content_form` 写入值 |\n|---|---|\n| a | `\"opinion-video\"` |\n| b | `\"long-essay\"` |\n| c | `\"short-text\"` |\n| d | `\"podcast\"` |\n| e | `\"tutorial-builder\"` |\n| f | `\"other\"` |\n| g | `\"mixed\"` |\n\n`rubric_form_mismatch` 派生：\n- 选 a → `false`\n- 选 b/c/d/e/f/g → `true`，cheat-status 持续提示\"你的形态可能需要 bump 调权重\"\n- **不再有\"严重不匹配\"档**——所有形态都能跑工作流，只是有的 rubric 需要更激进的 bump\n\n**Q1.5: 典型时长**（仅 Q1=a/d/f 时问）\n\n> \"你的视频典型时长？\n> a) 30秒-1分钟  b) 1-3分钟  c) 3-5分钟（推荐起步）\n> d) 5-10分钟   e) 10分钟以上\"\n\n记录到 `typical_duration_seconds`（30 / 90 / 240 / 450 / 900）。\n\n**Q1.6: 发布频率**\n\n> \"你打算多久发一篇？\n> a) 日更   b) 隔日   c) 每周   d) 灵活 / 不固定（关闭 buffer 监控）\"\n\n记录到 `target_publish_cadence_days`（1 / 2 / 7 / null）。\n\n**Q2: 你这个频道发过视频吗？**\n\n> \"a) 没发过 — 我会帮你从兴趣 + 热点 brainstorm 5 个候选 + 写 5 份初稿\n>  b) 发过 — 不管 1 条还是 100 条，我会帮你抓历史让后续 brainstorm 更贴合你做过什么\"\n\n如选 a → state 写 `calibration_samples: 0`，**Phase 3.5 跳过**，直接进入 Phase 4。\n如选 b → 进入 **Q2.1**。\n\n**Q2.1: 平台 + 抓取计划**（仅 Q2=b）\n\n> \"你内容主要在哪个平台？\n> a) 抖音 — 装 douyin-session adapter（Playwright + 扫码登录抖音创作者中心）\n> b) 小红书 — 装 xhs-explore adapter（Playwright + 扫码登录小红书创作者中心）\n> c) YouTube — 装 youtube-data-api adapter（需 API key）\n> d) B 站 — bilibili-stat adapter\n> e) LinkedIn — 装 linkedin-session adapter（Playwright + 登录 LinkedIn，抓单帖分析）\n> f) 微信视频号 — 装 wechat-channels adapter（Playwright + 扫码登录视频号助手，仅抓自己账号后台数据）\n> g) 其他 / 多平台 — 走 manual paste 模式\"\n\n如选 a/b/c/d/e/f → 询问 Q2.2；如选 g → 跳到 Q2.3 manual。\n\n**Q2.2: adapter 安装时机**（仅 Q2.1=a/b/c/d/e/f）\n\n> \"现在装 adapter 自动抓取，还是先手动告诉我？\n> - 现在装 — 引导你装 Playwright + 扫码 → 抓回最近 N 条数据\n> - 等下再装 — 先 manual 模式，state 标 'pending_adapter_setup'，\n>            cheat-status 持续提示装\"\n\n如选\"现在装\"→ 走 adapter install 引导（详见各 adapter README）→ 验证抓取可用 → Q2.3。\n如选\"等下\"→ 跳到 Q2.3 manual。\n\n**Q2.3: 抓取范围 / 历史规模**\n\n如 adapter 已装并验证可用：\n> \"我可以抓你最近多少条作为基础？\n> （建议 10-25 条；样本越多 baseline 越准。最多到你账号实际数量）\"\n→ 用户给数字 N，Phase 3.5 抓取 N 条\n\n如 manual 模式：\n> \"你大概发过多少条？给个范围就行（比如 '5-10 条' / '20+ 条'），\n>  这只用来标 calibration_samples 估值，不用准确。\"\n→ 用户给一个估值，Phase 3.5 跳过抓取，calibration_samples 写估值\n\n**Q3: 数据回收方式**\n\n> \"T+3 天复盘怎么拿数据？\n>\n> a) 手动粘 — 候补方案。**你必须粘 top 20+ 评论（带赞数）**，不是只粘播放数。\n>    评论才是真信号——'她不一样'这种模因爆发只能从评论看出，\n>    播放数永远告诉不了你什么内容真的击中了观众。\n> b) **[推荐默认]** adapter 自动抓 — 评论 + 数据全要。\n>    如果你现在没装 adapter，没关系，state 标 'pending_adapter_setup'，\n>    第一次 publish 之前装上就行（cheat-status 会持续提醒装）。\n>    装的指引在 adapters/perf-data/<platform>/README.md。\"\n\n**Q3 → `data_collection` enum 映射**：\n\n| 用户答 | `data_collection` 写入值 |\n|---|---|\n| a | `\"manual\"` |\n| b（默认） | `\"adapter\"` |\n\n默认推荐 b——除非用户明确说 \"a 我就要手动\"。\n\n**Q4: 候选选题**\n\n> \"你现在有候选选题列表吗？（如有外部 markdown / Notion 维护的）\n> a) 没有（默认）— 一会儿我帮你 brainstorm，或日常用 /cheat-trends 抓\n> b) 有，markdown 列表\n> c) 有，Notion / 其他\"\n\n**Q4 → `pool_status` enum 映射**：\n\n| 用户答 | `pool_status` 写入值 |\n|---|---|\n| a（默认） | `\"none\"` |\n| b | `\"markdown\"` |\n| c | `\"notion\"` |\n\n**Q5: 装几个 hook（默认装，不需要你决定）**\n\n> \"Q5：我顺便装几个 hook，回 'yes' 或 'enter' 就装：\n>\n> 1. **预测锁** — 我们一起做完预测后，文件被锁。你或我都不能改预测段。\n>    复盘只能往同一文件下半段追加，不污染上半段判断。\n>    （没这个锁，事后看到数据想\"修一下当时的预测\"几乎是必然的——你或我都会犯）\n>\n> 2. **SessionStart 自动报告** — 每次开新会话顶部显示 buffer / 待复盘 / 候选 top\n>\n> 3. **静默使用日志** — 异步记录使用频率，不阻塞，给将来诊断用\n>\n> 三个一起装。**不装也可以**（回 'no'）但你失去预测锁，校准价值会下降。\n>\n> 回 yes / no。\"\n\n**Q5 → `hooks_installed` 映射**：\n\n| 用户答 | `hooks_installed` 写入值 |\n|---|---|\n| yes / enter / 默认 | `true`（bool，**不是字符串 `\"yes\"`**） |\n| no | `false` |\n\n默认 yes——除非用户明确说 no。\n\n### Phase 2.5: 对标账号（**所有用户都问**，cold-start 强烈建议）\n\n> 工具早期最重要的信号源是**对标账号**——你 init 完没数据，rubric 等权 v0 等于占星。\n> 但如果你能找一个你想做成那样的账号，导入 5-10 条它的高 / 中 / 低样本，工具就有了 anchor。\n\n询问：\n\n```\n🎯 对标账号\n\n你能找一个对标账号吗？至少 3 条该账号的视频。\n\n  - 你**完全没发过历史**（Q2=a）→ **强烈建议**——rubric 没 anchor 全靠对标。\n    不找的话用通用 v0 等权 rubric，前 5 篇精度更差更久\n  - 你**已发历史**（Q2=b）→ **可选**——你也可以只用自己历史 calibrate；\n    但建议至少导 1 个对标做 sanity check（看你账号是否真的偏离对标方向）\n\na) 现在找 → 立刻进入 /cheat-learn-from（5-15 分钟，看你材料准备程度）\nb) 等下找 → state 标 `benchmark_status: pending`，cheat-status 持续提醒\nc) 不找 → state 标 `benchmark_status: none`，用通用 v0 起步\n\n回 a / b / c。\n```\n\n行为：\n- 选 a → Phase 3 创建脚手架完毕后，**自动 dispatch 到 /cheat-learn-from**（不让用户手动跑——已经在 init 流程里了）。完成后回 init Phase 4\n- 选 b → state 标 `benchmark_status: pending` + `benchmark_name: null`\n- 选 c → state 标 `benchmark_status: none`\n\n记录到 `benchmark_status` / `benchmark_name`（如 a 选则在 cheat-learn-from 里写入）。\n\n### Phase 3: 创建脚手架（逐项解释）\n\n按顺序创建并**解释每一项的作用**：\n\n0. **`.gitignore`（安全 — 必须第一步创建）**\n   ```\n   \"先创建 .gitignore，把账号凭证挡在版本控制外——这是第一件事。\n    .auth/ / .auth-xhs/ / .auth-linkedin/ / .auth-wechat-channels/ 存的是各平台登录态（等同账号密码），\n    .cheat-secrets.json 存 API key / cookie——一旦被 commit 或云同步就等于泄露账号。\n    注意：predictions/ videos/ scripts/ 这些**不**忽略——原则 #1/#3 依赖\n    git history 作为预测的不可变档案，必须入库。\"\n   ```\n   - 复制 `cheat-on-content/templates/gitignore.template` → `<user-repo>/.gitignore`\n   - 如 `<user-repo>/.gitignore` **已存在** → 不覆盖；逐行检查并**追加缺失行**，至少确保\n     `.auth/`、`.auth-xhs/`、`.auth-linkedin/`、`.auth-wechat-channels/`、`.cheat-secrets.json` 五行存在\n   - 即使用户项目当前**还不是 git 仓库**也照常创建——它会在用户 `git init` 的那一刻立即生效\n   - 创建后提醒一句：如果项目已经 `git init` 过且可能误加过 `.auth/`，让用户跑\n     `git rm -r --cached .auth .auth-xhs .auth-linkedin .auth-wechat-channels .cheat-secrets.json` 把已暂存的凭证移出\n\n1. **`.cheat-state.json`**\n   ```\n   \"正在创建 .cheat-state.json — 各子 skill 共享上下文的地方。\n    这次 init 收集的所有答案都会写在这里。\"\n   ```\n   写入（**所有 `<...>` 占位必须查上面 Q 的映射表换成具体 enum 值，绝不直接存字母**）：\n   ```json\n   {\n     \"schema_version\": \"1.4\",\n     \"skill_version\": \"1.0.0\",\n     \"rubric_version\": \"v0\",\n     \"content_form\": \"<查 Q1 映射表，写 enum 字符串如 \\\"opinion-video\\\">\",\n     \"typical_duration_seconds\": <Q1.5 派生：30/90/240/450/900>,\n     \"target_publish_cadence_days\": <Q1.6 派生：1/2/7/null>,\n     \"rubric_form_mismatch\": <Q1=a→false；其他→true>,\n     \"benchmark_status\": \"<Phase 2.5 派生：a→\\\"imported\\\"/b→\\\"pending\\\"/c→\\\"none\\\">\",\n     \"benchmark_name\": <imported 则字符串名，否则 null>,\n     \"benchmark_sample_count\": <imported 则数字，否则 0>,\n     \"baseline_plays\": null,\n     \"calibration_samples\": <Q2=a→0；Q2=b→Phase 3.5 import 回填或 Q2.3 估值>,\n     \"data_collection\": \"<查 Q3 映射表，写 \\\"manual\\\" 或 \\\"adapter\\\">\",\n     \"pool_status\": \"<查 Q4 映射表，写 \\\"none\\\"/\\\"markdown\\\"/\\\"notion\\\">\",\n     \"data_layer\": \"markdown\",\n     \"hooks_installed\": <查 Q5 映射表，写 bool true/false>,\n     \"enabled_trend_sources\": [\"manual-paste\"],\n     \"enabled_perf_adapters\": <Q2.1=a→[\\\"douyin-session\\\"]；b→[\\\"xhs-explore\\\"]；c→[\\\"youtube-data-api\\\"]；d→[\\\"bilibili-stat\\\"]；e→[\\\"linkedin-session\\\"]；f→[\\\"wechat-channels\\\"]；其他→[]>,\n     \"last_bump_at\": null,\n     \"last_bump_self_audited\": false,\n     \"last_published_at\": null,\n     \"last_published_file\": null,\n     \"last_retro_at\": null,\n     \"last_trends_run_at\": null,\n     \"last_trends_added_count\": 0,\n     \"last_prediction_self_scored\": false,\n     \"last_self_scored_at\": null,\n     \"consecutive_directional_errors\": [],\n     \"pending_retros\": [],\n     \"shoots\": [],\n     \"in_progress_session\": null,\n     \"initialized_at\": \"<本地 ISO 8601 含时区，如 \\\"2026-05-05T20:11:13+08:00\\\"，**不要用 UTC 的 Z 后缀**>\"\n   }\n   ```\n\n2. **`rubric_notes.md`**\n   ```\n   \"正在创建 rubric_notes.md — 你的评分维度的真实来源。\n    用的是 v0 占位 rubric——等权 7 维（每个维度同等重要）。\n    \n    为什么叫 v0：v0 是没校准前的占位。你的账号自己的真权重要从你\n    的数据反推，不是预设。跑完 5 篇有数据的内容后，会自动提议\n    升级到「校准 v1」（你的第一个真正校准过的 rubric）。\n\n    ⚠️ rubric_notes.md 是 blind sub-agent (channel B) 的白名单文件——\n    只能含通用语言（公式 / 维度定义 / bucket 边界），不能含真实视频名 / 实绩。\n    每次 bump 升级时的 Memo（含证据数据 + 派生证据）写到 rubric-memo.md（下一步创建）。\"\n   ```\n   - 复制 `cheat-on-content/starter-rubrics/<form>-zero.md`（cold-start）或 `<form>.md`（已有数据时仍可参考）\n\n2.5. **`rubric-memo.md`**（**新**——配合 cheat-score-blind 隔离协议）\n   ```\n   \"正在创建 rubric-memo.md — bump 升级 Memo 累积档案。\n    这是 cheat-bump Phase 5 写入 Memo 全文（含真实视频名 + 实绩 + 派生证据）的位置。\n\n    为什么单独一个文件：blind sub-agent 的白名单是 rubric_notes.md，\n    历史上 bump Memo 写进 rubric_notes.md 会让 blind sub-agent 通过白名单\n    拿到本该看不到的实绩数据——本文件是隔离修复，sub-agent 硬禁读本文件。\n    \n    现在是空的，等第一次 cheat-bump 升级后 append 第一段 Memo。\"\n   ```\n   - 复制 `cheat-on-content/templates/rubric-memo.template.md` → `<user-repo>/rubric-memo.md`\n\n3. **`script_patterns.md`**\n   ```\n   \"正在创建 script_patterns.md — 你的写作 pattern 沉淀（与 rubric 解耦）。\n    rubric_notes.md 教 Claude 怎么打分；\n    script_patterns.md 教 Claude 怎么写。\"\n   ```\n   - 复制 `cheat-on-content/templates/script_patterns.template.md`\n\n4. **四个目录**：`scripts/` + `predictions/` + `videos/` + `samples/`（都加 `.gitkeep`）\n   ```\n   \"正在创建四个目录：\n    \n    scripts/      — 拍前的草稿（cheat-seed 写或你写）\n    predictions/  — immutable 预测日志（hook 保护）\n    videos/       — 拍后的工作目录（cheat-shoot 创建子目录）\n    samples/      — 对标账号视频 / 转录（cheat-learn-from 创建子目录）\n    \n    前三处用同一组 <date>_<id>_<short> 命名相互关联。\n    samples/ 按对标账号名分组：samples/<账号名>/<video-id>/。\"\n   ```\n\n4.5. **`benchmark.md`**（仅 Phase 2.5 选 a/b 时）\n   ```\n   \"正在复制 benchmark.md 占位模板（实际内容由 cheat-learn-from 填）—— \n    这是你的对标账号的中央 reference。\n    前期工具的 rubric / pattern / 选题方向感大量从这里推；\n    后期 N≥10 后影响淡出，但保留作 sanity check。\"\n   ```\n   - 复制 `cheat-on-content/templates/benchmark.template.md` → `<user-repo>/benchmark.md`\n   - **Phase 2.5 选 c 不创建** → benchmark.md 不存在，state 标 `benchmark_status: none`\n\n4.7. **`audience.md`**\n   ```\n   \"正在创建 audience.md — 你账号的受众画像（'谁在看'）。\n    \n    现在是空骨架。它和 rubric_notes.md 平行——rubric 教 Claude 怎么打分，\n    audience 告诉 Claude 你的观众是谁。跑够几篇复盘后跑 /cheat-persona，\n    它会从评论数据聚类出真实画像，cheat-seed 选题写稿时就有了一面镜子。\n    \n    注意：audience.md 由评论派生 → 含实绩信号 → blind 打分 sub-agent 硬禁读它。\"\n   ```\n   - 复制 `cheat-on-content/templates/audience.template.md` → `<user-repo>/audience.md`\n   - 如 Phase 2.5 选了 a/b（有 benchmark）→ Phase 5 清单里提示\"可跑 `/cheat-persona — seed-from-benchmark` 先 seed 一份未验证画像\"\n\n5. **`WORKFLOW.md`** + **`STATUS.md`**\n   - 复制 templates/ 对应文件\n\n6. **如果 Q5=是 → 安装 hooks**\n   - 读 `.claude/settings.json`（如不存在则创建空 `{}`）\n   - merge 进 `hooks/prediction-immutability.json` 的 `hooks.PreToolUse`\n   - merge 进 `hooks/session-start.json` 的 `hooks.SessionStart`\n   - merge 进 `hooks/meta-logging.json` 的 hooks（如同时启用）\n   - 复制 `prediction-immutability.sh` + `session-start.sh` + `log-event.sh` 到 `.cheat-hooks/`，chmod +x\n   - settings.json 里的 command 路径用 `${CLAUDE_PROJECT_DIR}/.cheat-hooks/`\n\n7. **(Pool 选项 c—Notion)** 仅记录到 state file 的 `pool_status: notion`，后续 cheat-trends 调用时再处理\n\n### Phase 3.5: import 流程（仅 Q2=b 且用户同意抓取）\n\n如 Q2.2=现在装 → 走 adapter install + login（详见 [adapters/perf-data/<platform>/README.md](../../adapters/perf-data/)）。\n\n抓取成功后，对每条已发视频：\n\n1. **建 video folder**：`videos/<date>_<id>_<short>/`\n   - `<date>` = 视频实际发布日\n   - `<id>` = 12 位 hash，对 (title + 平台 ID) 做 sha256\n   - `<short>` = 标题前 3-8 字\n2. **写 report.md**：从 adapter 抓回的数据（播放 / 点赞 / 评论 / 转发 / top 评论）填入\n3. **询问用户原稿**：\"video 「{标题}」 你保留了原稿吗？\"\n   - 是 → 用户提供 → 存为 `videos/<id>/script.md`\n   - 否 → 标 `script_lost`（仍建 video folder，只是 script.md 缺失）\n4. **写 reconstructed prediction**：`predictions/<date>_<id>_<short>.md`\n   - header 标 `**Reconstructed retrospective — NOT a blind prediction**`\n   - 7 维打分基于 script + 复盘段实绩数据**反向打**——明确这是非校准用途\n   - 不计入 calibration_samples（这是导入的历史，不是校准积累）\n\nimport 完成后：\n- 派生 `baseline_plays` = 抓回视频的播放中位数 → 写入 state file\n- 派生 confidence 等级 → 后续 cheat-predict 写预测时直接用\n- 输出汇总：\"已 import N 条历史。最近一条 X w 播放，中位数 Y w，已建 N 个 video folder + reconstructed predictions\"\n\n### Phase 4: 测试 hook 是否生效（仅当 Q5=是）\n\n跑一次假的 Edit 拦截测试：\n1. 创建临时文件 `predictions/_test_hook.md`，含 `## 预测\\n[test]\\n## 复盘\\n`\n2. 尝试 Edit 这个文件的 `## 预测` 段\n3. 钩子应 exit 1 阻塞 → 报告\"✅ immutability 钩子生效\"\n4. 删除测试文件\n5. SessionStart hook 验证：直接调一次 `bash .cheat-hooks/session-start.sh` → 应输出报告（即使是空的也行）\n\n如果钩子未生效 → **不要假装成功**，明确告诉用户：\"钩子安装失败，可能是 .claude/settings.json 配置没生效。建议手动检查或重启 Claude Code。\"\n\n### Phase 4.5: 如 Phase 2.5 选 a → dispatch 到 /cheat-learn-from\n\n如果用户在 Phase 2.5 选了 a（现在导对标账号）→ **自动触发 /cheat-learn-from**：\n\n```\n✅ 脚手架 + hooks 装完。\n\n下面立刻进入 /cheat-learn-from 帮你导入对标账号——\n你 init 时选了\"现在找\"，不让你又开一个会话才跑。\n\n[invoke /cheat-learn-from]\n```\n\ncheat-learn-from 完成后回到 init 的 Phase 5。\n\n如 Phase 2.5 选 b/c → 跳过 Phase 4.5，直接 Phase 5。\n\n### Phase 5: 给\"下一步该说什么\"清单\n\n```\n✅ 初始化完成（rubric: v0，calibration_samples: <N>，confidence: <emoji 等级>）\n\n下次你可以直接说这些：\n\n📝 写完一篇稿子 → \"打分这篇 scripts/<...>.md\"\n🎯 准备发布前  → \"启动预测 scripts/<...>.md\"\n🎬 拍完了      → \"拍了 scripts/<...>.md\" → 建 video folder + buffer +1\n🚀 发布后      → \"已发布 https://...\"\n📊 T+3 天      → \"复盘 videos/<...>/\"\n📈 任何时候    → \"状态\"（看完整看板）\n\n<如果 Q4=没有候选选题:>\n🌱 现在跑 /cheat-seed 找选题？\n   - 没发过历史的：纯 brainstorm（兴趣 × 热点）\n   - 发过历史的（已 import）：brainstorm 会基于你过去做过什么给推荐\n   回 \"yes, seed\" 立刻跑，回 \"no\" 你自己想。\n\n💡 你的 confidence 是 <当前等级> —— 它会随着你跑更多复盘自动提升。\n   不要因为 confidence 低就跳过预测——预测的纪律本身就是工具的核心，\n   早期预测的\"价值\"是数据采集，不是决策。第 5 次复盘后 rubric 第一次校准，\n   confidence 会跨入 🟡 偏低；第 10 次后 🟢 中。\n```\n\n## Key Rules\n\n1. **不假装成功**：任何步骤失败 → 明确告诉用户哪一步出错。绝不写\"✅ 初始化完成\"如果实际没完成\n2. **不批量提问**：5 个问题一次问一个\n3. **不静默 mkdir**：每创建一个文件都解释它的作用\n4. **不强推 SQLite**：所有用户给 markdown，提一句\"将来到 30 篇会建议升级\"就够了\n5. **state 字段统一**：删掉 mode / prediction_complexity / bucket_scheme 等枚举字段——单一用 calibration_samples 整数 + confidence 派生\n6. **import 失败不阻塞**：Q2=b 但 adapter 装失败 / 抓取失败 → 优雅降级到\"标 calibration_samples 估值，不导入历史 video folder\"\n\n## Refusals\n\n- 「跳过 Q1-Q5，直接给我创建所有文件」 → 拒绝。问题答案直接影响默认配置（content_form、cadence、hooks）\n- 「我已经在别处初始化过了，把那个项目的配置同步过来」 → 慎重。提示用户手动 cp 现有 `.cheat-state.json` 和 `rubric_notes.md`，不自动跨项目同步\n- 「不装 hook 但保留 immutability 承诺」 → 允许，state 标 `hooks_installed: false`，cheat-status 持续提示\"你的 immutability 是君子协定\"\n\n## Integration\n\n- 写完后，主 SKILL.md 的路由就解锁了所有其他子 skill\n- `cheat-status` 读 `.cheat-state.json` 的 `calibration_samples` 字段决定显示哪个 confidence 等级\n- 如 Q2=b 走了 import → 历史 reconstructed predictions 进 `predictions/` 和 `videos/<...>/`，但**不**计入 calibration_samples（不是真校准样本）\n- `/cheat-seed` 读 `predictions/` 的所有历史 reconstructed prediction → brainstorm 时知道\"用户过去做过什么\"\n\n## State 字段写入清单\n\n| 字段 | 写入时机 | 来源 |\n|---|---|---|\n| `schema_version` | Phase 3 | 硬编码 \"1.4\" |\n| `skill_version` | Phase 3 | 硬编码 \"1.0.0\" |\n| `rubric_version` | Phase 3 | \"v0\" |\n| `content_form` | Phase 3 | Q1 → 查映射表换 enum 值（**不是字母**） |\n| `typical_duration_seconds` | Phase 3 | Q1.5 派生 |\n| `target_publish_cadence_days` | Phase 3 | Q1.6 派生 |\n| `rubric_form_mismatch` | Phase 3 | Q1≠a → true |\n| `benchmark_status` | Phase 3 / 2.5 | Q2.5 答案派生 |\n| `benchmark_name` | Phase 3 / 2.5 | Q2.5 用户提供 |\n| `benchmark_sample_count` | Phase 3 / 2.5 | cheat-learn-from import 后回填 |\n| `baseline_plays` | Phase 3.5（如 import 成功） | import 数据中位数；否则 null |\n| `calibration_samples` | Phase 3 / Phase 3.5 | Q2=a→0；Q2=b→Q2.3 估值或 import 数 |\n| `data_collection` | Phase 3 | Q3 → 查映射表换 enum 值 |\n| `pool_status` | Phase 3 | Q4 → 查映射表换 enum 值 |\n| `enabled_perf_adapters` | Phase 3 | Q2.1 派生（如 Q2=a 则 `[]`） |\n| `hooks_installed` | Phase 3-4 | Q5 → bool（不是字符串） |\n| `last_bump_at` / `last_published_at` / `last_published_file` / `last_retro_at` / `last_trends_run_at` | Phase 3 | 全部 `null` |\n| `last_bump_self_audited` | Phase 3 | `false` |\n| `last_trends_added_count` | Phase 3 | `0` |\n| `last_prediction_self_scored` | Phase 3 | `false` |\n| `last_self_scored_at` | Phase 3 | `null` |\n| `initialized_at` | Phase 3 | now() 本地 ISO 8601，含 `+08:00` 时区，**不要 UTC `Z`** |\n","skills/cheat-learn-from/SKILL.md":"---\nname: cheat-learn-from\ndescription: 从对标账号导入 script + 数据 → 拆 pattern + 派生 base rubric 信号 → 写到 benchmark.md / script_patterns.md / rubric_notes.md。**这是工具最早期信号的来源**——cold-start 用户没自己历史时全靠对标，发过历史的用户也建议至少 1 个对标做 sanity check。触发词：\"学这个账号\"/\"拆这几个对标视频\"/\"learn from\"/\"导入对标账号\"/\"找对标\"。\nargument-hint: <账号名> [— way: a (default) | b] [— append | --replace]\nallowed-tools: Bash(*), Read, Write, Edit, Glob, WebFetch, Skill\n---\n\n# /cheat-learn-from — 对标账号导入\n\n工具早期最重要的信号源是**对标账号**——你 init 完没数据，rubric 等权 v0 等于占星。但如果你能找一个你想做成那样的账号，导入 5-10 条它的高/中/低样本，工具就有了 anchor。\n\n后期当你自己 calibration_samples ≥ 10 时，benchmark 影响自然减弱——你的真实数据成为主要信号源。但 benchmark.md **不删**，仍是 cheat-seed brainstorm 的 reference frame。\n\n## Overview\n\n```\n[用户：学这个账号 / 启动 cheat-learn-from]\n  ↓\n[Phase 0: 检查 benchmark 状态]\n  ↓\n[Phase 1: 选 input 方式（Way a 默认）]\n  ↓\n[Phase 2: 收集材料]\n  Way a: 用户粘 N 条 script 文本 + 数据\n  Way b: 用 whisper 转录 samples/ 目录里的视频\n  ↓\n[Phase 3: 询问每条样本的\"印象判断\"（高/中/低 + 为啥）]\n  ↓\n[Phase 4: Claude 拆 pattern + 派生 rubric 信号]\n  ↓\n[Phase 5: 用户 review → 改 → 落盘]\n  ↓\n[Phase 6: 写 benchmark.md / script_patterns.md / rubric_notes.md]\n  ↓\n[Phase 7: 更新 state.benchmark_status]\n```\n\n## Constants\n\n- **MIN_SAMPLES = 3** — 最少 3 条样本（少于拆不出 pattern）\n- **RECOMMENDED_SAMPLES = 5-10** — 推荐区间，平衡信号量 vs 用户工作量\n- **MAX_SAMPLES_PER_RUN = 15** — 单次导入上限——再多 Claude context 不够 + 用户也累\n- **DEFAULT_WAY = a** — Way a 简单 + 准确，是 default\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<账号名>` | 用户参数；缺失则询问 |\n| `.cheat-state.json` | 状态文件 |\n| Way a: 用户粘的 script 文本 + 数据 | 对话 |\n| Way b: `samples/<账号名>/*.mp4` 等视频文件 | 用户提前下载好放进去 |\n\n## Workflow\n\n### Phase 0: 检查 benchmark 状态\n\n读 `.cheat-state.json` 的 `benchmark_status`：\n\n| 状态 | 处理 |\n|---|---|\n| `none` | 首次导入——继续 Phase 1 |\n| `pending` | 用户之前答应等下找——继续 Phase 1 |\n| `imported` 已有 benchmark | 询问\"你已有 benchmark [当前名]，N 条样本。要做什么？  a) 追加新视频到当前 benchmark  b) 替换为新 benchmark  c) 只看不改\" |\n\n参数解析：\n- `--append` → 追加到现有 benchmark\n- `--replace <new-name>` → 用新 benchmark 替换（旧的归档到 benchmark.archived/）\n- 没标志 + 已有 benchmark → 走上面询问\n\n### Phase 1: 选 input 方式（**两个独立维度**）\n\n每条样本 = **script** + **数据**。两者怎么拿是独立的——你可以混搭。\n\n#### Phase 1a: script source（怎么拿稿子）\n\n```\nscript 怎么拿？\n\na) **粘文本（最简单，推荐）**\n   - 你自己整理过 / 用工具提取过——直接粘进对话\n   - 工具推荐（按方便程度排）：\n   \n     抖音 / 小红书：\n     - 微信小程序「轻抖」—— 粘视频链接 → 自动提取文案 + 评论。最快\n     - 类似工具：\"视频解析助手\" / \"短视频文案提取\" 等小程序\n     - 通常有免费额度，重度使用收费\n     \n     B 站 / YouTube：\n     - 视频页面有\"显示字幕/文字记录\"按钮（如果 UP 主开了）\n     - 第三方：DownSub / SaveSubs / yt-dlp --write-auto-sub\n     \n     公众号 / Substack：\n     - 直接复制网页文字\n   \nb) **whisper 转录视频文件**\n   - 你下载了视频到 samples/<账号名>/<video>/source.mp4\n   - 需要装 whisper-cpp + ffmpeg（见 adapters/script-extraction/whisper/README.md）\n   - 转录可能有错别字 / 漏字 / 标点不准——准确度比 a 差\n\nc) **跳过 script，只用元数据 + 印象**\n   - 你拿不到稿子也懒得用工具\n   - 后果：pattern 拆不深（只能看标题 / 数据 / 你的印象），但 rubric 信号还行\n   - 适合\"先快速搭起来，将来补\"\n\n回 a / b / c。\n```\n\n#### Phase 1b: data source（怎么拿播放/点赞/评论）\n\n```\n数据（播放 / 点赞 / 评论 / 转发）怎么拿？\n\na) **手填数字（最简单）**\n   - 你查一下账号后台或视频页面，告诉我数字\n   - 不需要装任何工具\n\nb) **adapter 自动抓（如已配置）**\n   - 你已经装了 perf-data adapter（如 douyin-session）\n   - 给我视频 URL，工具自己抓数据 + top 评论\n   - 评论数据更全（手填只能告诉我数字，adapter 能拿到具体评论文本）\n\n回 a / b。\n```\n\n**最常见的组合**：\n- 完全零依赖路径：1a + 1b（粘文本 + 手填）—— 5 分钟搞定\n- 评论质量优先：1a + 2b（粘文本 + adapter 抓）—— 装了 adapter 就走这个\n- 拿不到稿子兜底：1b + 1b（whisper + 手填）\n\n### Phase 2: 收集材料\n\n按 Phase 1a + 1b 的组合走对应路径。\n\n**通用纪律**：每条样本最少要有 (script 或 transcript 或 N/A标记) + 数据 (4 项基础：播放/点赞/评论/转发)。\n\n#### Path A: 粘文本（Phase 1a=a）\n\n```\n好。我们一条一条来。最少 3 条，推荐 5-10 条。\n\n第 1 条 script，把整段粘下面（段落版，不要字幕格式）：\n```\n\n收到 script → 算 video_id（sha256(script_content)[:12]）→ 进 Phase 2 数据采集。\n\n#### Path B: whisper 转录（Phase 1a=b）\n\n```\n先确认 whisper 装了：\n\n[运行 `command -v whisper-cpp` 或 `command -v whisper` 检测]\n\n如果没装：\n  ❌ whisper 没装。三选一：\n  - brew install whisper-cpp（推荐——快）\n  - pip install openai-whisper（Python 慢些）\n  - 切回 Path A 自己粘文本（推荐用轻抖等小程序）\n\n装好后：把视频文件放到 samples/<账号名>/<video-name>/source.mp4\n（一个视频一个子目录）\n\n放好后告诉我\"放好了 N 条\"，我转录。\n```\n\n用户放好后：\n1. Glob `samples/<账号名>/*/source.*` 找视频文件\n2. 对每个视频跑 `bash adapters/script-extraction/whisper/run.sh <video> samples/<账号名>/<id>/`\n3. 失败项报告但继续其他\n4. 进 Phase 2 数据采集\n\n#### Path C: 跳过 script（Phase 1a=c）\n\n直接进 Phase 2 数据采集——告诉用户\"没 script 我能拆的 pattern 仅限标题级别 / 你的印象，rubric 信号正常拆\"。\n\n#### Phase 2 数据采集（Phase 1b=a 手填 / b adapter）\n\n**如 Phase 1b=a（手填）**：\n\n```\n第 1 条数据：告诉我\n- 标题\n- 播放量\n- 点赞\n- 评论数（不是评论内容，是数字）\n- 转发 / 分享数\n\n格式随意，能识别就行。例如：\n  \"标题：怎么停止期待\n   播放：71w / 点赞 2.4w / 评论 899 / 转发 1.8w\"\n\n如果你能再粘 top 5-10 条评论（带赞数）更好——pattern 拆能挖到模因层。\n```\n\n**如 Phase 1b=b（adapter）**：\n\n```\n你说装了 adapter（如 douyin-session）。给我每条视频的 URL，\n我跑 adapter 自动抓数据 + top 评论。\n\n第 1 条 URL：\n```\n\n收到 URL → 调对应 adapter → 写数据 + 评论到 samples/<账号名>/<id>/meta.md。\n\n**通用**：继续问第 2 条 / 第 3 条 / ... 用户说\"够了\"或达到 MAX_SAMPLES_PER_RUN 时进 Phase 3。\n\n### Phase 3: 询问\"印象判断\"\n\n对每条样本（不管 Way a 或 b），收完数据后**追加问印象**：\n\n```\n你看完 / 听完这条视频的印象，算这个账号的：\n  a) 高表现样本（代表作 / 你想做成这样的）\n  b) 中表现样本（普通水准 / 不上不下）\n  c) 低表现样本（不算这个账号的代表 / 你不想做成这样）\n\n为什么？（一句话——这个判断比数据更能告诉我你想做什么风格）\n```\n\n记录 (impression_label, impression_reason) 到内存。\n\n> 注意：印象**可以**和数据冲突——比如某条数据高但用户觉得\"不算代表作\"。这种冲突本身是有用信号，记录下来。\n\n### Phase 4: Claude 拆 pattern + 派生 rubric 信号\n\n阅读所有 (script, 数据, 印象) → 自己分析：\n\n#### 4a. Script patterns\n\n按 script_patterns.md 的 cheat sheet 框架拆：\n- 开头钩子：3 种类型分布（场景代入 / IS 戏仿 / 数据反转）\n- 主体结构：几段 / 怎么切\n- 句式 / 句长 / 节奏：短句还是长句、有没有标志性句式\n- emotional 标记 / 双声道\n- 致谢段 / 收尾\n- 高频词汇 / 词汇风格\n\n输出 N 个具体 pattern（每个引用具体样本作证据）。\n\n#### 4b. Rubric 信号（**仅定性，不给数值权重**）\n\n对每条样本打 7 维分（用通用维度），然后看：\n- 高表现样本（按用户印象）共有哪些维度高？\n- 低表现样本共有哪些维度低？\n- 哪些维度在高/低样本之间无差异（说明不是关键维度）？\n\n输出**定性方向**（不是数值权重）：\n- \"ER 看起来重要\"（3/3 高样本 ER ≥4）\n- \"SR 看起来不显著\"（高低样本 SR 分布无差异）\n- \"MS 高的样本评论区有明显模因爆发\"\n\n### Phase 5: 用户 review\n\n一次性展示所有结果给用户：\n\n```\n我从你给的 N 条对标视频拆出：\n\n📝 N 个 script pattern：\n  1. **[Pattern 1 名称]**：[一句话描述] → 证据：[样本 X / Y]\n  2. ...\n  \n🎯 Rubric 定性信号：\n  - 看起来重要的维度：ER / QL / MS（每个有 N 条高样本支持）\n  - 看起来不显著的维度：SR / NA\n  - 给的初始建议：你的对标账号是 [情感+金句驱动型] / [数据驱动型] / [类比讲解型] / ...\n  - **不直接给数值权重**——5-10 样本拟合容易过拟合，先用作 tier-2 信号\n  \n🎨 选题方向感：\n  - 主题分布大概：[主题 A 40% / 主题 B 30% / ...]\n  - 调性：[一句话]\n\n回 \"ok\" 我落盘，\n或指出哪些 pattern / 信号你不认同（\"Pattern X 我觉得不准\" / \"Rubric 信号 Y 错了\"）。\n```\n\n用户反馈循环：\n- \"ok\" → Phase 6 落盘\n- 用户挑刺 → Claude 改 → 重新展示 → 直至确认\n\n### Phase 6: 落盘\n\n#### 6a. benchmark.md\n\n按 [templates/benchmark.template.md] 格式写到 `<user-channel>/benchmark.md`：\n- 账号信息（账号名、URL、调性、粉丝量级——用户提供）\n- 导入的样本表\n- 基础 rubric 派生（仅定性）\n- 选题方向感\n\n如 `--append` → 在现有 benchmark.md 的样本表追加新行 + 重新拆 pattern；不重写整个文件。\n如 `--replace` → 把现有 benchmark.md 移到 `benchmark.archived/<旧账号名>_<日期>.md`，写新的。\n\n#### 6b. samples/<账号名>/\n\n为每条样本建子目录：\n```\nsamples/<账号名>/<video-id>/\n├── source.mp4 (Way b 才有，Way a 没有)\n├── transcript.md (从粘文本写 / whisper 转出来)\n└── meta.md (标题 / 数据 / 印象 / 印象理由)\n```\n\n#### 6c. script_patterns.md\n\n在 `<user-channel>/script_patterns.md` 加新段：\n\n```markdown\n## 对标 [账号名] 借鉴（imported on YYYY-MM-DD，N 条样本）\n\n> 这些 pattern 来自对标账号——**Imported, untested on my channel**。\n> 实拍验证后（≥2 次跑出 + 复盘确认有效）再去掉这个标记，升入正式 pattern。\n\n### Pattern A: [一句话名]\n**来自**: [样本 X]\n**描述**: [详细]\n\n### Pattern B: ...\n```\n\n#### 6d. rubric_notes.md\n\n在 `<user-channel>/rubric_notes.md` 加 / 更新\"benchmark-derived initial signals\"段：\n\n```markdown\n## Benchmark-derived initial signals\n\n> 来自 benchmark.md 的对标账号 [账号名]（N=N 样本，import on YYYY-MM-DD）。\n> **仅定性方向，不直接采纳为数值权重**——5-10 样本拟合容易过拟合。\n> 等你自己 N≥5 校准样本后正式 bump 时**再决定**是否调权重。\n\n- 看起来重要的维度: ER / QL / ...\n- 看起来不显著的维度: SR / NA / ...\n- Claude 给的初始建议: [一句话定性]\n```\n\n### Phase 7: 更新 state file\n\n```json\n{\n  \"benchmark_status\": \"imported\",\n  \"benchmark_name\": \"<账号名>\",\n  \"benchmark_sample_count\": <N>\n}\n```\n\n## Key Rules\n\n1. **Way a 默认**——简单 + 准确。Way b 仅给\"找不到 script 只有视频\"的兜底\n2. **必须问印象**——纯看 transcript 拆 pattern 容易抓表面，加用户印象才挖到深层\n3. **Rubric 信号仅定性**——不直接给数值权重。5-10 样本拟合过拟合\n4. **pattern 默认标 untested**——避免污染用户自己的 pattern 库\n5. **不直接抓视频**——下载是用户的事，避免 TOS + 反爬\n6. **可重复跑**——`--append` 加新视频，`--replace` 换账号\n7. **MIN_SAMPLES = 3**：少于 3 拆不出 pattern，拒绝继续\n\n## Refusals\n\n- 「跳过印象判断，直接拆」 → 拒绝。印象是关键 input\n- 「我只能给 1 条样本」 → 拒绝。最少 3 条\n- 「直接给我数值权重」 → 拒绝。Phase 4 只给定性信号\n- 「能不能不写 transcript 文件，只在内存里拆」 → 不行。transcript 持久化是后续 cheat-retro Phase 4b diff 的依据\n- 「帮我下载对标视频」 → 拒绝。引导用户用 yt-dlp / BBDown 等工具自己下\n\n## Integration\n\n- 上游：`/cheat-init` Phase 2.5 在 cold-start 用户时**强烈建议**跑 `/cheat-learn-from`；calibration 用户**可选**\n- 上游：`/cheat-status` 在 `benchmark_status=pending` + 距 init >24h 时持续提醒\n- 下游：`/cheat-seed` brainstorm 时读 benchmark.md → 知道用户对标方向\n- 下游：`script_patterns.md` 加新段，cheat-seed 写 draft 时按 pattern 选结构\n- 下游：`rubric_notes.md` 加 benchmark-derived signals 段，cheat-bump 时作为参考之一\n- N≥10 提示：`cheat-status` 在用户 calibration_samples ≥10 时提示\"你已有足够自己数据，benchmark 影响淡出（保留作 sanity check）\"\n\n## benchmark 何时淡出\n\n不是死磕样本数，是 **Claude 判断\"用户数据信号是否已超过 benchmark\"**：\n\n- **默认参考**：calibration_samples ≥ 10 → benchmark 影响淡出\n- **可以更早**：N=5 但用户的 (打分, 实绩) 配对里出现 ≥3 条与 benchmark pattern 不一致的——说明你账号已经走出对标的路径\n- **可以更晚**：N=15 但用户的样本都很相似（都做同一类内容），benchmark 仍有信号价值\n\n判断条件 + 默认值都在 cheat-status 触发器 #19 / cheat-seed Phase 0 里实现。\n\n**淡出后**：\n- cheat-seed brainstorm 仍读 benchmark.md，但**优先级低于用户自己的 predictions/**\n- rubric_notes 的 benchmark signals 段标 `**Status: superseded by user data**`，不删但不再主导\n- benchmark.md **不删**——保留作 sanity check（看你账号是否真的偏离对标方向太远）\n\n**任何时候用户主动**：跑 `/cheat-learn-from --replace none` 完全解除 benchmark 影响\n\n## 与其他 skill 的区别\n\n| Skill | 用途 |\n|---|---|\n| `/cheat-learn-from` | **从对标账号**导入 pattern / rubric 信号（一次性 / 偶尔追加） |\n| `/cheat-seed` | brainstorm 选题 + 写 draft（读 benchmark.md 作参考） |\n| `/cheat-trends` | 抓今天的热点（与 benchmark 无关） |\n| `/cheat-bump` | 升级 rubric（用户自己 N≥5 后用真实数据，不直接用 benchmark signals） |\n","skills/cheat-migrate/SKILL.md":"---\nname: cheat-migrate\ndescription: 把老用户的 .cheat-state.json 升级到当前 schema_version。读 migrations/registry.md 算迁移链，按顺序应用每一步迁移文件。幂等：跑两次结果一样。失败停在中间版本不前进。触发词：\"迁移\"/\"升级 state\"/\"migrate\"/\"我的 state 是老版本\"/\"schema 版本不对\"。\nargument-hint: \"[— from: <version>] [— to: <version>] [— dry-run]\"\nallowed-tools: Bash(*), Read, Write, Edit, Skill\n---\n\n# /cheat-migrate — Schema 版本迁移\n\n把用户 `.cheat-state.json` 从旧 `schema_version` 升级到 cheat-on-content 当前期望的 `LATEST_SCHEMA`。\n\n---\n\n## Overview\n\n```\n[用户：迁移 / 或 SessionStart 提示后用户跑]\n  ↓\n[Phase 0: 读 .cheat-state.json + migrations/registry.md → 确定迁移链]\n  ↓\n[Phase 1: dry-run（默认）展示迁移计划，等用户确认]\n  ↓\n[Phase 2: 备份 .cheat-state.json → .cheat-state.json.backup-<timestamp>]\n  ↓\n[Phase 3: 按顺序对每个 step 应用对应迁移文件的 HOW 段]\n  ↓\n[Phase 4: 验证升级后 state 文件能被解析 + schema_version 已更新]\n  ↓\n[Phase 5: 报告 + 提示如有备份需要清理]\n```\n\n---\n\n## Constants\n\n- **REGISTRY_PATH = `${SKILL_DIR}/../../migrations/registry.md`** — 版本链单一来源\n- **MIGRATIONS_DIR = `${SKILL_DIR}/../../migrations/`** — 迁移文件目录\n- **DRY_RUN_BY_DEFAULT = true** — 首次跑展示计划，不直接改文件\n- **BACKUP_BEFORE_WRITE = true** — 写之前必备份；备份文件保留至下次成功 init / 用户手动清理\n- **STOP_ON_STEP_FAILURE = true** — 任何 step 失败 → 停在中间版本，不前进，不回滚\n\n> 💡 调用时覆盖：`/cheat-migrate — dry-run: false` 直接执行 / `/cheat-migrate — to: 1.2` 仅升到指定版本\n\n---\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `.cheat-state.json` | 用户项目根 |\n| `migrations/registry.md` | LATEST_SCHEMA + 版本链表 |\n| `migrations/<from>-to-<to>.md` | 每步具体迁移指令 |\n\n---\n\n## Workflow\n\n### Phase 0: 确定迁移链\n\n1. 读 `.cheat-state.json` → 解析 `current_version = state.schema_version`\n2. 读 `migrations/registry.md` → 解析 `LATEST_SCHEMA` 字段（行：`LATEST_SCHEMA = \"X.Y\"`）\n3. 解析 `args.to` 覆盖（如有）；否则 target = LATEST_SCHEMA\n4. 解析 `args.from` 覆盖（罕见场景：用户的 state 文件 schema 字段坏了，强制指定起点）\n5. **状态判断**：\n   - `current_version == target` → 输出\"✅ state 已是 {target}，无需迁移\" → 退出\n   - `current_version > target`（比如用户跑了 dev 版又切回 release）→ 报错\"无法降级，请手动调整或重新 init\"\n   - `current_version < target` → 继续，从注册表查出迁移链\n6. 从注册表\"版本链\"表算出 `chain = [(from, to, file), ...]`，按顺序串起 current → target\n\n如果某一步在注册表里缺失（比如 `current_version` 不在表里）→ 报错并展示\"目前已知版本：[1.0, 1.1, ...]\"，让用户检查。\n\n### Phase 1: dry-run\n\n输出迁移计划：\n\n```\n📋 迁移计划\n\n当前版本: 1.0\n目标版本: 1.2\n将按顺序跑 2 步：\n\n  [1/2] 1.0 → 1.1（MINOR）\n       新增字段：typical_duration_seconds, target_publish_cadence_days, ...（共 12 字段）\n       删除字段：mode, prediction_complexity, bucket_scheme\n       详见: migrations/1.0-to-1.1.md\n\n  [2/2] 1.1 → 1.2（MINOR）\n       新增字段：[...]\n       详见: migrations/1.1-to-1.2.md\n\n⚠️ 备份位置: .cheat-state.json.backup-<timestamp>\n\n继续吗？回 yes 执行 / no 退出 / dry-run-detail 看每步具体改什么。\n```\n\n如 `args[\"dry-run\"] == false` 或用户回 yes → 进 Phase 2。\n\n### Phase 2: 备份\n\n```bash\ncp .cheat-state.json .cheat-state.json.backup-$(date +%s)\n```\n\n输出：\"📦 备份到 .cheat-state.json.backup-1714838400\"\n\n### Phase 3: 按顺序应用每步\n\n对 chain 里的每个 (from, to, file)：\n\n1. 输出 \"→ [N/M] 应用 {file}...\"\n2. 读 `migrations/<file>` → 找到 `## HOW (Claude steps for /cheat-migrate)` 段\n3. **按段内自然语言步骤逐项执行**——这是关键：迁移是 Claude 读 markdown 跑的，不是 python 脚本\n4. 每步完成后：\n   - 更新内存里的 state.schema_version = to\n   - **原子写**到磁盘（写 .tmp → rename）\n5. 如某步失败：\n   - 输出\"❌ {file} 第 N 步失败：{error}\"\n   - 不前进、不回滚（state 已停在前一步成功的中间版本）\n   - 提示：\"已停在 schema_version: {last_success_version}。修复后重跑 /cheat-migrate 会从这里继续\"\n   - 退出\n\n### Phase 4: 验证\n\n升完后：\n1. 读 `.cheat-state.json` → 解析 → 应能成功\n2. 检查 `schema_version == target`\n3. 检查所有\"必填字段\"非缺失（参照 [shared-references/state-management.md](../../shared-references/state-management.md) 完整 schema）\n4. 失败 → 报错\"迁移完成但验证失败：{detail}。state 文件可能不一致——查看备份恢复\"\n\n### Phase 5: 报告\n\n```\n✅ 迁移完成\n\n  从: 1.0\n  到: 1.2\n  应用步骤: 2\n\nstate 文件现在含 X 字段，全部通过验证。\n\n📦 备份保留：.cheat-state.json.backup-1714838400\n   （确认一切正常后可手动 rm；下次成功 /cheat-init 也会清理过期备份）\n\n下一步建议：\n  - 跑 /cheat-status 确认看板正常\n  - 如有 hooks 重装需求，跑 bash <skill_repo>/install.sh --reinstall-hooks\n```\n\n---\n\n## Key Rules\n\n1. **幂等**：在已升过的 state 上重跑应该立刻退出\"无需迁移\"，**不**重复应用步骤。靠对比 `current_version == target` 实现\n2. **不跳版**：1.0 → 1.3 必须按 1.0→1.1→1.2→1.3 顺序，每步独立可恢复。不允许\"直接升 1.0 → 1.3 的合并 migration\"\n3. **不静默兼容**：state 文件 schema_version 不识别 → 明确报错\"未知版本 X，最近已知版本 Y\"，不假装能继续\n4. **失败停在原地**：第 N 步失败时 schema_version 停在 N-1 已成功的版本，不回滚到迁移前。重跑能从断点继续\n5. **备份是硬约束**：写之前必有备份。即使用户跑 `--dry-run: false`，备份动作仍执行\n6. **不动 predictions / rubric / videos**：只改 `.cheat-state.json`。其他用户数据由各自 skill 负责，迁移 skill 不碰\n7. **MAJOR vs MINOR 透明**：dry-run 输出必标 (MAJOR) / (MINOR)。MAJOR 时额外提示\"老 skill 用旧字段读会出问题，迁移完不能回退到老 skill 版本\"\n\n---\n\n## Refusals\n\n- 「跳过 dry-run，立刻覆盖我的 state」 → **允许**（`--dry-run: false`），但备份仍强制执行\n- 「我的 state 损坏了 / schema_version 字段没了，能不能猜一个版本来跑」 → 允许指定 `--from: 1.0`，但要警告\"基于猜测的迁移可能导致字段错位\"\n- 「降级到旧版本（current > target）」 → 拒绝。schema 演进单向。要降级请手动 cp 历史 git 快照\n- 「合并多步迁移成一个 atomic」 → 拒绝。每步独立可恢复是设计核心\n- 「在跑 cheat-bump / cheat-predict 中途调 migrate」 → 拒绝。等其他 skill 完成再跑，避免 in_progress_session 状态被破坏\n\n---\n\n## Integration\n\n- 上游：SessionStart hook 检测 `state.schema_version != LATEST_SCHEMA` → 输出红色警告 + 建议跑 `/cheat-migrate`\n- 上游（手动）：用户 git pull 拉了新版后，看 CHANGELOG 标 BREAKING → 主动跑\n- 下游：跑完后所有其他 skill 读 state 都能拿到最新字段\n- 与 `cheat-init`：init 写新 state 时直接用 LATEST_SCHEMA，不需要走 migrate\n- 与 `install.sh --reinstall-hooks`：迁移**不**重装 hook 脚本（hook 脚本属于 skill 包代码，不属于用户 state）。这两件事解耦\n\n---\n\n## State 字段读写\n\n本 skill **写**：\n- `schema_version`（每步成功后更新）\n\n本 skill **读**：\n- 所有现有字段（取决于具体 migration 文件的 HOW 步骤）\n\n本 skill **绝不**写：\n- `calibration_samples` / `pending_retros` / `shoots` 等业务状态（这些是其他 skill 的职责）\n- 例外：迁移文件明确说\"派生新字段值时需要扫 predictions/ 算 baseline_plays\"，那是初始化新字段，不是改老字段\n\n---\n\n## Examples\n\n### 示例 1：用户从 v0.1.0 升到 v0.2.0（假设 0.2 引入 schema 1.2）\n\n```\n用户：迁移\nClaude: [跑 cheat-migrate]\n  Phase 0: current=1.1, target=1.2, chain=[(1.1, 1.2)]\n  Phase 1: dry-run 输出计划\n  用户: yes\n  Phase 2: 备份\n  Phase 3: 应用 1.1-to-1.2.md（MINOR：新增 platform_metrics_url 等字段）\n  Phase 4: 验证 OK\n  Phase 5: 报告 ✅\n```\n\n### 示例 2：用户跳了多版\n\n```\n用户：我从 v0.1.0 升到 v0.5.0，state 还是 1.0\nClaude: [跑 cheat-migrate]\n  Phase 0: current=1.0, target=1.4 (LATEST), chain=[(1.0, 1.1), (1.1, 1.2), (1.2, 1.3), (1.3, 1.4)]\n  Phase 1: dry-run 输出 4 步计划\n  ...\n```\n\n### 示例 3：迁移中途失败\n\n```\nPhase 3:\n  → [1/4] 应用 1.0-to-1.1.md ✓\n  → [2/4] 应用 1.1-to-1.2.md ✓\n  → [3/4] 应用 1.2-to-1.3.md... ❌ 失败：用户 baseline_plays 字段含非数字值，无法转 int\n  \nstate 已停在 schema_version: 1.2。\n修复 .cheat-state.json 后重跑 /cheat-migrate 会从 1.2 → 1.3 继续。\n```\n","skills/cheat-persona/SKILL.md":"---\nname: cheat-persona\ndescription: 从复盘评论数据派生 / 刷新账号的受众画像，写入 audience.md。这是和 rubric 平行的第二个派生物——rubric 答\"怎么打分\"，persona 答\"谁在看\"。cheat-seed 选题 / 写稿时读它。**audience.md 含实绩信号，cheat-score-blind 硬禁读**。触发词：\"构造受众画像\"/\"更新 persona\"/\"我的观众是谁\"/\"build persona\"/\"刷新受众画像\"/\"看看我的受众画像\"。\nargument-hint: \"[— seed-from-benchmark] [— rebuild]\"\nallowed-tools: Bash(*), Read, Write, Edit, Glob, Grep\n---\n\n# /cheat-persona — 受众画像派生\n\n从 `predictions/*.md` 复盘段的评论数据，聚类出账号真实受众画像，写入 `audience.md`。\n\n---\n\n## 核心定位\n\npersona 是和 rubric **平行**的第二个派生物，**不是 rubric 的一部分**：\n\n```\n复盘数据（评论 + 完播 + 转粉）\n   ├──→ rubric 进化（cheat-bump）   —— \"怎么打分\"\n   └──→ 受众画像（cheat-persona）    —— \"谁在看\"\n两者都喂给 cheat-seed，但用途不同\n```\n\n- **rubric**：这稿子会不会爆 → 喂 cheat-predict 打分\n- **persona**：谁会因为这条多看 3 秒 / 留评论 / 转发 → 喂 cheat-seed 选题 + 写稿\n\n**绝不混**：persona 不进打分公式。rubric 的 AB 维度（受众广度）可以参考 persona，但那是 cheat-bump 的事，不是这里。\n\n## ⚠️ 污染隔离（不可省）\n\n`audience.md` 从复盘评论派生 = **含已发布作品的实绩信号**。因此：\n\n- `audience.md` 在 [cheat-score-blind](../cheat-score-blind/SKILL.md) 的 hard refusal list 里，refusal_code `blocked_audience`\n- persona 影响 cheat-seed **写什么**（creative direction），不影响 cheat-predict **怎么打分**（blind sub-agent 永远不读 audience.md）\n- 这是干净的：persona 塑造的内容进了成稿，blind sub-agent 照成稿本身打分——没有 leak。leak 只会发生在 sub-agent 能读 audience.md \"因为这受众爱 X 所以加分\" 的情况，而它读不到\n\n## Overview\n\n```\n[用户：构造受众画像 / 更新 persona]\n  ↓\n[Phase 0: 收集数据 — 扫 predictions/*.md 复盘段评论 + benchmark.md]\n  ↓\n[Phase 1: 数据量判定 → 派生 Confidence 等级]\n  ↓\n[Phase 2: 评论聚类 — 自我认同 / 情绪寄存 / 反驳点 / 语言]\n  ↓\n[Phase 3: persona × rubric 交叉检验]\n  ↓\n[Phase 4: 写 audience.md（覆盖式重建，header 记 version + last_rebuilt）]\n  ↓\n[Phase 5: 控制台报告 + 跟上次画像的 diff]\n```\n\n## Constants\n\n- **AUDIENCE_PATH = audience.md** — 受众画像落盘位置\n- **MIN_RETROS_FOR_DATA_GROUNDED = 3** — 复盘数 ≥3 才算\"数据扎实\"（可基于评论质量软判断）\n- **MIN_COMMENTS_PER_TRAIT = 3** — 一条\"验证特征\"至少要 3 条评论证据，否则降到\"假设特征\"\n- **SEED_FROM_BENCHMARK = auto** — 无自己复盘数据但有 benchmark 时，seed 一份未验证画像\n\n> 💡 调用覆盖：`/cheat-persona — seed-from-benchmark`（强制用 benchmark seed）/ `— rebuild`（即使数据没变也重建）\n\n## Inputs\n\n| 来源 | 用途 |\n|---|---|\n| `predictions/*.md` 的 `## 复盘` 段 | **主数据源**——top 评论（带赞数）。persona 的金矿 |\n| `videos/*/report.md` | 完播 / 转粉率——薄信号，推\"留得住 vs 留不住\" |\n| `benchmark.md` | 冷启动 seed——\"看对标的人 ≈ 你想要的人\" |\n| `rubric_notes.md` | Phase 3 交叉检验用——persona 食欲 vs rubric 校准现实 |\n| `audience.md`（如已存在） | 上一版画像，用于 Phase 5 diff |\n\n## Workflow\n\n### Phase 0: 收集数据\n\n1. Glob `predictions/*.md`，对每个文件读 `## 复盘` 段（**只读复盘段——这是 channel A，本来就看实绩**）\n2. 抽取每篇的 top 评论（带赞数）+ 实绩 bucket\n3. 统计：有评论的复盘篇数 `N_retros`、评论总数 `N_comments`\n4. 读 `benchmark.md`（如存在）\n5. 读 `audience.md`（如已存在）→ 留作 Phase 5 diff\n\n### Phase 1: 数据量判定 + Confidence\n\n| 情况 | Confidence | 行为 |\n|---|---|---|\n| `N_retros == 0` 且无 benchmark | 🔴 无数据 | **不强行造**——告诉用户\"persona 需要复盘数据。先跑几篇 cheat-retro，或导 benchmark\"，退出 |\n| `N_retros == 0` 但有 benchmark | 🟠 benchmark-seed 未验证 | seed 一份 aspirational persona，**全文标\"未验证\"** |\n| `N_retros` 1-2 | 🟡 早期信号 | 能产出但特征多落\"假设\"段 |\n| `N_retros` 3-5 | 🟢 数据扎实 | 正常产出 |\n| `N_retros` ≥6 | 🔵 稳健 | 正常产出 + 可做更细的食欲分层 |\n\nConfidence 等级写进 audience.md header。\n\n### Phase 2: 评论聚类\n\n对收集到的所有评论，按四个维度聚类：\n\n1. **自我认同**——\"我也是…\" / \"这就是我\" / \"作为一个…\" 模式。统计哪类身份反复出现（\"大厂打工人\" / \"一人公司\" / \"考研党\" / ...）\n2. **情绪寄存**——观众来评论是为了什么情绪？被验证（\"说得太对了\"）/ 宣泄（\"我也好累\"）/ 抬杠（\"我不同意\"）/ 求助（\"那该怎么办\"）。统计占比\n3. **反驳点**——哪些观点引来稳定的反对声。这是 persona 边界\n4. **语言**——他们怎么说话。玩梗密度、真诚 vs 戏谑、有没有复制你的金句\n\n**聚类纪律**：\n- 一条\"验证特征\"至少 `MIN_COMMENTS_PER_TRAIT`（3）条评论证据。不够 → 降到\"假设特征\"段\n- 每条特征**必须**能引出具体评论 + 出处（哪篇 prediction 的复盘段）+ 条数\n- 发现\"反画像\"信号（你以为的受众 vs 实际评论的人不一样）→ 写\"反画像\"段\n\n### Phase 3: persona × rubric 交叉检验\n\n读 `rubric_notes.md` 当前 rubric + 校准历史。检查：\n\n- persona 说\"受众爱 X 类主题\" → rubric 校准池里 X 类主题真的 over-perform 吗？\n- 不一致 → 在 audience.md 的\"persona × rubric 交叉检验\"段 **flag 出来**\n\n诚实要求：两个派生物矛盾时**不要**强行调和。明确写\"persona 说 A，rubric 校准说 B，待下次复盘澄清\"——矛盾本身是信号。\n\n### Phase 4: 写 audience.md\n\n**覆盖式重建**（不是 append）——persona 是活文档，每次 rebuild 重写全文。但：\n\n- header 的 `Persona 版本` +1（v0 → v1 → v2）\n- header 记 `Last rebuilt` 日期 + `数据基础`（N 篇复盘 / M 条评论）+ `Confidence`\n- 文件底部\"版本历史\"段 **append 一行**：`vN — 基于 M 篇复盘 / K 条评论，主要变化：...`（这是唯一保留的历史；不搞 memo 累积——persona 是活文档不是公式）\n\n用 [templates/audience.template.md](../../templates/audience.template.md) 的结构。\n\n⚠️ **不走版本 memo / 不调跨模型审**——persona 不是高风险不可逆动作（写错了重跑一次就好），过度工程没必要。\n\n### Phase 5: 报告\n\n```\n✅ 受众画像已更新：audience.md（v2，🟢 数据扎实）\n\n数据基础：4 篇复盘 / 87 条评论\n核心画像：25-35 岁职场人，来找情绪共鸣不来找信息……\n\n跟 v1 的主要变化：\n- 新验证：\"深夜刷手机\" 场景共鸣强（v1 是假设，本次 17 条评论验证）\n- 新反画像：原以为\"学生党\"是受众，但评论里学生占比 <5% → 移到反画像\n- ⚠️ 交叉检验 flag：persona 说受众爱\"职场吐槽\"，但 rubric 校准显示职场类 composite 偏低——下次复盘留意\n\n下一步：\n- cheat-seed 选题 / 写稿时会自动参考这份画像\n- 再跑 3 篇复盘后建议再 /cheat-persona 刷新\n```\n\n## Key Rules\n\n1. **数据派生，不手写**——persona 必须来自评论聚类。用户想手动加特征 → 允许，但标 `user-asserted`（未经数据验证）\n2. **证据强制**——验证特征必须带评论条数 + 出处。无证据的进\"假设\"段\n3. **覆盖式重建**——每次 rebuild 重写 audience.md 全文，只在版本历史段 append 一行\n4. **不进打分**——persona 永远不喂 cheat-predict / cheat-score-blind。它是 cheat-seed 的 creative lens\n5. **矛盾不调和**——persona × rubric 冲突时如实 flag，不强行编一个故事\n6. **冷启动诚实**——没数据就说没数据，benchmark seed 全程标\"未验证\"\n\n## Refusals\n\n- 「我觉得我的受众就是 X，你直接写进 audience.md」 → 可以写，但标 `user-asserted` 放\"假设特征\"段，不放\"验证特征\"。persona 的价值在于数据 vs 你的幻想之间的 gap\n- 「persona 也给 cheat-predict 用，让打分更准」 → 拒绝。persona 是实绩派生物，进打分 = 把 channel B 的隔离打穿。persona 只服务 cheat-seed\n- 「跳过评论聚类，你凭感觉给我画一个」 → 拒绝。凭感觉画的是营销话术不是 persona。没评论数据就老实说\"先去复盘\"\n- 「把 persona 写进 rubric_notes.md，省一个文件」 → 拒绝。rubric_notes.md 是 blind 白名单，写 persona（实绩派生）进去 = 实绩泄漏漏洞重演（见 observation-lifecycle.md 的 leak guard）\n\n## Integration\n\n- 上游：`cheat-retro` 每完成一篇复盘 → flag \"已累计 N 篇复盘，可跑 /cheat-persona 刷新画像\"\n- 上游：`cheat-init` 创建空 audience.md 骨架；如导了 benchmark → 提示可 `/cheat-persona — seed-from-benchmark`\n- 下游：`cheat-seed` Mode A/B/C 读 audience.md 作为\"这个 persona 会在乎吗\"的镜子\n- 下游（phase 2 路线）：`cheat-recommend` persona-fit 排序；`cheat-status` persona 新鲜度 nag\n- **隔离**：`cheat-score-blind` 硬禁读 audience.md（refusal_code `blocked_audience`）\n\n## Known limitations\n\n1. **评论 ≠ 全部受众**——留评论的是受众里最活跃的一小撮（沉默大多数不在数据里）。persona 偏向\"会评论的人\"，不是\"所有看的人\"\n2. **平台评论可被污染**——水军 / 引战 / 跑题评论会进数据。cheat-persona 聚类时对明显异常值降权，但不能完全过滤\n3. **persona 滞后于真实受众变化**——画像基于过去 N 篇的评论。受众结构变了，要等新复盘累积才反映\n4. **不解决\"我想要的受众 ≠ 我实际的受众\"**——persona 只如实报告\"现在谁在看\"。想转向另一种受众是选题战略问题，cheat-persona 只提供\"现状 vs 目标\"的 gap，不替你做战略\n","skills/cheat-predict/SKILL.md":"---\nname: cheat-predict\ndescription: 给最终稿写一份 immutable 盲预测日志。这是 cheat-on-content 整个校准循环的核心动作——预测段一旦写完不可改，由 hook 强制。**自动检测**：如目标文件已有 `## 预测` / `## 预测 v1` 段（被 cheat-shoot 调用走 v2 模式），改成 append `## 预测 v2` 而非覆盖。**打分通过 Task tool 委派给 `cheat-score-blind` sub-agent**（context-isolated channel B），主 Claude review 后落盘。触发词：\"启动预测\"/\"start prediction\"/\"给这稿子打分并预测\"/\"写预测日志\"。\nargument-hint: <script-path> [— mode: v1|v2] [— prediction-file: <path>] [— skip-blind]\nallowed-tools: Bash(*), Read, Write, Edit, Glob, Task\n---\n\n# /cheat-predict — AI 主导的盲预测 + 用户 review\n\n**这个工具是\"作弊器\"——AI 帮你做判断**。所以 cheat-predict 的核心是：\n- **Claude 自己**读稿子 + 打 7 维分 + 给 bucket + 概率分布 + 反事实场景\n- 用户 **review** 后回 \"ok\" 接受，或指出哪个维度 / 哪个判断不对\n- 默认走快路径：用户直接 ok → 落盘\n- 慢路径：用户挑刺某个维度 → Claude 改 → 再 review → 直至确认\n\n不是用户从 7 维分到概率分布全部自己写，那 Claude 只剩\"格式化器\"——失去工具的核心价值。\n\n**严格遵守 [shared-references/blind-prediction-protocol.md](../../shared-references/blind-prediction-protocol.md)**——见过任何后续数据就不能写预测，只能记 reconstructed。\n完整组件清单见 [shared-references/prediction-anatomy.md](../../shared-references/prediction-anatomy.md)。\nConfidence 派生表见 [shared-references/state-management.md](../../shared-references/state-management.md)。\n\n## Overview\n\n```\n[用户：启动预测 scripts/<id>.md]\n  ↓\n[Phase 0: blind check 自检]                    ← 触犯就拒绝\n  ↓\n[Phase 0.7: 模式判定 — v1 (新建) 还是 v2 (append)]\n  ↓\n[Phase 1: 读 script + rubric + state + 派生 confidence]\n  ↓\n[Phase 2: **委派 cheat-score-blind sub-agent**（Task tool）拿 9 维盲打 + per-dim confidence]\n  ↓\n[Phase 2.5: 主 Claude 对 blind 输出做 review — 若任意维度 |delta| ≥ 2 vs 主估，弹给用户裁定]\n  ↓\n[Phase 3: **Claude 自己**找锚点对比]\n  ↓\n[Phase 4: **Claude 自己**给 bucket + 概率分布 + 中枢]   ← confidence 低时分布更平\n  ↓\n[Phase 5: **Claude 自己**写反事实场景 + 关键校准假设]\n  ↓\n[Phase 5.5: **用户 review**——展示完整草拟版，等用户 \"ok\" 或挑刺]\n  ↓\n   ├─ \"ok\" → Phase 6 落盘\n   └─ \"X 维度应该 Y 不是 Z\" → Claude 改 → 再 review → 循环\n  ↓\n[Phase 6: 落盘 — v1 写新文件 / v2 append 到现有文件 ## 复盘 之前]\n  ↓\n[Phase 7: 更新 state.in_progress_session]\n```\n\n## Constants\n\n- **SCRIPTS_DIR = scripts/** — 草稿源目录\n- **PREDICTION_DIR = predictions/** — 落盘目录\n- **BLIND_CHECK = strict** — strict（默认）/ lenient（仅警告，不推荐）—— 跟 [blind-prediction-protocol.md](../../shared-references/blind-prediction-protocol.md) \"见过数据\"边界相关\n- **BLIND_SCORING = on**（默认）/ off —— 是否走 [cheat-score-blind](../cheat-score-blind/SKILL.md) sub-agent。off 等价于 `--skip-blind` flag，标 `last_prediction_self_scored: true` 给 cheat-status 警告\n- **DISAGREEMENT_THRESHOLD = 2** —— blind 与主 Claude 自评的单维度差异 |Δ| ≥ 此值 → Phase 2.5 弹用户裁定\n- **BUCKET_PRESET = auto** — 自动派生：有 baseline_plays → 按 baseline × {0.3 / 1 / 3 / 10 / 30}；无 baseline → 平台通用默认\n- **MIN_ANCHORS = 2** — 锚点对比期望 2 个；不够时显式标\"锚点 N/A\"段（不删段，不省略）\n\n> 💡 调用时覆盖：`/cheat-predict scripts/<id>.md — BLIND_CHECK: lenient` / `--skip-blind`（都不推荐）\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<video-folder-path>` 或 `<script-path>` | 用户参数；缺失则询问 |\n| `rubric_notes.md` | 用户项目根 |\n| `.cheat-state.json` | 状态文件 |\n| `predictions/*.md`（可选） | 历史预测，作为锚点 |\n\n### 入参解析（Phase 0.5，在 blind check 之前）\n\n用户给的路径**应该是** `scripts/<date>_<id>_<short>.md`。如不在 scripts/ 下：\n\n| 形态 | 处理 |\n|---|---|\n| `scripts/<date>_<id>_<short>.md` | 标准路径，直接用 |\n| `<id>` 或 `<short>` 简写 | glob `scripts/*_<id>_*.md` 或 `scripts/*<short>*.md` 找匹配 |\n| 任意外部 .md 文件（如 `~/Desktop/my-draft.md`） | **警告 + 询问**：\"建议把稿子放到 scripts/<date>_<id>_<short>.md 让 cheat-on-content 管理。要我帮你 cp 过去并算 id 吗？\"用户同意 → 建标准路径再继续 |\n| `videos/<id>/` 路径（user 误以为视频文件夹存稿子）| 提示\"video folder 是拍后才建的——pre-shoot 草稿在 scripts/。你要 predict 哪份稿子？\" |\n\n如 scripts/<id>.md 不存在 → 报错并询问\"你想 predict 的稿子在哪？\"\n\n## Workflow\n\n### Phase 0: Blind check 自检（**最关键**，触犯立即终止）\n\n按 [blind-prediction-protocol.md](../../shared-references/blind-prediction-protocol.md) 的\"子 skill 必须做的检查清单\"执行：\n\n1. 询问用户该作品当前发布状态：\n   - 未发 → 通过\n   - 已发 < `RETRO_WINDOW_DAYS` 天 → 询问\"你看过任何后续数据吗（播放/点赞/评论）？\"\n     - 用户回答\"没看过\" → 通过，标记 `published_before_prediction: true` + `blind_status: confirmed_no_data_seen`\n     - 用户回答含糊 → 视为\"已看\"，按下一项处理\n   - 已发 ≥ `RETRO_WINDOW_DAYS` 天 → **立即拒绝写\"预测\"**，建议改用 `_redo.md` 路径记 reconstructed retrospective\n\n2. 自检对话历史里是否含播放/阅读/点赞/评论/转发等字眼的实际数字 → 命中则视为已见数据，按上面 strict 模式处理\n\n3. `BLIND_CHECK=lenient` 模式：仅警告 + 强制在文件头标注 `**Reconstructed retrospective — NOT a blind prediction**`，但仍允许继续\n\n通过 → 进入 Phase 0.7。\n\n### Phase 0.7: 模式判定（v1 vs v2）\n\n判定本次是新建预测（v1）还是对既有预测的 v2 追加（拍后改稿场景）。\n\n**显式参数优先**：用户/调用方传 `— mode: v2` + `— prediction-file: <path>` → 直接 v2 模式。\n\n**自动检测**（无显式参数）：\n1. 推断目标 prediction 路径：`predictions/<同 scripts/<id> 命名>.md`\n2. 读该路径：\n   - 不存在 → **v1 模式**，进入 Phase 1\n   - 存在但只有空 `## 复盘`（无任何 `## 预测...` 段）→ **v1 模式**（异常状态，覆盖警告 + 进 Phase 1）\n   - 存在且含 `## 预测` 或 `## 预测 v1` 段 → **v2 模式**\n\n**v2 模式额外动作**：\n- 比较输入 script（最终拍摄稿）与 `## 预测` 段引用的原 `Script Hash`\n- 如完全一致（hash 同）→ 警告\"稿子没改，是否真要写 v2？\"——用户确认才继续；不确认则退出\n- 如不同 → 算 diff 概要（行数 / 字数 / 结构变化）→ Phase 5.5 review 时展示给用户\n- 标记 `prediction_basis = \"post_shoot_pre_publish\"`（v1 默认 `pre_shoot`）\n\n### Phase 1: 读最终稿 + rubric + state + 派生 confidence\n\n1. 按 Phase 0.5 解析后的路径，读 `scripts/<id>.md` 全文\n2. 计算 `script_hash` = sha256(script 内容)[:12] → header 用\n3. 读 `rubric_notes.md`，识别当前公式 + 维度（同 cheat-score Phase 2）\n4. 读 `.cheat-state.json` 拿 `rubric_version`、`content_form`、`calibration_samples`、`typical_duration_seconds`、`baseline_plays`\n5. **从 `calibration_samples` 派生 confidence 等级**（按 [state-management.md confidence 表](../../shared-references/state-management.md)）→ 后续写入 prediction header\n6. 询问用户：\"这是你打算实际拍摄发布的最终稿吗？还是会再改？\"——必须是最终稿\n7. 如果稿子字数与 `typical_duration_seconds` 派生范围严重不符（差 >50%）→ 提示用户：\"这条稿子 N 字，按你设的典型时长（X 分钟）应该是 M-K 字。是临时改了时长，还是稿子需要砍/补？\"\n\n### Phase 2: 委派 cheat-score-blind sub-agent 拿盲打分\n\n**BLIND_SCORING=on**（默认）—— 主 Claude 不再 inline 打分。通过 Task tool spawn `cheat-score-blind`，让一个 context-isolated 的 sub-agent 只看 script + rubric_notes.md 给出 N 维分。\n\n详见 [cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md) 的\"主 Claude 调用契约\"段。**Task prompt 必须精简**：\n\n```\nSpawn cheat-score-blind sub-agent.\n\nInput:\n  script_path: <Phase 0.5 解析出的 scripts/<id>.md>\n  rubric_notes_path: rubric_notes.md\n\nTask: 按 rubric_notes 当前公式给上面 script 打分。返回严格 JSON（见 cheat-score-blind SKILL.md Phase 2 schema）。\n不要读 state file / predictions/ / videos/ 任何其他文件。\n不要询问用户 —— 你没有用户。\n```\n\n**调用前自检**：把 Task prompt 串过 `grep -Ei '播放|阅读|点赞|评论数|实际|retro|复盘|实绩|w$|万$'`——命中 → 改 prompt 重发。\n\n**主 Claude 自己也内心估一份**（不发 sub-agent）——纯为 Phase 2.5 disagreement 检测，**不落盘**、**不替代 sub-agent 输出**。这个估值代表\"如果我没用 sub-agent，我会打多少\"，是 contamination 的客观指标。\n\n**沙盒 escape**：`BLIND_SCORING=off` 或 `--skip-blind` —— 主 Claude 自己打 7 维。state 立刻标 `last_prediction_self_scored: true` + `last_self_scored_at: <ISO>`，cheat-status 持续提示警告。仅用于：\n- Task tool 不可用（开发环境 / 离线）\n- 用户主动 audit 主 Claude 的 inline 判分能力（极少数）\n\n按当前公式算 composite——**用 sub-agent 回传的 dim 分**，不用主 Claude 自估。\n\n### Phase 2.5: Blind 输出 review + disagreement detection\n\n拿到 sub-agent JSON 后，主 Claude 必做的事：\n\n1. **JSON validity check**：`python3 -c \"import json; json.loads(...)\"` 应能解析；不能解析 → 主 Claude 重发 Task（最多 3 次重试），仍败 → abort，向用户报告\n2. **Contamination check**：`self_check.any_contamination_signal == true` → 警告用户\"sub-agent 自报疑似 contamination\"，但仍接受打分（confidence 降一档）\n3. **Refusal check**：`refusal != null` → 按 [cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md) Phase 2 的处理表对应路径\n4. **Disagreement detection**（核心）：\n   - 主 Claude 内心估一份 N 维分（Phase 2 末尾的\"自估\"）\n   - 对每个维度算 `delta = |主估 - blind|`\n   - 任何维度 `delta >= DISAGREEMENT_THRESHOLD`（默认 2） → **弹给用户裁定**\n\n弹裁定 UX：\n\n```\n⚠️  blind sub-agent 跟主 Claude 在某些维度差异较大：\n\n| 维度 | blind (sub) | 主 Claude 自估 | delta | sub-agent 理由 |\n|---|---|---|---|---|\n| ER | 5 | 3 | 2 | \"PPT加油猫猫开头—具象画面强\" |\n| AB | 2 | 4 | 2 | \"一人公司视角，受众窄\" |\n\n谁更准？\n  a) 信 sub-agent（隔离打分，但同 Claude 模型）\n  b) 信主 Claude 自估（有更多对话上下文，可能是 contamination）\n  c) 我自己定（你直接给分）\n\n回 a / b / c <你的分数>\n```\n\n用户选：\n- a → 用 sub-agent 全套分进 Phase 3\n- b → 用主 Claude 自估全套分（视为有意接受 contamination）→ 强制标 `last_prediction_self_scored: true`\n- c → 用户给的分覆盖该维度，其他维度仍走 sub-agent → 记到 `User Override`\n\n**所有 delta** —— 即使全 < THRESHOLD —— 都记录到 prediction header 的 `BlindScore Disagreement` 字段（详见 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 组件 1）。delta=0 也要记录。\n\n### Phase 3: 锚点对比\n\n**所有阶段都跑此 phase**——锚点不够时显式标 N/A，不删段。\n\n1. Glob `predictions/*.md`，读每个文件 header（提取 composite、实绩 bucket、duration_seconds）。**注意排除 reconstructed predictions**（标记 \"Reconstructed\" 的不算锚点）\n2. **优先**找同时长样本（`Target Duration (s)` 与本次差 ±20% 内）\n3. 在同时长（或全部）池里，找 2-4 个 composite 与本次预测 ±0.5 范围内的样本\n4. **如果池子太小**（同时长 < 2 个 + 全部 < 2 个）→ 输出\"锚点对比 N/A 段\"（参考 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 组件 5）—— 仍写这段，告诉读者锚点为何缺\n5. 列对照表；如跨时长，每行额外列\"时长 vs 本次\"列\n6. **关键诊断**：如果某个锚点的 composite 几乎相同但实绩差异 ≥3x → 说明 rubric 没捕获关键维度。**在文件里明确标注**作为新观察的种子\n\n> 为什么按时长筛锚点：4 分钟视频 5w 播放 vs 1 分钟视频 5w 播放完全不是一回事——长视频每秒扛了更多注意力损失。跨时长锚点容易得出虚假结论。\n\n### Phase 4: Bucket + 概率分布 + 中枢\n\n**所有阶段都写**——confidence 低时分布**更平**，不是省略。\n\n1. 从 `starter-rubrics/<content_form>.md` 读默认 bucket 边界（除非用户在 rubric_notes.md 自定义了）\n2. 选择最可能的 bucket（headline call）\n3. **必须**给出所有 bucket 的概率分布——加起来 100%\n4. **必须**给出该 bucket 内的\"中枢\"点估计\n\n**反诚实陷阱**：如果你给一个 bucket 95% 概率，下次预测错了你没法说\"我其实不太确定\"。**真实的概率分布通常在 headline bucket 是 40-65%**，剩下 ≥35% 散布在邻近 buckets。\n\n### Phase 5: 反事实场景 + 关键校准假设\n\n**所有阶段都写**——校准池小时关键校准假设可能没有合适对照样本，那就写\"无可对照样本——仍写下我对这次的核心赌注\"+ 1-2 条这次想测的事。\n\n**反事实场景**（4 段，每段对应一个可能的 bucket，写\"如果落在这里，意味着什么 rubric 假设被验证 / 推翻\"）：参考 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 组件 6。\n\n**关键校准假设**（强烈推荐）：\n- 找一个对照样本（最好是上一篇预测）\n- 明确写\"我押本篇 vs 对照 = X 倍\"\n- 写\"如果反过来 / 差距 < N → 哪个 rubric 假设被推翻\"\n\n如 `REQUIRE_HYPOTHESIS=required` → 缺失则不允许落盘。\n\n### Phase 5.5: 用户 review（**核心 — 决定写什么进文件**）\n\nPhase 2-5 全部在内存里做完后，**一次性展示完整草拟版**给用户：\n\n```\n我的预测草稿（写文件前 review）：\n\n📊 7 维分（v0 / v2 / 等当前 rubric）：\n| 维度 | 分 | 理由 |\n|---|---|---|\n| ER | 5 | \"PPT 加油猫猫 + 老板看到 + 大脑空白\"——情感重 |\n| HP | 5 | 开头\"PPT 第七页大屏中央 一只加油猫猫\"具体反差强 |\n| QL | 5 | \"加油猫猫救了我一命\"双金句 |\n| NA | 4 | 单一时间线 + 反思，清晰但不复杂 |\n| AB | 4 | 一人公司题但 AI 焦虑普适 |\n| SR | 3 | AI 焦虑是议题但不是热点对峙 |\n| SAT | 2 | 共情调性，几乎无讽刺 |\n→ composite ≈ 8.00\n\n🎯 押 bucket：30-100w，中枢 ~60w\n   概率分布: <5w 5% / 5-30w 22% / **30-100w 50%** / 100-150w 18% / >150w 5%\n   confidence: 🟢 中（基于 8 个校准样本，中枢 ±25%）\n\n🔍 锚点对比：\n| 对照 | composite | 实绩 | 异同 |\n|---|---|---|---|\n| ... | ... | ... | ... |\n\n🤔 反事实：\n   如果 >100w → 验证 ER 主导假设强化\n   如果 30-100w → 基准线 ok\n   如果 <30w → 推翻 \"AI 焦虑普适\"，AB 偏乐观\n\n🎲 关键校准假设：本篇 vs [对照] 押 1.5x\n\n——————————————————————————————\n\n回 \"ok\" 我直接落盘，\n或指出哪些维度 / 判断不对（如 \"AB 给 3，太乐观\" / \"中枢应该 30w 不是 60w\"）。\n```\n\n用户三种回应：\n\n1. **\"ok\"** / \"可以\" / \"继续\" → 直接 Phase 6 落盘，header 标 `Scored By: claude`\n2. **\"X 不对，应该 Y\"** → Claude 改对应字段（不光改值，要更新 composite + 概率分布等连锁影响），重新展示 → 循环回 Phase 5.5\n3. **\"全部重做\"** → Phase 2-5 重跑（罕见，通常是 Claude 严重误判稿子调性）\n\n**用户挑刺的字段**记录到 prediction header 的 `User Override` 段（Phase 6 写入）：\n- 哪个维度 / 哪个数字被覆盖\n- AI 原值 vs 用户改后的值\n\n复盘时这个字段帮诊断：\n- 用户每次都 ok（claude 一致）→ 没有用户偏见污染\n- 用户经常覆盖某维度 → Claude 在该维度系统性偏离用户实际感觉\n- 覆盖维度被实绩验证 → 用户直觉准 → rubric 可能漏了什么\n\n**用户挑刺的纪律**：\n- 用户**只能改字段值**，不能在 review 阶段塞新理由让 Claude 重写整段——那是把 Claude 当代笔\n- 改完 composite / 概率分布 / 锚点不一致 → Claude 自动连锁更新（不是用户算）\n\n### Phase 6: 落盘\n\n#### Phase 6a: v1 模式（新建预测文件）\n\n文件名约定（[blind-prediction-protocol.md](../../shared-references/blind-prediction-protocol.md) 的\"文件名约定\"段）：\n```\npredictions/YYYY-MM-DD_<id>_<short-title>.md\n```\n- `YYYY-MM-DD`：今天日期（预测写下的日期）\n- `<id>`：12 位 hash，对稿子全文做 sha256 取前 12 位（稳定 ID，重写不变）\n- `<short-title>`：3-8 字，去标点\n\n**第一段标题写 `## 预测 v1`**（不再写裸 `## 预测`——为将来可能的 v2 留 schema 一致性。老用户的 legacy `## 预测` 文件不动，hook 都识别）。\n\n**header 必填字段**：\n- `Article ID`（与 scripts/<id>.md 同 id）\n- `Script Path`（指向 scripts/<id>.md）\n- `Script Hash`（Phase 1 算出的）\n- `Calibration Samples` + `Confidence`（从 state 派生）\n- `Prediction Basis`：`pre_shoot`（v1 默认）\n- `Scored By`：`claude` / `claude+user_override`\n- **`BlindScored By`**：`subagent-v1`（Phase 2 默认）/ `main-claude-self`（`--skip-blind` 时） / `mixed`（Phase 2.5 用户裁定 b/c）\n- **`BlindScore Disagreement`**：JSON 字段列表，每维度 `{dim, blind, self, delta, decided_as}`，**所有维度必记**（即使 delta=0）\n- `User Override`（如有覆盖）：列出哪些字段被用户改了\n- 其他见 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 组件 1\n\n留一个空的 `## 复盘` 段：\n```markdown\n## 复盘\n\n（待填——T+RETRO_WINDOW_DAYS 天后跑 /cheat-retro <对应 video folder>）\n```\n\n#### Phase 6b: v2 模式（append 到既有文件）\n\n**绝不**用 Write 覆盖文件——会被 immutability hook 拦。用 **Edit** 在 `## 复盘` 之前插入 `## 预测 v2` 段：\n\n```python\n# 伪代码\nedit_old = \"## 复盘\\n\"   # 单独一行，确保 hook awk 识别为 v1 段的边界\nedit_new = \"\"\"## 预测 v2 (replaces v1; basis=post_shoot_pre_publish)\n\n**Diff vs v1**: 改了 N 行（X→Y%），主要变化：[摘要]\n**重判触发**: cheat-shoot 检测稿子改动 ≥30%\n**Script Hash (v2)**: <新稿子 hash>\n\n[7 组件 — 与 v1 同 anatomy]\n\n---\n\n## 复盘\n\"\"\"\n```\n\nv1 段**不动**。v2 段头部明确写\"replaces v1\"——读者一眼知道哪段是有效预测。\n\ncheat-retro 复盘时按\"读最后一个 `## 预测 vN`\"逻辑，自然取到 v2 算偏差。\n\n#### 共用规则\n\n**所有阶段都用统一完整版格式**（参考 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) \"完整结构总览\"）。confidence 低不缩格式，只让 header 标 confidence 等级 + 锚点对比段写\"N/A 解释\" + 概率分布更平。\n\n写文件**前**自检 7 个组件齐全（缺锚点 / 关键校准假设 → 写\"N/A 解释段\"，不删段）。\n\n### Phase 7: 更新 state file\n\n更新 `.cheat-state.json`：\n```json\n{\n  \"in_progress_session\": {\n    \"type\": \"prediction\",\n    \"file\": \"predictions/YYYY-MM-DD_<id>_<short>.md\",\n    \"video_folder\": \"videos/YYYY-MM-DD_<id>_<short>/\",\n    \"started_at\": \"<ISO timestamp>\",\n    \"rubric_version\": \"<v0/v2/...>\"\n  },\n  \"last_prediction_self_scored\": <true 仅当 --skip-blind / Phase 2.5 选 b>,\n  \"last_self_scored_at\": <ISO 当 last_prediction_self_scored=true 时>\n}\n```\n\n`video_folder` 为 null 表示用户跑的是裸 .md 文件，没建 video folder。\n\n`in_progress_session` 在 `cheat-publish` 触发时清除。如果用户预测后从未 publish（弃稿），下次 `/cheat-init` 或 `/cheat-status` 检测到陈旧 in_progress 会询问是否清理。\n\n`last_prediction_self_scored`：\n- `true` 仅当本次预测走了 `--skip-blind` 或 Phase 2.5 用户选了 b（信主 Claude 自估）\n- 一旦 `true` → cheat-status 持续 nag：\"上次预测没走 blind sub-agent，已 N 天\"——直到下次 normal `cheat-predict`（走 sub-agent）触发后才清回 `false`\n- `last_self_scored_at` 跟随更新；下次 `cheat-predict` 走 sub-agent → 这两个字段一起被重置\n\n### Phase 8: 控制台总结\n\n**Cold-start-simple 模式**：\n\n```\n✅ 预测落盘（cold-start 简化版）：predictions/2026-05-04_a3f2c1d4e5b6_停止期待.md\n\n7 维打分：ER5 / HP5 / QL4 / NA3 / AB5 / SR2 / SAT4\n方向押注：比上一篇明显好（ER+HP 双 5）\n对比对象：N/A（这是第 1 篇）\n\n⚠️  ## 预测段已 immutable（hook 锁定）。\n⚠️  这是 cold-start 期简化版——没有 bucket 数字。前 5 篇都这样。\n   第 5 篇复盘后会自动解锁完整预测（bucket / 概率 / 锚点 / 反事实）。\n\n进度：第 N 篇 / 共 5 篇 cold-start 期\n\n下一步：\n- 发布后 → \"已发布 https://...\"\n- T+3 天 → \"复盘 predictions/2026-05-04_...\"\n```\n\n**Complete 模式**：\n\n```\n✅ 预测落盘：predictions/2026-05-04_a3f2c1d4e5b6_停止期待.md\n\nbucket 押注：30-100w（中枢 50w）\n关键校准假设：本篇 vs 谁问你了 = 1.5-2x\n\n⚠️  ## 预测 段已 immutable（hook 锁定）。\n⚠️  你不能再向我\"透露\"这条作品的播放数据，否则下次复盘的盲度声明失效。\n   如果你不小心看到了，告诉我——我会在文件里补一个 integrity warning。\n\n下一步：\n- 发布后 → \"已发布 https://...\"\n- T+3 天 → \"复盘 predictions/2026-05-04_...\"\n```\n\n## Key Rules\n\n1. **blind check 是硬门槛**。BLIND_CHECK=strict 模式下，触犯即终止，不允许\"软处理\"。lenient 仅用于演练\n2. **整数维度分**。同 cheat-score\n3. **概率分布 = 100%**。不允许 95% + 8%；要么诚实给 50% + 30% + 15% + 5%，要么承认不知道\n4. **必须有 `## 复盘` 占位空段**——否则 hook 不知道哪里是 immutable 边界\n5. **不允许\"先写文件再讨论分数\"**——文件落盘后预测段就锁了；讨论必须发生在 Phase 2 之后、Phase 6 之前\n6. **id 是稿子 hash，不是时间戳**——重写 _redo.md 时 id 不变，便于跨文件追溯\n\n## Refusals\n\n- 「我已经看过播放数据了，但你假装没看到给我做个预测」 → 拒绝。BLIND_CHECK=strict 直接终止\n- 「我把预测段先写一版，等数据出来再调」 → 拒绝。这是把 immutable 协议反着用\n- 「我改稿了想让你覆盖之前的预测，不要 v2 段」 → 拒绝。v1 是档案，v2 才是当前判断——append 不覆盖。即使你\"主观感觉 v1 完全错了\"，git history 里 v1 还能查，但工作目录里 v1 必须留\n- 「跳过反事实场景，太麻烦」 → 拒绝。反事实是复盘诊断的依据，缺它复盘退化为\"准 / 不准\"\n- 「可不可以只写 bucket，不写概率分布」 → 拒绝。概率分布是逼你诚实的工具\n- 「这次先用 lenient 模式，下次再 strict」 → 询问原因。如果是测试 / 演练 → 允许且文件明确标 reconstructed；如果是想偷懒 → 拒绝\n- 「sub-agent 太慢，你直接打就行」 → 用 `--skip-blind` flag 显式声明。**不接受**主 Claude 自作主张跳过 sub-agent。flag 触发 state.last_prediction_self_scored=true，cheat-status 持续提示直到下次 normal 调用清除\n- 「Phase 2.5 选 b 后我不想标 last_prediction_self_scored=true」 → 拒绝。选 b 等于\"我有意接受主 Claude 自评\"——必须留下污染追踪轨迹\n- 「我是 cold-start 但想跑完整版预测，给我 bucket 数字」 → 拒绝。前 5 篇 bucket 数字是 false precision，给反而误导。等第 5 篇复盘后 cheat-status 会主动提示解锁。如果用户确实想要数字（罕见，自我教育目的）→ 允许但在文件头醒目标 `**Numerical predictions in cold-start are NOT predictive — for self-education only**`\n\n## Integration\n\n- 前置：`/cheat-init` 必须完成 + `rubric_notes.md` 存在\n- 上游可选：`/cheat-score` 反复尝试不同稿子版本\n- 下游：`/cheat-publish`（发布登记）→ `/cheat-retro`（复盘）→ 累计 ≥ MIN_SAMPLES 后 `/cheat-bump`\n- hook 依赖：`hooks/prediction-immutability.sh` 必须已安装在用户 `.claude/settings.json`，否则 immutability 仅靠 SKILL.md 自律——`cheat-status` 会持续提示\n","skills/cheat-publish/SKILL.md":"---\nname: cheat-publish\ndescription: 登记一篇内容已发布，把 URL/平台 ID/发布时间写入对应预测文件 header 和 state file。这是一个轻量动作——只更新元数据，**不动预测段任何字符**。触发词：\"已发布\"/\"I shipped\"/\"发布链接是 X\"/\"刚发完 [url]\"/\"publish registered\"。\nargument-hint: <prediction-file-or-url> [— platform: youtube|bilibili|douyin|...]\nallowed-tools: Bash(*), Read, Edit, Glob\n---\n\n# /cheat-publish — 发布登记\n\n把作品的发布元数据（URL、发布时间、平台）补到预测文件 header 与 state file。**禁止改预测段**——hook 会拦。\n\n## Overview\n\n```\n[用户：已发布 https://...]\n  ↓\n[Phase 0: 找到对应的预测文件]   ← 通过 in_progress_session 或匹配\n  ↓\n[Phase 1: 解析 URL → 平台/发布时间]\n  ↓\n[Phase 2: 更新 prediction 文件 header（仅 metadata 段）]\n  ↓\n[Phase 3: 更新 .cheat-state.json，清除 in_progress_session]\n```\n\n## Constants\n\n- **AUTO_DETECT_PLATFORM = true** — 从 URL 模式自动识别平台\n- **VERIFY_BLIND = true** — 提醒用户：从此刻起看到任何后续数据都会破坏盲度声明的诚信\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<prediction-file>` 或 URL | 用户参数；缺失则用 `.cheat-state.json` 的 `in_progress_session.file` |\n| `.cheat-state.json` | 用户项目根 |\n\n## Workflow\n\n### Phase 0: 找到对应的预测文件\n\n按优先级：\n1. 用户参数明确给了 prediction 文件路径 → 用它\n2. 用户参数只给了 URL → 读 `.cheat-state.json` 的 `in_progress_session.file`\n3. 都没有 → 列出 `predictions/*.md` 中 header 没填 `published_at` 的文件，让用户选\n\n**警告路径**：若 `in_progress_session.file` 与用户给的 URL 时间差超过 14 天 → 提示\"这个预测写于很久之前，确认是这篇？\"\n\n### Phase 1: 解析平台\n\n`AUTO_DETECT_PLATFORM=true` 时按 URL 模式：\n\n| URL 模式 | 平台 |\n|---|---|\n| `youtube.com/*` `youtu.be/*` | youtube |\n| `bilibili.com/*` `b23.tv/*` | bilibili |\n| `douyin.com/*` `iesdouyin.com/*` `v.douyin.com/*` | douyin |\n| `xiaohongshu.com/*` `xhslink.com/*` | xhs |\n| `mp.weixin.qq.com/*` | wechat |\n| `substack.com/*` `*.substack.com/*` | substack |\n| `medium.com/*` `*.medium.com/*` | medium |\n| `twitter.com/*` `x.com/*` | twitter |\n| 其他 | unknown — 询问用户 |\n\n发布时间获取：\n- 不强求自动抓——绝大多数平台需要登录态\n- 询问用户：\"发布时间是？（默认：现在）\" → 接受 ISO 8601 或自然语言（\"今天 14:30\" / \"20 分钟前\"）\n- 解析失败 → 用 now()\n\n### Phase 2: 更新 prediction 文件 header\n\n**绝不**触碰 `## 预测` 段及之后。只动文件最顶部的 metadata 块。\n\n读文件，定位到 metadata 块（在第一个 `##` 之前的所有行）。检查是否已有这些字段——有则警告\"已登记过\"并询问是否覆盖；无则追加：\n\n```markdown\n**Published at**: 2026-05-04T14:32:00+08:00\n**Platform**: douyin\n**URL**: https://v.douyin.com/abc123\n**Video Folder**: videos/2026-05-04_a3f2c1d4_停止期待/\n**Aweme ID**: 7234567890123456789  (douyin / 视频号 等需要的 platform-specific ID)\n```\n\n**关于 platform-specific ID**：\n- 抖音：从 URL 短链 resolve 后提取 `aweme_id`（v.douyin.com → 重定向后含 modal_id 或 item_id 参数）\n- B 站：BV 号\n- 小红书：note_id\n- YouTube：v= 参数后的 video_id\n\n如果用户给的是分享短链（无法立刻 resolve）→ 标 `Aweme ID: pending`，下次 `/cheat-retro` 时由 adapter 解析。\n\n**video folder 处理**：到 cheat-publish 这一步，对应的 `videos/<id>/` 目录**应该已经由 cheat-shoot 创建**（含 script.md）。\n\n- 如 video folder 不存在 → 警告\"你跳过了 cheat-shoot？建议先跑 cheat-shoot 把拍摄稿登记进 video folder 再发\"，**询问用户是否跳过登记直接发**：\n  - 是 → 自动建一个 video folder（fallback），但不询问稿子一致性，标 `ad_hoc_publish: true`\n  - 否 → 让用户先跑 cheat-shoot 再回来 publish\n\n用 Edit 工具（不是 Write 重写整个文件）。\n\n**hook 行为预期**：因为只动 metadata 段（在 `## 预测` 之前），immutability hook 应放行。如果 hook 误拦 → 报告 bug，**不要绕过 hook**。\n\n### Phase 3: 更新 state file\n\n```json\n{\n  \"in_progress_session\": null,\n  \"last_published_at\": \"<ISO timestamp>\",\n  \"last_published_file\": \"predictions/<filename>\",\n  \"last_published_video_folder\": \"videos/<...>/\",\n  \"last_published_platform_id\": \"<aweme_id 或 BV 号 等>\",\n  \"pending_retros\": [\n    \"predictions/<filename>\"\n  ],\n  \"shoots\": [\n    // 移除 video_folder 匹配本次发布的项；buffer -1\n  ]\n}\n```\n\n**`shoots` 队列处理**（buffer 跟踪关键）：\n1. 读 state.shoots[]\n2. 找 `video_folder == 本次发布的 video_folder` 的项 → 移除\n3. 如果没找到 → 警告\"buffer 队列里没有这条视频。是直接发布没经过 /cheat-shoot 吗？\"——不阻塞，但提示用户下次走 /cheat-shoot 让 buffer 跟踪准确\n\n`last_published_platform_id` 是 cheat-retro 调 adapter 时的输入——如 douyin-session 需要 aweme_id 直接抓数据。\n\n`pending_retros` 是待复盘列表——`cheat-status` 会基于这个列表 + RETRO_WINDOW_DAYS 显示\"今天该复盘哪些\"。\n\n### Phase 4: 提醒 + 下一步 + buffer 状态\n\n```\n✅ 登记完成：predictions/2026-05-04_a3f2c1d4e5b6_停止期待.md\n   - Published at: 2026-05-04 14:32\n   - Platform: douyin\n   - URL: https://v.douyin.com/abc123\n\n📦 Buffer：N 篇（颜色 + 含义）\n   按你的 cadence（X）= N×X 天 buffer\n   [如颜色变了，提示\"现在该去拍/暂停拍\"]\n\n⚠️  从此刻起，你看到任何关于这条作品的播放/点赞/评论数据\n    都会破坏盲度声明的诚信。如果不小心看到，告诉我——\n    我会在文件里追加一个 integrity warning。\n\n📅 计划复盘：T+3d，约 2026-05-07\n   到时间说：\"复盘 predictions/2026-05-04_...\"\n```\n\nBuffer 颜色由 [shared-references/cadence-protocol.md](../../shared-references/cadence-protocol.md) 派生。如本次发布让 buffer 跌入红色（断更风险）→ 高亮警告\"今天必须再拍 ≥1 条\"。\n\n## Key Rules\n\n1. **不动预测段**。即使是修复笔误，也不允许在 publish 时改预测段\n2. **不抓数据**。publish 是登记动作，不是数据回收（那是 cheat-retro 的活）\n3. **state 字段名固定**。`pending_retros` / `last_published_at` 是其他子 skill（特别是 cheat-status / cheat-retro）依赖的契约\n4. **平台未知不强报**。无法识别 → 询问用户，允许 `platform: other` 作为兜底\n5. **重复登记需明示**。已有 published_at → 询问\"覆盖？\"，绝不静默覆盖\n\n## Refusals\n\n- 「我顺手把预测段也改一下」 → 拒绝。请走 `_redo.md` 路径\n- 「URL 我等会儿补，先把发布时间记上」 → 允许：URL 字段可后续追加；published_at + platform 必填\n- 「跳过 metadata 更新，直接清 in_progress_session」 → 拒绝。元数据是复盘时的关键上下文（特别是 platform 决定数据回收用哪个 adapter）\n\n## Integration\n\n- 上游：`/cheat-predict`（写出 prediction 文件并设 in_progress_session）\n- 下游：T+RETRO_WINDOW_DAYS 后 → `/cheat-retro`\n- `cheat-status` 用 `pending_retros` 字段计算\"今天该复盘哪些\"\n- 平台字段被 `cheat-retro` 用来路由到对应的 perf-data adapter（manual-paste / youtube-data-api / 等）\n","skills/cheat-recommend/SKILL.md":"---\nname: cheat-recommend\ndescription: 从 candidates.md 里按当前 rubric 排序推荐 top N 选题，每条带 composite + 一句 rationale + 锚点对比。**candidates 不存在时给引导而非报错**。触发词：\"推荐选题\"/\"next topic\"/\"下一篇做什么\"/\"recommend topics\"/\"挑一个选题\"。\nargument-hint: \"[— top: N] [— filter: tier1|all|safe|risky]\"\nallowed-tools: Read, Glob, Grep\n---\n\n# /cheat-recommend — 候选池排序推荐\n\n读 candidates.md → 按 composite 排序 → 输出 top N 推荐，每条带评分细节 + 锚点对比 + 推荐理由。\n\n## Overview\n\n```\n[用户：推荐选题]\n  ↓\n[Phase 0: 检查 candidates.md 存在性]   ← 不存在则引导，不报错\n  ↓\n[Phase 1: 解析 candidates 列表]\n  ↓\n[Phase 2: 过滤（tier / 安全性 / 已发过）]\n  ↓\n[Phase 3: 排序 by composite + 找锚点]\n  ↓\n[Phase 4: 输出 top N + 每条的 rationale + 锚点对比]\n```\n\n## Constants\n\n- **TOP_N = 5** — 默认推荐 top 5\n- **STRATEGY = stable+experimental** — 推 ≥2 时按 [cadence-protocol.md](../../shared-references/cadence-protocol.md) 的\"1 稳分 + 1 实验性\"策略；推 1 时只推 top 稳分\n- **POOL_PATH = candidates.md** — 候选池路径\n- **EXCLUDE_PUBLISHED = true** — 排除已发布的（与 `predictions/*.md` 去重）\n- **EXCLUDE_REJECTED = true** — 排除用户主动跳过的（`tier=skip`）\n- **REQUIRE_SCORED = true** — 只推荐已打分的——避免推没读过的素材\n- **DUPLICATE_CATEGORY_LOOKBACK** — 派生自 `state.target_publish_cadence_days`：max(3, cadence_days × 3) 天内已发同类目候选不推（避免审美疲劳）\n\n> 💡 调用时覆盖：`/cheat-recommend — top: 3 — filter: safe`\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `candidates.md` | 用户项目根 |\n| `predictions/*.md` | 用于去重 |\n| `.cheat-state.json` | 当前 rubric_version |\n\n## Workflow\n\n### Phase 0: 候选池存在性检查\n\n读 `candidates.md`：\n\n| 状态 | 处理 |\n|---|---|\n| 文件不存在 | **不报错**。输出引导：见下方\"无候选池引导\" |\n| 文件存在但空（< 1 个 entry） | 同上 |\n| 文件存在且非空 | 进入 Phase 1 |\n\n**无候选池引导**（核心：不让用户第一次遇到 cheat-recommend 时被劝退）：\n\n```\n你目前没有候选池（candidates.md 不存在或为空）。\n\n绝大部分人没有候选池——这很正常。四个建立方式，挑一个：\n\n1. 🌱 [推荐] 跑 /cheat-seed\n   一次性的种子动作：3 个问题（兴趣 / 调性 / 红线）→ 拉公开热点 + Claude brainstorm\n   → 输出 15 候选让你挑 5 → 默认顺带写 5 个 draft。5 分钟搞定。\n   \n   - 没发过历史的：纯 brainstorm（兴趣 × 热点）\n   - 发过历史的（init 时已 import）：brainstorm 会基于\"你过去做过什么\"给推荐\n   \n   说：\"找选题\" 或 \"seed\"\n\n2. 🔥 [日常补充] 用 /cheat-trends 抓 20 条带打分的候选\n   说：\"抓热点\" — 从 weibo-hot / zhihu-hot / b站热门 / HN / 你配的源各拉 N 条\n   适合已经跑过 /cheat-seed、想日常补充候选池的用户\n\n3. ✍️  手动建：把候选标题贴进 candidates.md，每行一条\n   我会自动给每条粗打分\n\n4. 📋 从 Notion / RSS 导入：跑 /cheat-init --mode add-pool 配置 adapter\n\n你也可以跳过候选池，直接给我具体稿子说\"启动预测\"。\n\n> /cheat-seed vs /cheat-trends 的区别：\n> - seed 是种子动作（含 brainstorm + 可选 draft），适合\"我从零开始没选题\"\n> - trends 是日常多 adapter 抓取（不 brainstorm 不写 draft），适合\"日常补充候选池\"\n```\n\n完成引导 → 退出，不继续后续 phase。\n\n### Phase 1: 解析 candidates\n\n按 [candidate-schema.md](../../shared-references/candidate-schema.md) 的\"Markdown 表示\"格式解析每个 H3 entry：\n\n```markdown\n### [tier1] 标题\n- **id**: a3f2c1d4e5b6\n- **composite (v2)**: 8.47 — ER=4 HP=4 QL=5 NA=3 AB=5 SR=3 SAT=3\n- **predicted bucket**: 5-30w\n...\n```\n\n提取每条的 `id` / `title` / `tier` / `composite` / `dimension_scores` / `note`。\n\n容错：`candidates.md` 格式被用户手改过 → 询问用户 schema，**不要静默忽略不识别的 entry**。\n\n### Phase 2: 过滤\n\n```\n1. EXCLUDE_PUBLISHED=true → 扫 predictions/*.md 的 header，提取所有 id；从候选池过滤掉\n2. EXCLUDE_REJECTED=true → 过滤 tier=skip\n3. REQUIRE_SCORED=true → 过滤 composite=null（未打分的不推荐）\n4. filter 参数：\n   - tier1: 只保留 tier=tier1\n   - all: 不过滤（tier1+2+3）\n   - safe: 排除 tier=risky\n   - risky: 仅显示 tier=risky（用于\"我今天就想发风险议题\"）\n```\n\n### Phase 2.5: Buffer 颜色覆盖（**最高优先级**）\n\n读 `state.shoots` + `state.target_publish_cadence_days` 算 buffer 颜色（[cadence-protocol.md](../../shared-references/cadence-protocol.md)）：\n\n| Buffer 颜色 | 推荐策略覆盖 |\n|---|---|\n| 🔴 红 | **只推 top 1 稳分**——不推实验性。回：\"buffer 已 0/1 篇，下个发布日断更风险高，今天必须拍 ≥1 条稳分。下面是 top 1 稳分（不推实验性）\" |\n| 🟠 橙 | 标准 1 稳 + 1 实验，但提示\"建议优先拍稳分\" |\n| 🟢 绿 | 标准 1+1（默认） |\n| 🔵 蓝 | **拒绝推荐**。回：\"你 buffer 已 N 条，cadence-protocol 规定积压时暂停拍摄。先发存货 + 复盘。手动覆盖请说 '我就要拍'\" |\n| 灵活模式 (`target_publish_cadence_days=null`) | 不应用 buffer 覆盖，标准策略 |\n\n### Phase 3: 排序 + 选 1 稳 + 1 实验（按 STRATEGY）\n\n#### 第 1 条（稳分）\n\n1. 按 `composite` 降序排\n2. 过滤掉 `tier=risky`（稳分要安全议题）\n3. 过滤掉 `category` 与最近 `DUPLICATE_CATEGORY_LOOKBACK` 天已发/已推过的重复（避免审美疲劳）\n4. 取 top 1\n\n#### 第 2 条（实验性）\n\n1. 在 candidates.md 中找：\n   - 维度组合与最近已发样本**差异最大**（增加校准信息量），或\n   - 含明确的 pattern/dimension hypothesis（如 \"MS=5 的 A/B 对照\"），或\n   - tier=risky 但用户主动愿意试（用 `--filter risky` 覆盖）\n2. composite 不一定 top——但有\"信息价值\"\n3. 如 candidates 池里没有合适的实验性候选 → 回：\"候选池里没有明显的实验性样本，给你 2 条稳分\"\n\n#### 剩余 (TOP_N - 2) 条\n\n按 composite 降序补满，标 \"（备选）\"。\n\n#### 锚点\n\n对每条找 1-2 个 composite 接近的**已发布**作品作为锚点（从 `predictions/*.md` 读）。优先**同时长**锚点（按 `state.typical_duration_seconds` ±20%）。\n\n### Phase 4: 输出\n\n```\n🎯 候选池推荐（rubric: v2 / buffer: 🟢 绿 / cadence: 隔日更）\n\n📌 第 1 条 — **稳分**（推荐立即拍）：\n  **[tier1] [👍 9.18] \"为你好\"高密体系**\n   - 维度：ER=5 HP=5 QL=4 NA=4 AB=5 SR=5 SAT=4\n   - 粗预测桶：30-100w（中枢 ~60w）\n   - rationale：ER+SR 双 5 顶配，\"高密度家庭议题\"普适且分享安全\n   - 锚点：仓鼠 (composite 9.41, 实绩 124w) — 同走\"理论框架+具象样本\"路线\n   - 风险：议题厚重，不适合连续 2 篇都打这种\n\n🧪 第 2 条 — **实验性**（验证特定假设）：\n  **[tier1] [👍 8.71] 哈哈长度**\n   - 维度：ER=3 HP=5 QL=5 NA=4 AB=5 SR=4 SAT=5\n   - 粗预测桶：30-100w（中枢 ~55w）\n   - **测试目标**：v2.1 候选维度 MS+TS 双 5 vs 谁问你了同 ER/HP/QL/SR 但 MS+TS 低 3\n   - 信息价值：拍这条能强证据/弱推翻 v2.1 升正\n   - 锚点：谁问你了 (composite 8.24, 实绩 11.7w)\n\n（备选 top 3）：\n  3. ……\n  4. ……\n  5. ……\n\n下一步：\n- 选稳分 + 实验性各拍 1 条 → 改写 script → \"启动预测\"\n- 只拍 1 条 → 选稳分（buffer 颜色越红，越应该选稳分）\n- 想抓更多候选 → 说\"抓热点\"\n- 都不满意 → 说\"过滤改 all\"看其他 tier 或 \"regen\"\n```\n\n如 buffer 颜色为 🔴：\n```\n🔴 buffer 警戒：你 buffer 已 0/1 篇，**下个发布日可能断更**。\n   按节奏协议，只推 top 1 稳分（不推实验性）：\n\n  **[tier1] [👍 9.18] \"为你好\"高密体系**\n   - ...（同上稳分格式）\n\n今天必须拍这条。挑 5 条候选 → \"抓热点\"。\n```\n\n如 buffer 颜色为 🔵：\n```\n🔵 buffer 积压：你 buffer 已 N 条，**暂停推荐**。\n   按节奏协议，先发存货 + 复盘。\n   - 已拍未发：N 条（最早一条 X 天前拍的）\n   - 待复盘：N 条\n   说 \"已发布 ...\" 出队，或 \"复盘\" 处理待复盘项。\n   如果你坚持要拍新的，回 \"我就要拍\"，我会推 top 1 稳分。\n```\n\n每条必有：维度评分（让用户能挑战打分）+ 锚点（让用户校准 composite 的可信度）+ rationale（让用户理解推荐逻辑）。**不允许只输出 composite 排序而无解释**——那是黑箱。\n\n## Key Rules\n\n1. **不报错，给引导**。candidates 缺失是默认状态，不是错误\n2. **不推未打分的**。REQUIRE_SCORED=true 是诚实门槛——推未读过的素材是占星\n3. **必带锚点**。composite 8.47 在不同账号意味不同，锚点把抽象数字 ground 到真实样本\n4. **必带 rationale**。一句话——为什么这条比第二条强？\n5. **去重 published**。已发过的不推（用户可显式覆盖）\n\n## Refusals\n\n- 「直接给我 composite 最高的，不用解释理由」 → 拒绝。展示评分 + 锚点是发现\"打错\"的唯一机会\n- 「把 candidates.md 里所有 entry 都重新打分一遍」 → 路由到 `/cheat-score` 单条做；批量重打分是 `/cheat-bump` 的一部分，不在 recommend 范围\n- 「按预测桶排，不要按 composite」 → 询问理由。bucket 是 composite 的离散化，按 composite 排即按 bucket 排，差异在桶内序——如果用户真想按\"押注期望值\"排，需要乘以平均播放，那是另一个独立 scoring 维度\n\n## Integration\n\n- 上游：`/cheat-trends` 把外部热点拉进 candidates.md → recommend 自动看到\n- 下游：用户挑一条后写稿 → `/cheat-predict`（candidate 的粗 composite 不进入 prediction，prediction 重新打）\n- 与 `/cheat-status` 协调：status 显示 \"candidates 池有 N 条 tier1 未发\"，recommend 提供具体推荐\n","skills/cheat-retro/SKILL.md":"---\nname: cheat-retro\ndescription: T+N 天数据回收 + 复盘 + 把实绩观察写入 rubric-memo.md。这是校准循环的反馈环节——不复盘的预测等于占星。触发词：\"复盘 [path]\"/\"retro this\"/\"T+3d 数据来了\"/\"抓数据 [path]\"/\"把这篇复盘了\"。\nargument-hint: <prediction-file> [— window: 3|5|7] [— source: manual|adapter]\nallowed-tools: Bash(*), Read, Edit, Write, Glob, Grep, Skill\n---\n\n# /cheat-retro — 数据回收与复盘\n\n抓 T+N 天的实际表现 → 对比预测 → 提炼新观察 → 写入 rubric-memo.md。**只追加 `## 复盘` 段，绝不改预测段**。`rubric_notes.md` 是 blind 白名单，只能保存通用公式、维度定义和抽象规则，不能写入样本名、实绩、评论、链接或播放/阅读数。\n\n## Overview\n\n```\n[用户：复盘 predictions/2026-05-04_...]\n  ↓\n[Phase 0: 校验 immutability + 校验时间窗口]\n  ↓\n[Phase 1: 抓数据（manual paste 或 adapter）]\n  ↓\n[Phase 2: 写实绩段 + top 评论关键词]\n  ↓\n[Phase 3: 验证/推翻预测的各假设]\n  ↓\n[Phase 4: 提炼新观察]\n  ↓\n[Phase 5: 落盘（追加到 ## 复盘 段）]\n  ↓\n[Phase 6: 写入 rubric-memo.md 的\"观察记录\"段]\n  ↓\n[Phase 7: 检测是否触发 bump 候选 → 提示用户跑 /cheat-bump]\n```\n\n## Constants\n\n- **RETRO_WINDOW_DAYS = 3** — 默认 T+3d。短视频快平台可设 1，长文设 7\n- **DATA_SOURCE = manual** — manual: 用户粘数字；adapter: 调对应平台 adapter（需配置）\n- **AUTO_PROPOSE_BUMP = true** — Claude 判断是否系统性偏差时自动提议 /cheat-bump\n  - **默认参考**：连续 ≥3 次同向偏差（high/low）→ 提议\n  - **但 Claude 可以更早提议**：1 次极端偏差（如中枢 50w 实绩 5w 这种 ≥10x），即使没有\"连续\"也提议\n  - **也可以更晚**：3 次同向但每次偏差都很小（<25%），可能只是噪声不是系统性\n- **TOP_COMMENTS_N = 20** — 抓 / 粘 top N 高赞评论\n\n> 💡 调用时覆盖：`/cheat-retro <file> — window: 7 — source: adapter`\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<prediction-file>` 或 `<video-folder>` | 用户参数；缺失则从 `.cheat-state.json` 的 `pending_retros[0]` |\n| `rubric_notes.md` | 用户项目根（只读，用于当前规则上下文；不得写入实绩观察） |\n| `rubric-memo.md` | 用户项目根（写入复盘观察、实绩证据、样本名与评论信号） |\n| `.cheat-state.json` | 状态文件 |\n\n### 入参解析（同 cheat-predict 双形态接受）\n\n用户给的可能是：\n- **`predictions/2026-05-04_<id>_<short>.md`** → 直接用这个 prediction 文件\n- **`videos/2026-05-04_<id>_<short>/`** → 找对应的 prediction 文件（按 id 匹配）+ 把 report.md 写到该 video folder 里\n- 缺省 → 从 `pending_retros[0]` 取最早的\n\n## Workflow\n\n### Phase 0: 校验\n\n1. 读 `<prediction-file>`，确认存在\n2. **识别有效预测段**：扫所有 `## 预测...` 段（可能含 `## 预测`、`## 预测 v1`、`## 预测 v2` 等）：\n   - 取**最后一个**`## 预测 vN` 作为本次校准的依据（v2 存在则用 v2；只有 v1 则用 v1；legacy 单段 `## 预测` 直接用）\n   - state.shoots 对应项的 `v2_prediction_written` 应与\"是否存在 v2 段\"一致——不一致则警告（state 与文件脱节）\n3. **校验 immutability**：在内存 cache 住所有 `## 预测...` 段的内容（用于 Phase 5 后核对——**全部段不可改**，不只是有效段）\n4. 校验文件 header 有 `Published at` → 没登记的不能复盘，提示用户先 `/cheat-publish`\n5. 校验时间窗口：今天 - published_at >= RETRO_WINDOW_DAYS。不够 → 提示\"还差 X 天\"，询问用户是否仍坚持复盘（标 `early_retro: true`）\n6. 校验已有复盘段是否已填——已填则询问\"是补充还是修正？\"\n   - 补充 → 在已有复盘段下追加新子段，标日期\n   - 修正预测段（用户错觉）→ 拒绝\n\n### Phase 1: 抓数据\n\n按 `state.data_collection` 字段分两条路径——抓回数据后**写到 video folder 的 `report.md`**（如果 prediction 关联 video folder），同时解析摘要 inline 到 prediction 的复盘段。\n\n#### Path A：`DATA_SOURCE=manual`（候补方案）\n\n- 询问用户：\"粘贴这条作品的当前数据：播放 / 点赞 / 评论 / 转发 / 收藏（顺序无所谓，能识别就行）\"\n- 用户粘 → 解析提取数字\n- **强制要求 top 评论**：让用户从平台后台或直接打开评论区贴 TOP_COMMENTS_N 条到对话里（每条带赞数）\n  - 用户拒绝 / 给少于 5 条 → **拒绝继续**：\"评论才是真信号——'她不一样'这种模因爆发只能从评论看出。\n    没评论的复盘 = 看体温计判断病情。粘 top 20 给我。如果实在拿不到，告诉我原因（比如评论被关了），我帮你标 `comments_unavailable`，但这次复盘价值打折。\"\n- 把粘的原始数据写到 `videos/<...>/report.md`（如有 video folder）\n\n#### Path B：`DATA_SOURCE=adapter`\n\n按 prediction header 的 `Platform` 字段 + state 的 `enabled_perf_adapters` 决定调哪个：\n\n| Platform | Adapter | 调用方式 |\n|---|---|---|\n| `douyin` | `adapters/perf-data/douyin-session/` | `bash <adapters-dir>/douyin-session/run.sh <aweme_id> <video_folder>` |\n| `xhs` | `adapters/perf-data/xhs-explore/` | `bash <adapters-dir>/xhs-explore/run.sh <note_id> <video_folder>` |\n| `linkedin` | `adapters/perf-data/linkedin-session/` | `bash <adapters-dir>/linkedin-session/run.sh <activity_id> <video_folder>` |\n| `wechat_channels` | `adapters/perf-data/wechat-channels/` | `bash <adapters-dir>/wechat-channels/run.sh <post_id> <video_folder>` |\n| `youtube` | `adapters/perf-data/youtube-data-api/`（planned — batch 3, not yet available） | 调 YouTube Data API（需 API key） |\n| `bilibili` | `adapters/perf-data/bilibili-stat/` | `bash <adapters-dir>/bilibili-stat/run.sh <bvid> <video_folder>` |\n| 其他 | 无 adapter | 优雅降级到 Path A |\n\n> `<adapters-dir>` = 克隆源码处的 `cheat-on-content/adapters/perf-data/`（install.sh **不**复制 adapter 到 ~/.claude/skills，只复制 15 个 skill）。定位：`find ~ -path '*/cheat-on-content/adapters/perf-data' -type d | head -1`。\n\n**douyin-session 的特殊处理**：\n- 视频 URL（如 `https://v.douyin.com/abc123`）→ 短链解析 → 提取 aweme_id\n- 调用前确认 cookie 文件存在（adapter 会找 `.auth/`）；不存在则提示用户先跑 `python <adapter>/crawler.py login`\n- adapter 输出在 `<video_folder>/report.md`（adapter 的 renderer.py 已经按这个格式写）\n- cheat-retro 读这个 report.md 解析关键数据 → 摘要写入 prediction 的复盘段\n\n**xhs-explore 的特殊处理**：\n- 笔记 URL（`https://www.xiaohongshu.com/explore/<note_id>?xsec_token=...` 或 `https://xhslink.com/xxx`）→ 提取 note_id\n- 调用前确认 cookie 存在（adapter 找 `.auth-xhs/`）；不存在则提示先跑 `python <adapter>/crawler.py login`\n- 字段已校准（观看 `view_count` 等已写死）；万一接口改版导致某项为 0，看 report.md 末尾 galaxy 原始 JSON，把新 key 加进 `crawler.py` 的 `_normalize_note`\n- **评论可能抓不到**（xsec_token 缺失 / 评论关闭）→ report.md 标\"未抓到评论\" → 此时**降级要求用户 manual 粘 top 20 评论**（评论是真信号，不能省）\n\n**linkedin-session 的特殊处理**：\n- 帖子 URL（`https://www.linkedin.com/feed/update/urn:li:activity:<id>/`）或裸 activity_id → adapter 自动提取 activity_id\n- 调用前确认 cookie 存在（adapter 找 `.auth-linkedin/`）；不存在则提示先跑 `python <adapter>/crawler.py login`\n- **只能抓你本人发的帖子**（LinkedIn 单帖分析仅作者可见）；LinkedIn 界面 日/英 随机切换，`extract.py` 的 `POST_METRICS` 已存双语标签，万一某项为 None 看 `.cheat-cache/linkedin-session-debug/post_<id>.txt` 把新标签补进去\n- **评论只给数、不给正文**（分析页限制）→ report.md 标注 → **降级要求用户 manual 粘 top 评论**（评论是真信号，不能省）\n\n**wechat-channels 的特殊处理**：\n- 仅抓用户自己登录后可见的视频号助手后台数据；调用前确认内容项目存在 `.auth-wechat-channels/`\n- 不存在登录态时，提示先运行 `python <adapter>/review.py login` 并扫码；登录态只保存在内容项目本地\n- adapter 输出 `report.md`，但不落盘原始响应、请求体、完整后台 URL、后台截图、页面文本、评论者昵称或内部用户 ID\n- 找不到目标作品或 adapter 失败时，非 0 退出并降级 manual；不得把旧 report.md 当作本次成功\n\n**bilibili-stat 的特殊处理**：\n- 视频 URL（`https://www.bilibili.com/video/<BV号>` 或 b23.tv 短链）或直接给 BV 号 → adapter 自动提取 BV 号\n- **无需登录**：B站视频数据(view)与评论(reply)都是公开接口、免 wbi 签名，adapter 是纯 httpx，没有 `crawler.py login` 步骤、不碰 `.auth/`\n- 依赖 httpx：首次用 `pip install -r <adapter>/requirements.txt`\n- 评论按热度（sort=2）抓取；B站老接口主楼评论可能偏少，不足时降级 manual 粘\n\n**任何 adapter 失败**（cookie 过期 / 接口变化 / 网络）→ **优雅降级到 manual**，提示用户：\"adapter 调用失败，原因 [X]。改用 manual 模式——粘下面的数据\"。**不阻塞流程**。\n\n#### 共同输出\n\n不管 Path A 还是 B，最终：\n- `videos/<...>/report.md` 含完整原始数据（数字 + top 评论）\n- prediction 文件复盘段含**摘要**（关键比率 + 评论关键词聚类 + 验证/推翻判定）\n- report.md 是数据真相，prediction 复盘段是判断真相\n\n### Phase 2: 写实绩段 + top 评论分析\n\n**实绩数据格式**（参考 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 的复盘段格式）：\n\n```markdown\n### 实绩数据\n- 播放：71.1w（落在 `30-100w` 桶内偏高，相对中枢 50w **+42%**）\n- 点赞：2.4w（赞播比 3.38%）\n- 评论：899（评播比 0.126%）\n- 收藏：5251\n- 分享：1.8w（分播比 2.53%，强）\n```\n\n数据点之间的派生比率（赞播比、评播比、分播比）必须算出来——它们是单纯播放数无法暴露的信号。\n\n**top 评论关键词聚类**：\n- 把粘进来的 N 条评论分 3-5 类（高赞模因 / 概念引用 / 离题噪声 / 转发暴露暗示 / @朋友传播 等）\n- 每类列代表性评论（带赞数）\n- 报告比例（\"22% 是模因复用、35% 是概念引用、5% 是离题\"）\n\n### Phase 3: 验证/推翻\n\n对 prediction 文件里的每一项（推理因素表、关键校准假设、反事实场景），逐项判定：\n\n```markdown\n### 哪些预测被验证 ✅ / 推翻 ❌\n\n**验证 ✅**:\n- 关键校准假设完全成立：本篇 71.1w / 谁问你了 11.7w = 6.07x，远超我押的 1.5-2x\n- ER=5 主导情感传播力 → H1 强证据\n- HP=5 验证：分播比 2.53% 与\"金句被高频引用\"匹配\n\n**推翻 ❌**:\n- 中枢 50w 被超出 +42%\n- 反事实推理里\"必须搭配强社会议题才能破 30w\" 完全错误\n- SR 押注（\"H2 SR 应上调\"）反向被推翻：SR 在情感向场景几乎不贡献\n```\n\n**关键纪律**：\n- 每条验证 / 推翻必须引用具体数据（\"分播比 2.53%\"），不许写\"基本符合\"这种含糊措辞\n- 反事实的\"如果落在 X bucket 意味着什么\"——实际落在的那个 bucket 直接告诉你哪个 rubric 假设被测试了，明确写出来\n\n### Phase 4: 提炼新观察（**两类，分别写入两个文件**）\n\n#### 4a. Rubric 观察（写入 rubric-memo.md）\n\n打分维度 / 公式 / bucket 边界相关的观察：\n\n```markdown\n### 需要写进 rubric-memo.md 的新观察\n\n1. **ER 在情感向场景的真实权重应 ≥ ×2.0**：与谁问你了 6x 流量比是 v2 rubric 最强的反事实证据\n2. **议题分享冲动 (TS) 是隐藏维度**：joker / \"她不一样\" / 滤镜重构提供了安全的自嘲身份，转发不暴露处境，TS=5 的样本\n3. ……\n```\n\n每条观察必须可追溯到具体数据点（不写\"情感很重要\"——写\"ER5/SR2 vs ER3/SR4 同 composite 下流量差 6x\"）。\n\n#### 4b. 写作 Pattern 观察（写入 script_patterns.md）\n\nDiff `scripts/<id>.md`（pre-shoot 草稿，可能是 cheat-seed 写或用户写）vs `videos/<id>/script.md`（实际拍摄稿——cheat-shoot 时用户提供的版本），找出**改动且对流量有明显影响**的部分：\n\n| 用户做了什么 | 流量影响 | 是否提议追加 pattern |\n|---|---|---|\n| 砍掉某段 | 实绩 ≥ 中枢 → \"砍掉没伤流量\"——验证那段冗余 | 是，加到 script_patterns.md \"用户改稿历史观察\"表 |\n| 加了某句 / 互动钩子 | 实绩超中枢 → 可能是新 pattern | 是，候选 Pattern N，标 ≥1 样本待验证 |\n| 改了风格（如开头软化） | 高于同类样本 → 风格改动有效 | 是，候选 Pattern N |\n| 没动结构 / 改动与流量无关 | — | 不追加 |\n\n输出格式：\n\n```markdown\n### 需要写进 script_patterns.md 的新 pattern 候选\n\n1. **用户改稿模式**: 砍掉 [X 段] / 加了 [Y]\n   - 流量影响：实绩 [N] vs 中枢 [M]，[偏差 / 命中]\n   - 建议：追加到 script_patterns.md 的\"用户改稿历史观察\"表\n\n2. **新 pattern 候选 N**：[一句话描述]\n   - 单样本支持\n   - 触发条件：[何时该用]\n   - 建议：追加到 script_patterns.md 末尾的\"新发现的 Pattern\"段，标 ≥1 样本待验证\n```\n\n询问用户：\"要把这些追加到 script_patterns.md 吗？(yes / no / 选择哪几条)\"。**用户确认后才追加**——避免把单点观察直接写成正式 pattern。\n\n> **rubric 进化 ≠ 写作进化**——两者解耦：\n> - rubric_notes.md 学的是\"哪些维度真的预测流量\"\n> - script_patterns.md 学的是\"什么写法真的能起作用\"\n> 可能有交叉（如 MS 维度与\"互动钩子\" pattern），但记录在两个文件里是因为**作用域不同**——rubric 改了影响所有未来打分，pattern 改了影响所有未来 draft。\n\n如果 `videos/<id>/script.md` **缺失**（cheat-shoot 时用户标 `script_lost`） → 跳过 4b，没法 diff。\n如果 `script_consistency = \"consistent\"`（用户拍时没改稿）→ 4b 仍然有意义（diff 也许是空），但可以快速跳过细查。\n如果 `script_consistency = \"modified\"`（用户拍时改了）→ **4b 是核心**，重点学这次改动 → 流量影响。\n\n### Phase 5: 落盘到 ## 复盘 段\n\n用 Edit 工具，**仅追加**到现有 `## 复盘` 段（如有占位 `（待填）` 行先删除）：\n\n```markdown\n## 复盘\n\n**复盘时间**: 2026-05-07（发布 T+3d）\n**抓取时间**: 2026-05-07 09:30\n**数据来源**: manual paste\n\n### 实绩数据\n[Phase 2 内容]\n\n### Top 评论关键词\n[Phase 2 内容]\n\n### 哪些预测被验证 / 推翻\n[Phase 3 内容]\n\n### 需要写进 rubric-memo.md 的新观察\n[Phase 4 内容]\n```\n\n**写完后再次校验**：读取保存后的文件，对比**所有** `## 预测...` 段（v1 / v2 / legacy）的合并哈希应等于 Phase 0 cache 的合并哈希。**任一段被改 → 报错并回滚**。\n\n### Phase 6: 写入 rubric-memo.md + script_patterns.md\n\n#### 6a. rubric-memo.md（Phase 4a 的输出）\n\n按 [observation-lifecycle.md](../../shared-references/observation-lifecycle.md) 的 blind leak guard，追加到 `rubric-memo.md` 的 `## 观察记录` 段。这里可以包含真实样本名、实绩数据、评论关键词和链接；这些内容**绝不**写入 `rubric_notes.md`：\n\n```markdown\n### YYYY-MM-DD [标题简称] (id) — [一句话定性]\n- 预测：composite=X.XX，bucket=Y\n- 实绩：播放 / 点赞 / 评论 / 转发（带 T+Nd 标注）\n- Top 评论关键词：[简短摘录 + 赞数]\n- 判断：哪个维度被验证/推翻？为什么？\n- Rubric 调整：[如果有，写明 \"下次打 XX 类文章时改 YY\"]\n- 详见：[predictions/<file>.md]\n```\n\n**检测跨样本 pattern**：扫描 `rubric-memo.md` 已有\"观察记录\"，看新观察是否与某条已有观察形成 ≥2 样本支持。命中则在 `rubric-memo.md` 升级到\"重大跨样本观察\"段。只有在后续 `/cheat-bump` 落地时，才把已验证的规律抽象成通用语言写入 `rubric_notes.md`。\n\n#### 6b. script_patterns.md（Phase 4b 的输出，**用户确认后才写**）\n\n如 Phase 4b 用户回 \"yes\" 或选择性确认了某几条：\n- \"用户改稿模式\" → 追加到 script_patterns.md 的\"用户改稿历史观察\"表\n- \"新 pattern 候选 N\" → 追加到末尾\"新发现的 Pattern\"段，**显式标 ≥1 样本待验证**\n\n新 pattern 候选的格式（同 [script_patterns.template.md](../../templates/script_patterns.template.md) 的 Pattern 11/12 示例）：\n\n```markdown\n### Pattern N（来自 [视频简称]，单样本待验证）\n\n**现象**：[Phase 4b 描述]\n\n**原理**：[为什么有效——基于这一次观察的猜测]\n\n**触发条件**：[何时该用]\n\n**待验证**：需要 ≥2 样本支持才能升正式 pattern。\n```\n\n跨样本 pattern 升正式：扫描\"新发现的 Pattern\"段，看是否有 ≥2 样本支持同一现象 → 升到核心 pattern 库 + 删 \"待验证\" 标记。\n\n如用户在 Phase 4b 全否（\"no\"）→ 跳过 6b，rubric-memo.md 仍照写。\n\n### Phase 7: 检测 bump 触发\n\n读 `.cheat-state.json` 的 `consecutive_directional_errors` 字段，按本次复盘判定向更新：\n- 本次预测高估（实绩 < 中枢 -25%） → push `[\"high\"]` + 记录 deviation_magnitude（如 0.5x / 0.3x）\n- 本次预测低估（实绩 > 中枢 +25%） → push `[\"low\"]` + 记录 deviation_magnitude\n- 在 ±25% 内 → 不 push\n\n**Claude 判断是否提议 bump**（不是固定门槛）：\n\n```\n判断维度：\n1. 连续同向次数（参考默认：≥3）\n2. 单次偏差幅度（参考默认：>2x 或 <0.5x 算极端）\n3. 偏差是否能解释为单一维度漏判（如 ER 或 SR 一致偏离）\n4. 用户是否在复盘里反复提到同一现象\n\n任一足够强 → 提议 bump：\n- 3 次连续同向，每次都中等偏差 → 提议\n- 1 次极端偏差（如 ≥10x），即使没连续 → 提议（\"一次性强信号\"）\n- 2 次同向 + 评论区出现一致的反向证据 → 提议（\"评论 + 数据双信号\"）\n\n不提议的情况：\n- 3 次同向但每次都很小（<25%）→ 可能只是噪声\n- 偏差跨多个维度无清晰方向 → bump 不知道改什么\n```\n\n提议时输出：\n\n```\n🚨 检测到 [系统性偏差信号] / [极端单点偏差] 。\n\n[简短描述：连续 N 次 / 1 次极端 / 评论双信号 等]\n\n这可能是 rubric 系统性偏差的信号。建议：\n- 跑 /cheat-bump 看是否需要升级公式\n- 或先看 /cheat-status 详细分析\n\n注：本次提议是 [default-aligned: 满足 ≥3 同向] / [judgment-driven: 1 次 10x 强偏差]\n```\n\n更新 state file：\n```json\n{\n  \"calibration_samples\": <+1>,\n  \"pending_retros\": [<剔除本次>],\n  \"last_retro_at\": \"<ISO>\",\n  \"consecutive_directional_errors\": [...]\n}\n```\n\n## Key Rules\n\n1. **预测段 immutable**。Phase 0 cache + Phase 5 校验是双保险。任何 hash 不一致 → 报错回滚\n2. **数据来源必须标注**。`数据来源: manual paste` 或 `数据来源: adapter:douyin-session` 写进复盘段\n3. **观察可追溯**。每条新观察引用具体数据点\n4. **不在复盘里 bump**。Phase 7 只**提议** bump，实际升级走 `/cheat-bump`——避免一次操作做两件事\n5. **早复盘标记**。RETRO_WINDOW_DAYS 不到就复盘 → state file 记 `early_retro: true`，bump 时这种样本权重降级\n\n## Refusals\n\n- 「这条数据已经看过了，但你假装没看，按预测时的盲度做复盘」 → 复盘本来就是看完数据再做的；这个表述本身没有违规，但要确认用户没在 prediction 写之前透露过数据\n- 「把预测段的概率分布改一下，让复盘看起来更准」 → 拒绝。原则 #1\n- 「跳过观察提炼，直接结束」 → 拒绝。新观察是 rubric 进化的唯一燃料；缺它复盘退化为\"看一眼\"\n- 「直接 bump，不要单独走 /cheat-bump」 → 拒绝。bump 流程有完整的跨模型审 + cleanup pass，retro 是触发器不是执行器\n\n## Integration\n\n- 前置：`/cheat-publish` 已登记 + 时间窗口达到\n- 下游：累计 `consecutive_directional_errors` 满 3 → 触发 `/cheat-bump` 提议\n- 状态字段更新：`calibration_samples` +1（这是 cheat-status 显示进度的关键）\n- pending_retros：剔除本条\n- 与 [observation-lifecycle.md](../../shared-references/observation-lifecycle.md) 紧耦合：每次复盘是观察新增的入口\n","skills/cheat-score-blind/SKILL.md":"---\nname: cheat-score-blind\ndescription: |\n  INTERNAL sub-agent for blind 7-dim rubric scoring. **NOT a user-facing skill — do NOT invoke from main conversation.** Called via Task tool by cheat-score / cheat-predict / cheat-bump to get a context-isolated score on a script. Receives ONLY script_path + rubric_notes_path; refuses any other input. Outputs strict JSON: 9 dimensions × {score 0-5, confidence enum, one-line reason}. **Hard refuses to Read** .cheat-state.json, predictions/*, retro 段, or anything that could leak post-publish data. This is channel B in the 3-channel calibration model (A=main, B=blind sub, C=cross-model).\nallowed-tools: Read, Glob, Grep\nargument-hint: <script-path> <rubric-notes-path>\n---\n\n# /cheat-score-blind — Channel B (blind scorer sub-agent)\n\n> ⚠️ **这是子 agent，不是用户 skill**。只能由 `cheat-score` / `cheat-predict` / `cheat-bump` 通过 Task tool spawn。用户直接 trigger 没有意义——主对话已经被污染，调用 blind sub-agent 在主 context 里跑不构成隔离。\n\n---\n\n## Why this exists（绝不可省的背景）\n\ncheat-on-content 的 7/9 维打分原本 inline 在主对话——但主 Claude 已经看过：\n- 用户对话历史（含偶然提到的播放数 / 评论 / 情绪）\n- 已发布作品的实绩数据\n- 历史 `predictions/*.md` 含复盘段（**严重污染**）\n- 用户的赞美 / 抱怨 / 期待\n\ninline 打分 = **被污染的\"盲\"预测**。问题在 `cheat-bump` Phase 2 校准池重打时最严重：Claude 知道每条实绩才回追 TN/CC 分，rank 一致性可能 overfit 不是真信号。\n\n**channel B 的角色**：用 Task tool 把打分动作丢进一个**全新 context**——这个 sub-agent 没看过主对话、没读过 state、没碰过 predictions/。它只看 script 全文 + rubric_notes.md，按 rubric 打分。\n\n输出回传主对话后，主 Claude 自己对比、做最终决策。隔离的是**打分这个动作的输入**，不是决策权。\n\n## 三 channel 模型\n\n| Channel | 输入 | 用途 | 风险 |\n|---|---|---|---|\n| **A** = 主对话 | 全部上下文 | 跟用户交互、写 retro、决策 | 被实绩 / 用户态度污染 |\n| **B** = blind sub-agent (this) | **只** script + rubric_notes.md | 给一份未受污染的打分作为 anchor | 仍是 Claude，RLHF prior 共享 |\n| **C** = 跨模型 audit (`mcp__llm-chat__chat` to qwen-max) | 校准池数据 + 新公式 | bump 终局 sanity check | RPM 限制、模型差异、单点 |\n\nA 决策时把 B 当对照看 disagreement，**不当真理**。C 只在 bump 终局调一次。\n\n---\n\n## Inputs（**唯一被允许的输入**）\n\n| 必填 | 来源 | 说明 |\n|---|---|---|\n| `<script-path>` | 主 Claude 通过 Task prompt 显式传入 | `scripts/<id>.md` 全文 |\n| `<rubric-notes-path>` | 同上 | 用户项目根 `rubric_notes.md` 当前 rubric 公式 + 维度定义 |\n\n**仅此两个文件可读**。其他一切**硬拒绝**——见下方 \"Hard refusals\" 段。\n\n## 禁止读取（hard list）\n\n下面这些路径 / 模式 sub-agent **绝不能 Read** —— 即使主 Claude 在 Task prompt 里手滑塞进来，也要拒绝并在 JSON 输出标对应 `refusal` 码：\n\n| 路径模式 | 为什么禁 | refusal_code |\n|---|---|---|\n| `.cheat-state.json` | 含 calibration_samples / pending_retros / last_published_at / shoots — 全是后视数据 | `blocked_contaminated_input` |\n| `predictions/*.md` | 含 `## 预测` 段 + `## 复盘` 段，复盘段就是实绩 | `blocked_contaminated_input` |\n| `videos/*/report.md` | T+3d 抓回的真实数据 | `blocked_contaminated_input` |\n| `videos/*/script.md` | 后改拍摄稿，复盘时被对照 | `blocked_contaminated_input` |\n| `STATUS.md` | cheat-status 渲染的看板，含过去数据 | `blocked_contaminated_input` |\n| `.cheat-cache/usage.jsonl` | 行为 log | `blocked_contaminated_input` |\n| **`rubric-memo.md`** | **cheat-bump 升级 Memo 累积档案——含真实视频名 + 实绩 + 派生证据。这是 channel B 的最大泄漏入口（PR #11 实测复现）** | **`blocked_rubric_memo`** |\n| **`audience.md`** | **cheat-persona 从复盘评论派生的受众画像——含评论证据 / 实绩信号。属 channel A creative 资产，进 blind 打分 = 实绩泄漏** | **`blocked_audience`** |\n| 任何含\"播放 / 阅读 / 点赞 / 评论数 / 转发 / w / 万 / k / M\"的文件 | 直接污染 | `blocked_contaminated_input` |\n\n**白名单只有两个**：\n- `scripts/<id>.md`（pre-shoot 草稿，传入参数）\n- `rubric_notes.md`（评分公式 + 维度定义，**应**只含通用语言；如发现实绩数字 → 标 `non_blind_warning` 并降 confidence）\n\n如果主 Claude Task prompt 漏传了某条路径，sub-agent 主动询问\"我只允许读 script + rubric_notes，缺哪个？\"——**绝不**自己去 Glob 探测项目结构补全。\n\n> ⚠️ **白名单兜底自检**：读完 `rubric_notes.md` 后必跑 `grep -E '\\\\d+\\\\s*[wWmMkK万]|播放|实绩|实际'`——命中 → 标 `self_check.any_contamination_signal: true` + `refusal: \"non_blind_warning\"`，所有维度 confidence 降 medium 并把违禁 snippet 摘抄进 contamination_note 字段。**仍输出 dimensions** 让主 Claude 知道发生了什么——拒绝输出比误判更糟，但要诚实标注。\n\n---\n\n## Workflow\n\n### Phase 0：边界自检\n\n1. 解析 Task prompt 拿 `<script-path>` 和 `<rubric-notes-path>`\n2. 校验路径符合白名单——不在 `scripts/` 下的 .md → 拒绝（除非主 Claude 显式说明\"这是临时草稿临时路径，标 `non_standard_path: true`\"）\n3. Read `<rubric-notes-path>` → 解析当前 rubric_version + 维度数量（7 或 9）+ 公式\n4. Read `<script-path>` → 拿到 script 全文 + 字数\n\n⚠️ **不要做的事**：\n- 不要为了\"看看用户做啥账号\"去 Read `benchmark.md` —— benchmark 是 Channel A 的 context，不属于本 sub-agent\n- 不要为了\"看看历史风格\"去 Glob `predictions/` —— 那是污染源\n- 不要去 Read `.cheat-state.json` 看 calibration 进度 —— 你**完全不需要知道**主 Claude 跑了多少篇\n\n### Phase 1：按 rubric 打 N 维分\n\n按 `rubric_notes.md` 当前 rubric 公式：\n\n- v0：7 维等权（ER / SR / HP / QL / NA / AB / SAT）—— 默认起步\n- v1：用户校准过的（权重不同）\n- v2 / v2.1 / ...：含 MS / TS 等新增维度（9 维）\n\n对每个维度：\n1. 给一个 **0-5 整数分**\n2. 给一个 **per-dim confidence** enum：`high | medium | low`\n   - high：稿子里有直接证据（一句话指向该维度）\n   - medium：可推断但需要解释\n   - low：稿子信号太弱，纯估\n3. 给一行 **理由** ≤ 30 字，**必须引用稿子里具体词或场景**\n\n不算 composite——composite 是公式行为，主 Claude 用回传的维度分自己算。\n\n### Phase 2：返回严格 JSON\n\n输出**只能**是一个有效 JSON。所有 markdown 解释都封禁——主 Claude 要的是结构化数据回主 context 解析。\n\n```json\n{\n  \"subagent_version\": \"v1\",\n  \"rubric_version\": \"v2\",\n  \"script_path\": \"scripts/2026-05-04_abc123_短title.md\",\n  \"script_hash\": \"<sha256:12 of script content>\",\n  \"scored_at\": \"<ISO 8601 +08:00>\",\n  \"dimensions\": {\n    \"ER\": { \"score\": 4, \"confidence\": \"high\",   \"reason\": \"PPT加油猫猫开头—具象画面，情绪反差强\" },\n    \"SR\": { \"score\": 3, \"confidence\": \"medium\", \"reason\": \"AI焦虑是议题但非热点对峙\" },\n    \"HP\": { \"score\": 5, \"confidence\": \"high\",   \"reason\": \"首句\\\"第七页大屏中央 加油猫猫\\\"具象反差\" },\n    \"QL\": { \"score\": 5, \"confidence\": \"high\",   \"reason\": \"\\\"加油猫猫救了我一命\\\"双关金句\" },\n    \"NA\": { \"score\": 4, \"confidence\": \"medium\", \"reason\": \"单线反思+收束，清晰但不复杂\" },\n    \"AB\": { \"score\": 4, \"confidence\": \"medium\", \"reason\": \"一人公司题但AI焦虑普适\" },\n    \"SAT\": { \"score\": 2, \"confidence\": \"high\",  \"reason\": \"共情调，几乎无讽刺\" }\n  },\n  \"input_status\": {\n    \"rubric_notes_read\": true,\n    \"script_read\": true,\n    \"any_other_file_read\": false\n  },\n  \"self_check\": {\n    \"saw_play_numbers\": false,\n    \"saw_comments\": false,\n    \"saw_retro_segment\": false,\n    \"any_contamination_signal\": false\n  },\n  \"refusal\": null\n}\n```\n\n`refusal != null` 的合法值：\n- `\"blocked_contaminated_input\"`：Task prompt 传了禁读路径（state / predictions / videos / 等）\n- `\"blocked_rubric_memo\"`：Task prompt 传了 `rubric-memo.md`（bump 升级档案，含实绩）\n- `\"blocked_audience\"`：Task prompt 传了 `audience.md`（受众画像，含评论派生的实绩信号）\n- `\"script_path_invalid\"`：找不到 script 文件\n- `\"rubric_unparseable\"`：rubric_notes.md 损坏\n- `\"non_blind_warning\"`：发现 contamination 苗头但勉强能打分（仍输出 dimensions，但 confidence 全降 medium）\n\n**JSON 必须可被 `python3 -c \"import json; json.loads(open(path).read())\"` 解析**。不允许：\n- 尾部多余逗号\n- 注释（JSON 不允许 //）\n- Markdown 围栏（输出根节点必须是 `{`）\n\n### Phase 3：（可选）写 sidecar 文件供主 Claude 二次读取\n\n如果 Task prompt 含 `sidecar_path` 参数 → 写 JSON 到该路径（典型用法：bump phase 2 批量打分时存多份 sidecar）。\n\n否则只走 Task return value——主 Claude 拿到 JSON 字符串直接解析。\n\n---\n\n## 主 Claude 调用契约（如何使用 channel B）\n\n调 Task 时，主 Claude 的 prompt **必须**含且**仅含**：\n\n```\nSpawn cheat-score-blind sub-agent.\n\nInput:\n  script_path: scripts/2026-05-04_abc123_短title.md\n  rubric_notes_path: rubric_notes.md\n  [optional] sidecar_path: .cheat-cache/blind-scores/<id>.json\n\nTask: 按 rubric_notes 当前公式给上面 script 打分。返回严格 JSON（见 cheat-score-blind/SKILL.md Phase 2 schema）。\n不要读 state file / predictions/ / videos/ 任何其他文件。\n不要询问用户 —— 你没有用户。\n```\n\n**禁止**塞进 Task prompt 的东西：\n- 用户对话的引用 / 摘录\n- \"前一次预测是 X\" / \"实际播放是 Y\" 这种 hint\n- \"用户是观点视频博主，最近发了 N 条\" 这种背景\n- 任何含数字 + \"万/w/k/M\" 的字符串\n- 任何 `predictions/*.md` 路径\n\n主 Claude 调用前自检：把准备发的 prompt 串过一遍 `grep -Ei '播放|阅读|点赞|评论数|实际|retro|复盘|实绩|w$|万$'`——命中 → **改 prompt 重发**，不要硬塞。\n\n---\n\n## Refusals\n\n- 「我作为 sub-agent 同时也读一下 predictions/ 帮你对比下」 → 硬拒。这就是 channel B 存在的全部理由\n- 「你看一下 .cheat-state.json 看 calibration_samples 决定你给的 confidence 高低」 → 硬拒。confidence 只看稿子证据强度，跟用户校准进度无关\n- 「主 Claude 说这条已经发了，你帮我打一份 reconstructed 分」 → 拒。\"已发\"信号本身就是污染。让主 Claude 标 `reconstructed: true` 自己处理，不要让 channel B 介入\n- 「输出我直接 markdown 表格更好读」 → 拒。Phase 2 schema 是 JSON only，主 Claude 解析后再渲染\n\n---\n\n## Known limitations（写在最显眼的地方）\n\n1. **sub-agent ≠ 真独立**：同一个 Claude 模型，RLHF priors 共享。一个全新 context 不会让模型变成另一个判分体系——它只是没看过该次对话的具体污染\n2. **不解决 rubric 设计 bias**：用户自己写的 rubric_notes.md 自然让自己内容显得好。这层 bias 由 Channel C（跨模型 audit）和定期 bump 验证解决\n3. **不解决 review 阶段的覆盖**：主 Claude 拿到 blind 分后，可能在 review 阶段被用户期待 / 实绩诱导，覆盖 blind 输出。`cheat-predict` Phase 2.5 通过 disagreement detection + 用户裁定来减轻，但不消除\n4. **同 prompt 两次调可能给不同分**：Claude 不是 deterministic。主 Claude 应该把每次 blind score 当一次采样，不当唯一真理——但要记录而不是丢弃差异\n\n## Integration\n\n- **`cheat-score`** Phase 2：默认 delegate 到本 sub-agent（替代旧的 inline 打分）\n- **`cheat-predict`** Phase 2：默认 delegate；Phase 2.5 用 disagreement detection\n- **`cheat-bump`** Phase 2：**强制** delegate，bump 时**不接受 self-scored fallback**\n- **`cheat-retro`**：不调用——retro 本来就看实绩，blind 无意义\n","skills/cheat-score/SKILL.md":"---\nname: cheat-score\ndescription: 给单篇稿子打 rubric 分。**只在控制台输出，不写文件，不预测**。触发词：\"打分这篇 [path]\"/\"score this [path]\"/\"给这稿子打分\"/\"先打分看看\"。是 cheat-predict 之前的轻量探索动作。\nargument-hint: <draft-path>\nallowed-tools: Read, Glob, Grep\n---\n\n# /cheat-score — 单稿打分\n\n打分但**不预测**。用户用它快速看稿子的 composite，决定是否值得进入正式预测流程。\n\n## Overview\n\n```\n[用户：打分这篇 draft.md]\n  ↓\n[读 draft.md + rubric_notes.md]\n  ↓\n[逐维度打 0-5 + 写一行理由 + 算 composite]\n  ↓\n[控制台输出：评分 + composite + 推荐下一步]\n  ↓\n[结束 — 不写任何文件]\n```\n\n## Constants\n\n- **RUBRIC_PATH = rubric_notes.md** — 当前 rubric 来源\n- **OUTPUT_DETAIL = full** — full: 含每维度理由；compact: 仅分数表\n\n> 💡 调用时覆盖：`/cheat-score draft.md — OUTPUT_DETAIL: compact`\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<draft-path>` | 用户作为参数传入；如缺失则在对话里询问 |\n| `rubric_notes.md` | 用户项目根 |\n| `.cheat-state.json` | 用户项目根（用于读当前 `rubric_version` 与 mode） |\n\n## Workflow\n\n### Step 1：前置检查\n\n1. 读 `.cheat-state.json` → 不存在则提示用户先跑 `/cheat-init`，停止\n2. 读 `<draft-path>` → 不存在或无内容 → 报错并停止\n3. 读 `rubric_notes.md` 找到当前生效的公式段（一般在\"当前评分维度\"或\"综合分公式\"位置）\n\n### Step 2：识别公式与维度\n\n从 `rubric_notes.md` 解析出：\n- 当前 rubric_version\n- 维度列表与权重（如 `ER×1.5 + SR×1.5 + HP×1.5 + QL + NA + AB + SAT`）\n- 归一化常数（如 `/ 8.5 × 2.0`）\n- 每个维度的 0-5 含义（从\"当前评分维度\"段表格读）\n\n如果 `rubric_notes.md` 格式与预期不符（用户手改过结构）→ 询问用户当前公式是哪一行，**不要自己猜**。\n\n### Step 3：**delegate 到 blind sub-agent**（不再 inline 打分）\n\n主对话已经被用户对话 / 已发数据 / 历史 retro 段污染——inline 打分等于带着后视镜判分。\n\n改成**通过 Task tool 调 `/cheat-score-blind` sub-agent**，主 Claude 只做调度 + review。详见 [skills/cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md)。\n\n**Task prompt 模板**（**只能含**下面这些）：\n\n```\nSpawn cheat-score-blind sub-agent.\n\nInput:\n  script_path: <用户给的 draft path>\n  rubric_notes_path: rubric_notes.md\n\nTask: 按 rubric_notes 当前公式给上面 script 打分。返回严格 JSON（见 cheat-score-blind SKILL.md Phase 2 schema）。\n不要读 state file / predictions/ / videos/ 任何其他文件。\n不要询问用户 —— 你没有用户。\n```\n\n**禁止**塞进 Task prompt 的东西（[cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md) 的\"主 Claude 调用契约\"段）：\n- 用户对话引用 / 摘录\n- 含播放数 / 万 / w / k 等字眼\n- \"前一次预测是 X\" / \"实际播放是 Y\" 等 hint\n- 任何 `predictions/*.md` 路径\n\n调用前 grep 自检：`echo \"<prompt>\" | grep -Ei '播放|阅读|点赞|评论数|实际|retro|复盘|实绩|w$|万$'` 命中 → 改 prompt 重发。\n\n### Step 4：解析 sub-agent 回传 JSON + review\n\nsub-agent 返回严格 JSON。主 Claude：\n\n1. 解析 dimensions 段（含 score + per-dim confidence + reason）\n2. 校验 `self_check.any_contamination_signal == false`，否则警告\n3. 按 rubric_notes 公式算 composite（公式逻辑在主，分数来自 sub-agent）\n4. **不修改 sub-agent 给的维度分**——score 只是显示。如果用户挑刺（\"AB 给 3 不是 4\"），主 Claude 记录到 `User Override` 但 sub-agent 原始分留档\n\n如果 sub-agent 返回 `refusal != null`：\n- `blocked_contaminated_input` → 报告 Task prompt 含违禁字段，让主 Claude 重发\n- `script_path_invalid` → 检查路径\n- `rubric_unparseable` → 提示用户 rubric_notes.md 损坏\n- `non_blind_warning` → 仍接受 dimensions（但 confidence 全 medium），警告\n\n### Step 5：算 composite + 输出\n\n按当前公式算综合分。控制台输出（OUTPUT_DETAIL=full）：\n\n```\n📊 [draft.md 短标题] — 打分（rubric: v2）\n\n| 维度 | 分 | 理由 |\n|---|---|---|\n| ER (情感共鸣)        | 5 | \"半夜三点翻聊天记录\" 极端具象 |\n| HP (钩子强度)        | 5 | IS 句一句锁定受众 |\n| QL (金句密度)        | 5 | MVP 句\"间歇性希望\"独立可传 |\n| NA (叙事性)          | 3 | 平铺直叙，弱弧线 |\n| AB (受众广度)        | 5 | 暗恋/前任普适 |\n| SR (社会议题共振)    | 2 | 纯个人情感，无社会托底 |\n| SAT (讽刺深度)       | 4 | 致谢段自指反讽 |\n\n公式：(ER×1.5 + SR×1.5 + HP×1.5 + QL + NA + AB + SAT) / 8.5 × 2.0\ncomposite = (5×1.5 + 2×1.5 + 5×1.5 + 5 + 3 + 5 + 4) / 8.5 × 2.0 = **8.24**\n\n📍 落在 30-100w 桶（基于 starter-rubrics 的 bucket 边界）\n\n下一步建议：\n- 如果你已写定最终稿、准备发布 → 说 \"启动预测\"\n- 如果想再改稿子 → 改完再打一次（多次打分不留痕迹）\n- 如果想看历史相近 composite 的样本 → 说 \"找 composite 8.0-8.5 的锚点\"\n```\n\nOUTPUT_DETAIL=compact 时仅输出分数表 + composite，不附理由列。\n\n### Step 6：**绝不**做的事\n\n- ❌ 写任何文件（包括 predictions/、rubric_notes.md、candidates.md）\n- ❌ 给 bucket 概率分布（那是 cheat-predict 的活）\n- ❌ 触发\"已发布\"或\"复盘\"逻辑\n- ❌ 提议 rubric 升级（即使打分时发现明显异常也只在控制台提示，不动 rubric）\n\n## Key Rules\n\n1. **打分走 sub-agent**。主 Claude 不再 inline 打分。看 [cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md) 的隔离协议\n2. **整数分**。不允许 4.5、3.7\n3. **盲打优先**。sub-agent 只看 script + rubric，天然盲——这是它存在的全部理由\n4. **理由是诊断工具**。每个维度的 1-30 字理由不是装饰——复盘时用来找出哪个维度判断错了\n5. **不写文件**。这是 score 与 predict 的核心区别。score 是探索，predict 是承诺\n6. **不算 candidate composite**。candidates.md 里的 composite 字段在 cheat-trends/cheat-recommend 里写——score 只服务\"已写好的具体稿子\"\n\n## Refusals\n\n- 「打分顺便预测一下」 → 拒绝。请改用 `/cheat-predict`。原因：predict 必须走 blind check + 写 immutable 日志，score 跳过这些\n- 「打完分把分数写进 rubric_notes.md 的观察段」 → 拒绝。observation lifecycle 规定观察必须有\"实绩 vs 预测\"对比，光有打分不构成观察\n- 「能不能直接告诉我会不会爆」 → 拒绝。给具体 composite + bucket 的判定要求走 predict 流程；score 只输出当前 rubric 下的机械计算\n- 「跳过 blind sub-agent 让主 Claude 直接打」 → cheat-score **不接受**这种 escape hatch（与 cheat-predict 不同；cheat-predict 有 `--skip-blind`）。score 是轻量探索，没理由放弃隔离。如真的 Task tool 不可用 → 提示用户配置后再试\n\n## Integration\n\n- 是 `cheat-predict` 的前置探索：用户可以反复 score 不同稿子版本，确定一份再 predict\n- score 不更新 `.cheat-state.json`——这是无副作用操作\n- 如果用户连续 score 同一稿子 ≥3 次 → 控制台温和提示\"反复打分会引入决策疲劳，差不多可以决定了\"\n","skills/cheat-seed/SKILL.md":"---\nname: cheat-seed\ndescription: 跟用户对话讨论选题——**默认一次一个**，用户主动给主题或经历，AI 围绕用户的输入深挖、提炼角度、写一份 draft。不是 AI 拿三个开放问题追用户，也不是一次给 5 个候选。触发词：\"找选题\"/\"我想做一条 X\"/\"最近有个想法\"/\"seed\"/\"启动种子\"。可选 batch 模式：`/cheat-seed --batch 5` 走旧的 brainstorm 5 候选 + 写 5 draft 流程。\nargument-hint: \"[— batch: N] [— sources: <comma-separated>]\"\nallowed-tools: Bash(*), Read, Write, Edit, Glob, WebFetch, Skill\n---\n\n# /cheat-seed — 选题对话（默认）/ 批量 brainstorm（可选）\n\ncheat-seed 的核心是**跟用户讨论选题**，不是机械地 brainstorm。好内容来自用户的真实经历 + 观察 + 情绪——这些是 AI 不可能凭空 brainstorm 出来的。AI 的角色是**听用户讲 → 帮提炼角度 → 写一份 draft**，不是 dump 15 候选让用户挑。\n\n**默认模式**：对话式一次一个。\n**Batch 模式**（`--batch N`）：保留旧的 brainstorm N 候选 + 写 N 份 draft 流程，给\"完全没想法 + 想批量初始化\"的用户。\n\n## 三种 Mode（自动识别）\n\n```\nMode A — 用户主动给主题（**最常见**）：\n  用户：\"/cheat-seed\" + 直接说\"我想做一条关于 X 的\"\n       或：\"/cheat-seed 我最近开会被领导...\"\n  ↓\n  AI 围绕 X / 这件事**深挖**——什么瞬间触发？最让你 [情绪 / 不爽 / 觉得有意思] 的是哪点？\n  ↓\n  收敛到一个具体角度 → 提议 → 用户认可 → 写 1 份 draft → 完成\n  ↓\n  问\"下一篇？\" 或用户说\"今天就这样\"\n\nMode B — 用户给方向但不具体：\n  用户：\"最近想做点关于 [职场 / 婚恋 / AI / ...] 的\"\n  ↓\n  AI：\"[范围] 太广。最近你接触到的具体哪件事让你想做这个方向？\"\n  ↓\n  收敛到 Mode A 的具体经历\n\nMode C — 用户完全没想法（少见）：\n  用户：\"我不知道做什么\" / \"帮我想个题\"\n  ↓\n  AI：\"好，进 brainstorm 模式——先抓热点 + 你之前的兴趣方向，给你 1 个建议\"\n  ↓\n  跑 trend-sources 抓热点 + 读 candidates.md / predictions/ 看用户历史\n  ↓\n  提议 1 个角度（不是 5 个） → 用户认可 → 写 draft\n\nBatch Mode — 用户显式要批量（`/cheat-seed --batch 5`）：\n  按旧版 brainstorm 流程：3 问题 → 15 候选 → 用户挑 → 写 5 draft。\n  给\"今天想一次性把未来 2 周的选题搞定\"的用户。\n```\n\n**关键纠正**（与旧版的区别）：\n- AI **不主动开放问**——等用户给输入再深挖\n- 一次一个选题，不是 5 个\n- 默认对话式 + 一次一个，batch 是 escape hatch\n\n## Constants\n\n- **DEFAULT_TREND_SOURCES = [\"manual-paste\"]** — 仅 Mode C / Mode A 灰色场景 / Batch 用到。用户可在 state 里加 aihot / trendradar-mcp\n- **TREND_TOOL_ROUTING** — 按 `content_form` 路由数据源，详见 [shared-references/data-source-routing.md](../../shared-references/data-source-routing.md)\n- **MODE_B_MAX_REPROBE_TURNS = 2** — Mode B \"为什么\" 反问最多 2 轮；超过则转 Mode C\n- **MAX_DEEP_DIVE_TURNS = 4** — Mode A 收敛阶段最多 4 轮反问，避免 AI 过度盘问\n- **WITH_DRAFT = yes** — 默认确认角度后立刻写 draft；用户可说 \"等下，我自己写\" 跳过\n- **DRAFT_LENGTH** — 派生自 `state.typical_duration_seconds`：30s→100-200字 / 90s→250-500字 / 240s→600-1000字 / 450s→1100-2000字 / 900s→2200+字\n- **HUMANIZE_DRAFT = on**（默认）/ off —— 写完 draft 后用 `humanizer` skill 过一遍，去掉 AI 写作 tells（em-dash 滥用 / rule of three / inflated 词汇 / 空泛归因等）。off 时直接出原始 AI draft。**只 humanize 正文，不动 header 的\"必须改写\"警告**\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `.cheat-state.json` | 读 calibration_samples / typical_duration / cadence |\n| `rubric_notes.md` | 读当前 rubric（粗打分用） |\n| `script_patterns.md` | 读已有 pattern（写 draft 时按 cheat sheet 选结构） |\n| `predictions/*.md`（如有） | 已发历史，brainstorm 时作为 context |\n| `audience.md`（如有） | 受众画像——选题 / 写稿时的\"谁在看\"镜子（由 `/cheat-persona` 派生） |\n\n## Workflow\n\n### Phase 0: 前置检查 + 加载所有 context（**核心：3 个 context 来源**）\n\n1. 读 `.cheat-state.json` → 不存在则提示先跑 `/cheat-init`\n2. 读 `rubric_notes.md` 拿当前公式（粗打分用）\n3. 读 `script_patterns.md`——写 draft 时按 cheat sheet 选结构\n4. **读已有 prediction 文件**（含 init 时 import 的 reconstructed）作为 **context 来源 A**（用户自己历史）：\n   - 0 个 → A 为空\n   - ≥1 个 → A 有内容，提取 (title / 7 维 / 实绩)\n5. **读 `benchmark.md`**（如存在）作为 **context 来源 B**（对标账号）：\n   - `state.benchmark_status = imported` → B 有内容，提取对标账号的样本主题分布、调性、Pattern\n   - `state.benchmark_status = none / pending` → B 为空\n6. **读 `audience.md`**（如存在且非空骨架）作为 **受众镜子**：\n   - Confidence 🟡 以上（有真实复盘数据派生）→ 选题 / 写 draft 时作为\"这个 persona 会在乎吗\"的检验视角\n   - Confidence 🔴/🟠（空 / 仅 benchmark seed）→ 当弱参考，不当硬约束\n   - **不进粗打分**——audience.md 是 creative lens，粗打分仍只用 rubric。persona 影响\"写什么 / 哪个角度\"，不影响分数\n7. 检查用户的入参——是否含具体 topic / 经历，决定走 Mode A/B/C/Batch\n\n**brainstorm 时的 context 优先级**（**Claude 判断**——下面是参考默认）：\n\n- **A 主导**（用户自己数据）：当 Claude 判断用户数据已能驱动方向时（参考默认：`calibration_samples ≥ 10`，但 Claude 可以更早——如 N=5 但出现 ≥3 个与 benchmark 明显不一致的强样本）\n- **B 主导**（benchmark）：用户数据少 + benchmark 有内容时\n- **B 缺席**（benchmark 为空）+ 用户数据少：纯靠用户 input + 抓热点；明确告诉用户\"没 benchmark 也没足够自己数据，建议跑 /cheat-learn-from 后再回来 brainstorm\"\n\n判断依据**不是死磕样本数**，而是看：\n- 用户最近 N 个样本的实绩**是否与 benchmark 的高表现样本类型一致**——一致说明 benchmark 仍有借鉴价值；不一致说明用户已经走自己的路\n- 用户的样本**多样性**——3 篇都是同类内容不算成熟；3 篇覆盖不同类目反而比 10 篇同类更可信\n\n### Phase 1: Mode 分流\n\n读用户输入，识别：\n\n**含具体名词 + 情绪 / 经历词**（\"我昨天开会...\" / \"我看到 X 让我...\" / \"我对 Y 觉得...\"）→ **Mode A**（深挖；如话题是时事，Phase 2A.5 询问要不要拉外部数据）\n\n**含方向词但无具体内容**（\"想做职场\" / \"AI 方向\" / \"婚恋\"）→ **Mode B**（单问\"为什么想做这个\"——用户内省窗口，**不调任何热点工具**）\n\n**显式说没想法**（\"不知道做什么\" / \"帮我想\" / \"随便给个\"）→ **Mode C**（按 [data-source-routing.md](../../shared-references/data-source-routing.md) 调热点工具 + 用户挑 + 回到内省；都不行则走\"聊经历\"三选项兜底）\n\n**显式 `--batch N`**（用户主动批量）→ **Batch Mode**\n\n**纯 `/cheat-seed` 无附加内容** → **询问入口问题**：\n\n```\n你今天想干嘛？\n\n- 有想做的主题 / 经历 → 直接告诉我（\"我想做一条 X\" / \"我最近 X...\"）\n- 知道大致方向 → 告诉我（\"想做职场\" / \"AI 方向\"）→ 我会单问你为啥想做\n- 完全没想法 → 说\"帮我想\"→ 我用 [aihot / trendradar] 拉今天的热点给你看\n- 批量搞定 → 说 \"batch <N>\"\n\n(我不会拿一堆开放问题追你——你给一句话我就开始)\n```\n\n注意这是**唯一的开放式问题**——只在用户**纯触发** `/cheat-seed` 时问。如果用户已经在触发词里给了内容（\"/cheat-seed 我想做...\" 或 \"找选题 我最近开会...\"），直接进 Mode A/B/C 不再问这一句。\n\n### Phase 2A: Mode A 深挖（用户给了具体 topic / 经历）\n\n**核心原则**：围绕用户给的内容**深挖**，**不要切到别的话题**。\n\n**反问类型（按场景挑）**：\n\n- 触发瞬间：\"你说 X 这件事，最初是哪个具体瞬间触发你想做的？\" / \"是什么让你觉得这值得讲一条视频？\"\n- 情绪锚点：\"这里面最让你 [生气 / 觉得荒唐 / 觉得有意思] 的是哪个细节？\"\n- 角度选择：\"你想说的是 [角度 a：现象批判] 还是 [角度 b：自我反思] 还是 [角度 c：泛化到普遍]？\"\n- 受众想象：\"你心里想着是说给哪种人听？她/他听完会怎么想 / 怎么转发？\"\n- 反对意见探测：\"如果有人反驳说 [反方观点 X]，你会怎么回？\"——逼用户先想清楚立场\n\n**反问纪律**：\n- 一次只问 **1 个**问题（不要塞 3 个连珠炮）\n- 最多 `MAX_DEEP_DIVE_TURNS` 轮（默认 4）——超过就主动收敛：\"OK 我感觉够了，帮你提议一个角度试试\"\n- 用户的回答如果含 emoji / 简短 / 不耐烦 → 立刻收敛，不要逼\n\n**收敛输出**：\n\n```\n我感觉这个角度能做：\n\n[一句话立意：50 字以内]\n\n走法：\n- 用 [Pattern X 结构]（来自 script_patterns.md）\n- 钩子：[具体场景 / 句子]\n- 主体：[3 个观察是什么]\n- 收尾：[MVP 句方向]\n\n粗打分（v0 等权 7 维）：ER=X HP=X QL=X NA=X AB=X SR=X SAT=X → composite ≈ X.X\nConfidence: 🔴 极低 (你才校准 0/N 篇)\n\n要不要让我先写一份 draft？(yes / 换角度 / 我自己写)\n```\n\n用户回 yes → Phase 4 写 draft。\n用户说\"换角度\" → 回 Phase 2A 深挖更多。\n用户说\"我自己写\" → 把 candidate 加进 candidates.md 标 tier1，结束。\n\n### Phase 2B: Mode B — 单问\"为什么\"，触发用户内省\n\n用户给了方向但不具体（\"想做职场\" / \"AI 方向\" / \"婚恋\"）。**这阶段不调任何热点工具**——是用户内省的窗口，外部信息会污染。\n\n只问一个问题，**单刀直入**：\n\n```\n为什么想做这个主题？\n```\n\n不要问\"a/b/c 三种例子\"——那是 dump 选项让用户挑，破坏内省。让用户自己组织语言。\n\n**根据用户回答分流**：\n\n| 用户答 | 分类 | 行为 |\n|---|---|---|\n| 含具体经历 / 个人卡点（\"我自己经常加班\" / \"我看到 X 让我...\"） | **真动机** | 转 Mode A 深挖（Phase 2A） |\n| 抽象热度归因（\"这话题最近热\" / \"别人都在做\" / \"听说能涨粉\"） | **空动机** | 反问\"那你自己对这个话题最有感觉的角度是啥？\"——逼出个人 stake；继续空 → 转 Mode C |\n| \"我也不知道\" / \"朋友说赚钱\" / 模糊推搡 | **真没想法** | 直接转 Mode C |\n\n**反问纪律**：最多 2 轮。第 2 轮还问不出真动机 → 直接转 Mode C，**不要无限挖**。\n\n> 设计意图：Mode B 是\"过滤器\"，不是\"工厂\"。用户来这里要么暴露真实动机（→ 进 Mode A）要么暴露自己其实没想法（→ 进 Mode C）。两条都比硬要在 Mode B 里产出选题更好。\n\n### Phase 2C: Mode C — 外部素材 + 用户挑 + 回到内省\n\n用户完全没想法（直接显式说 / 从 Mode B 转过来）。**这是唯一默认调热点工具的入口**。\n\n按 [shared-references/data-source-routing.md](../../shared-references/data-source-routing.md) 的路由规则：\n\n1. **拉外部素材**（按 `content_form` 选 trend source）：\n   - `tutorial-builder` / AI 类 → 调 aihot skill\n   - `opinion-video` / `long-essay` / `podcast` / `other` → 调 trendradar-mcp（如启用）\n   - `mixed` → 两个都调\n   - 都不可用 → 走 manual-paste（询问用户：\"今天看到啥可以拍的？粘几条 URL/标题\"）\n\n2. **聊经历兜底**（用户拒绝看外部素材 / 外部素材都不感兴趣）：\n\n   ```\n   外部素材你都没感觉，那回到你自己。三个开口，挑一个开始讲：\n\n   a) 你最近真实接触到的某件具体事？（\"上周我看到我同事 X...\"）\n   b) 你最近读到 / 看到的某条让你想吐槽的内容？（\"知乎上有个回答...\"）\n   c) 你长期琢磨的某个 unsolved 困惑？（\"我一直没想明白为啥 X...\"）\n\n   随便挑一个开始讲。\n   ```\n\n3. **拉到外部素材后**，用 rubric 粗筛 + 按 content_form 过滤，留 5 条最契合的：\n\n   ```\n   今天这 5 个跟你形态契合：\n   1. [标题 A]（来源: trendradar / 微博热搜 / hot_score: 8.5）\n   2. [标题 B]（来源: aihot / 模型类 / 精选）\n   3. ...\n\n   你哪个最有感觉？没感觉就回 '都没'，我换方向问。\n   ```\n\n4. **用户挑了一条 → 回到内省**：\n\n   ```\n   OK [标题 X]。你为啥对这条最有感觉？\n   是 [angle1] 还是 [angle2] 还是别的？\n   ```\n\n   → 用户答 → 转 Mode A 深挖。\n\n5. **用户回 \"都没\"** → 转回 Mode C 第 2 步的\"聊经历兜底\"。\n\n**关键**：热点不是\"塞 5 候选让用户挑\"，是\"给材料 + 强制问用户的个人 stake\"——AI 不替用户决定哪条最值得做。\n\n### Phase 2A.5: Mode A 灰色场景 — 用户讲了时事话题\n\nMode A 默认深挖用户经历。但如果**用户讲的本身是时事话题**（产品名 + 时间词 / 人名 + 事件词），按 [data-source-routing.md](../../shared-references/data-source-routing.md) 的\"时事判定\"规则，**询问**用户要不要拉外部数据作参考：\n\n```\n💡 [话题] 是时事——我可以拉一下今天的舆论风向（各平台情绪 + 主要 angles）作参考。\n\n要看吗？回 '看' 我调；回 '不用' 我直接跟你深挖你的角度。\n```\n\n用户回 \"看\" → 调对应 trend source → 把数据 inline 到深挖 context；\n用户回 \"不用\" → 标准 Mode A 深挖，不动外部数据。\n\n**永远不主动调**——用户的 angle 优先于外部数据，避免外部信息**带偏**用户视角。\n\n### Phase 2D: Batch Mode（用户显式 `--batch N`）\n\n**保留旧 brainstorm 流程**：\n\n1. 问 3 个清单问题（兴趣 / 调性 / 红线）—— Batch 模式才问这些\n2. 抓热点 + Claude brainstorm 15 候选\n3. 用户挑 N\n4. 写 N 份 draft 到 scripts/——**每份都走 Phase 4 的段落版格式 + Phase 4.5 自检**（line-format + humanizer），不因为批量就跳过\n\n详见 commit history（旧 cheat-seed 的 Phase 1-3）。这是 escape hatch，不是默认。\n\n### Phase 3: 计算 candidate ID + 落候选池\n\n不管 Mode A/B/C 哪条路径，确认角度后：\n\n1. 算 candidate id：`sha256(\"seed-\" + 立意 + 触发时间)[:12]`\n2. 写一行 entry 到 `candidates.md`（按 [candidate-schema.md](../../shared-references/candidate-schema.md) 格式）\n3. 标 `tier=tier1` + `read_status=deep_read`（已经讨论过，不是 skim）\n\n### Phase 4: 写 draft\n\n`WITH_DRAFT=yes` → 顺次写到 `scripts/<YYYY-MM-DD>_<id>_<short-title>.md`：\n\n**写 draft 前必读** `script_patterns.md` —— 按\"结构选型 cheat sheet\"对应用户的 topic 选合适结构。如果文件还在抽象骨架阶段（用户没填几个 pattern），就用 starter rubric 对应的通用框架。\n\n**字数**：按 `DRAFT_LENGTH` 派生（基于 `typical_duration_seconds`）。\n\n#### ⚠️ 正文必须是段落版，不是字幕格式（**最常见的生成跑偏**）\n\n模型的训练先验会把\"视频脚本\"默认写成提词器/字幕的短行格式。**这是错的**——cheat-seed 的 draft 是给用户**改写**的散文稿，不是拍摄字幕。字幕是剪映拍后自动断的，不是写作时的形态。\n\n生成正文时，眼睛盯住下面这个对照：\n\n```\n❌ 字幕格式（不要这样写）：\n你有没有发现\n所有审稿人都在说一样的话\n你的研究太老套了\n但你仔细看\n他们引用的全是 5 年前的反应\n\n✅ 段落版（必须这样写）：\n你有没有发现，所有审稿人都在说一样的话——你的研究太老套了。但你仔细看，他们引用的全是 5 年前的反应。AI 不是新东西，新的是这次大家集体觉醒了。\n```\n\n规则：\n- **每段 100-300 字**，逗号 / 句号 / 破折号自然连，**不在句子边界硬断行**\n- 段与段之间空一行（自然的主题切换才换段）\n- 一份 draft 正文一般 3-6 段，**不该有几十个单句行**\n\n#### 格式：\n\n```markdown\n# [立意标题]\n\n> ⚠️ **Draft by Claude — 你必须改写后再拍**\n>\n> 这是脚手架，不是成品。你的语气 / 节奏 / 个人经历无法 AI 生成。\n> 改写流程：\n> 1. **直接在本文件改写**（同 path：scripts/<...>.md）\n>    - 加你的语气、个人经历、真实金句\n>    - 砍铺垫、砍模型缩写、砍学术包装\n> 2. 改完后跑 `/cheat-predict scripts/<本文件>.md`\n> 3. 拍完跑 `/cheat-shoot scripts/<本文件>.md`\n\n**Article ID**: <12 位 hash>\n**调性**: [基于讨论得出的，不是清单 Q]\n**目标时长**: <state.typical_duration_seconds 转换> 分钟\n**目标字数**: <按时长派生>\n**结构选型**: [按 script_patterns.md 的 cheat sheet 显式标，如 \"metaphor 优先\" / \"数据反转开场\"]\n**用到的 patterns**: [编号 + 简短说明]\n**讨论种子**: [一句话回顾 deep dive 出来的核心]\n\n---\n\n[draft 正文 —— **段落版**，3-6 段，每段 100-300 字，不是单句碎行]\n```\n\n`WITH_DRAFT=no`（用户说\"我自己写\"）→ 跳过 Phase 4 + Phase 4.5。\n\n### Phase 4.5: draft 自检 pass（版式 + 去 AI 味）\n\ndraft 写完落盘后、**在展示给用户前**跑两步自检。**顺序固定：先 4.5a 修版式，再 4.5b 去 AI 味**——humanizer 处理散文，喂它字幕格式的碎行会乱。\n\n**为什么安全**（不污染校准）：cheat-seed 的 draft 不是被预测/发布的东西——用户改写后、cheat-predict 打分的是**用户最终稿**。这两步只是给用户更干净的起点。\n\n#### Phase 4.5a: line-format 自检（字幕格式 → 段落版）\n\nPhase 4 的散文指令 + ❌/✅ 对照已经在生成时压先验，但生成仍可能跑偏。这一步是**确定性兜底**：\n\n```bash\n# 只看正文段（--- 分隔线之后）\nbody=$(awk '/^---$/{f=1;next} f' scripts/<id>.md)\nline_count=$(printf '%s\\n' \"$body\" | grep -c .)        # 非空行数\nchar_count=$(printf '%s' \"$body\" | wc -m | tr -d ' ')\navg_chars_per_line=$(( char_count / (line_count > 0 ? line_count : 1) ))\n```\n\n判定：**`avg_chars_per_line < 15` 且 `line_count >= 8`** → 判定为字幕格式 → **自动重排**：\n- 把句子边界的硬断行合并回自然段落\n- 按主题切换分 3-6 段，每段 100-300 字\n- 用 Edit 替换正文段（header 不动）\n- 在 Phase 5 输出里标一行：\"📐 检测到字幕格式，已重排为段落版\"\n\n不命中 → 跳过，正文已经是段落版。\n\n#### Phase 4.5b: humanizer 去 AI 味\n\n`HUMANIZE_DRAFT=on`（默认）—— 用 `humanizer` skill 过一遍。Claude 自己写的初稿天然带 AI tells（em-dash 滥用 / rule of three / \"inflated\" 词汇 / 空泛归因 / -ing 浅层分析），这一步把它们清掉。\n\n步骤：\n\n1. 检查 `humanizer` skill 是否可用（`~/.claude/skills/humanizer/` 存在）：\n   - 不可用 → 跳过 4.5b，在 Phase 5 输出里加一行\"（humanizer 未装，draft 是原始 AI 版——`git clone https://github.com/blader/humanizer` 到 ~/.claude/skills/ 可启用自动去 AI 味）\"\n2. 可用 → 通过 Skill tool 调 `humanizer`，**只传 draft 正文**（`---` 分隔线之后、4.5a 已重排好的段落版），**绝不传 header**：\n   - header 的 `⚠️ Draft by Claude — 你必须改写后再拍` 警告是**有意的脚手架标记**，不是要 humanize 的散文\n   - **voice calibration**：如果用户有历史脚本（`videos/*/script.md`）或填过 `script_patterns.md`，把最近 1-2 份作为 humanizer 的 voice 参考样本一起传——让它往\"**这个用户的声音**\"靠，而不是\"通用人声\"\n3. humanizer 返回去 AI 味的正文 → 用 Edit 替换 draft 文件的正文段（header 不动）\n4. 记录 humanizer 报告的\"修了哪些 tell\"（如 `em-dash 滥用 ×3 / rule of three ×2 / inflated 词汇: \"深刻\" \"本质上\"`），Phase 5 输出里展示\n\n**纪律**：\n- humanizer 是**去 AI 味**，不是**替用户改写**。它让 draft 不那么像机器写的，但**仍不是用户的声音**——header 的\"必须改写\"警告依然成立，Phase 5 输出要重申\n- 如果 humanizer 把某句改得偏离了 `结构选型` / 用到的 pattern → 以 pattern 为准，那句回滚（pattern 是和用户讨论定的，humanizer 不该推翻）\n- humanizer **不负责版式**——断行问题在 4.5a 已经修完，humanizer 拿到的已是段落版\n\n### Phase 5: 输出\"下一步\" + 询问继续\n\n```\n✅ Draft 写完：scripts/2026-05-04_<id>_<short>.md\n📐 版式自检：通过（段落版）  ← 或\"检测到字幕格式，已重排为段落版\"\n🧹 humanizer 过了一遍：修了 em-dash 滥用 ×3 / rule of three ×2 / inflated 词汇 2 处\n   （draft 现在不那么\"机器味\"了——但这仍是脚手架，不是你的声音）\n\n接下来你可以：\n- 改写这份 draft（直接在原文件改）—— 加你的语气、经历、真实金句\n- 改完跑 \"打分这篇 scripts/<...>.md\" 看 7 维评分\n- 决定要拍 → \"启动预测 scripts/<...>.md\"\n\n下一篇你想做什么？\n（直接告诉我具体经历 / topic，或者说\"今天就这样\"结束）\n```\n\n> humanizer 那行只在 `HUMANIZE_DRAFT=on` 且 skill 可用时出现。未装时换成一行提示如何启用。\n\n用户说\"今天就这样\" → 结束 cheat-seed。\n用户给新 topic → 回 Phase 1 重新分流。\n\n## Key Rules\n\n1. **AI 不主动开放问**——只在用户纯触发 `/cheat-seed` 时问一次入口问题，其他时候**等用户给输入再深挖**\n2. **一次一个选题**——默认 Mode A/B/C 都给 1 个建议；用户主动要批量才走 Batch\n3. **反问纪律**：一次问 1 个，最多 4 轮，用户不耐烦立刻收敛\n4. **深挖围绕用户给的话题**，不要切到别的——你说\"开会被领导骂\"，AI 不该问\"那你最近有没有觉得 AI 让大家...\"这种平行话题\n5. **写 draft 必须读 script_patterns.md**——按用户已有 pattern 选结构\n6. **draft 是脚手架**——header 加醒目警告\"必须改写\"\n7. **humanizer 只去 AI 味，不替用户改写**——Phase 4.5b 让 draft 不那么机器味，但它仍不是用户的声音；\"必须改写\"的警告不因为过了 humanizer 就失效\n8. **正文是段落版不是字幕格式**——生成时盯 Phase 4 的 ❌/✅ 对照；Phase 4.5a 用 `avg_chars_per_line < 15 且行数 ≥ 8` 做确定性兜底，命中就重排。字幕是剪映拍后自动断的，不是写作时的形态\n\n## Refusals\n\n- 「跳过深挖，直接写 draft」 → 询问\"你想直接给主题让我写吗？OK 但 draft 质量可能差——我不知道你的角度。给我一句话立意我就写\"\n- 「AI 替我决定 topic」 → 拒绝。Mode A/B/C 路径里 AI 永远只**呈现外部素材** + **问用户角度**，不替用户拍板\"做哪条\"\n- 「Mode B 我懒得回答为什么，直接给我 5 个候选吧」 → 拒绝。Mode B 的\"为什么\"是过滤器——你答不出来就不该用 Mode B 的方向。要么进 Mode A 给具体经历，要么进 Mode C 我帮你找素材\n- 「Mode A 时直接帮我拉热点，不问我同意」 → 拒绝。Mode A 用户已经有 angle，未经允许拉外部数据会污染他的视角。详见 [data-source-routing.md](../../shared-references/data-source-routing.md)\n- 「一次写 5 个 draft」 → 不在默认流程。用户必须显式 `--batch 5`\n- 「我懒得改写，直接拍 AI draft」 → 警告\"AI 直接生成的稿子拍出来 ER 偏低，会污染你的校准数据\"，但用户坚持也允许（标 `unmodified_ai_draft: true`）\n\n## Integration\n\n- 上游：`/cheat-init` Phase 5 末尾在 `pool_status=none + calibration_samples=0` 时主动询问\"现在跑 /cheat-seed？\"\n- 上游：`/cheat-recommend` 在 candidates 空时引导文案中提及 `/cheat-seed`\n- 上游：`/cheat-status` 在 `pool_status=none + 距 init >24h` 时提示\"还没拍——跑 /cheat-seed？\"\n- 下游：用户的 candidate → candidates.md（tier1，已 deep_read）\n- 下游：（默认）draft → Phase 4.5 humanizer 去 AI 味 → scripts/<id>.md → 用户改写 → /cheat-predict\n- 可选依赖：[`humanizer`](https://github.com/blader/humanizer) skill（MIT，外部项目）。装在 `~/.claude/skills/humanizer/` 时 Phase 4.5 自动启用；未装则优雅跳过。**不打包进 cheat-on-content**——用户自己 clone\n- 与 `/cheat-trends` 区别：cheat-seed 是**讨论 + 写 draft**（重 conversation）；cheat-trends 是**多 adapter 抓 + 粗打分**（重 fetch）。两者目的不同，不互相替代。\n","skills/cheat-shoot/SKILL.md":"---\nname: cheat-shoot\ndescription: 登记一条视频已拍摄。**建 video folder + 询问实际拍摄稿是否与 scripts/<id>.md 一致 + buffer +1**。与 cheat-publish 配对：拍了进队列，发了出队列。触发词：\"拍了\"/\"拍了 X\"/\"shot\"/\"shot it\"/\"已拍 X\"/\"录完了\"。\nargument-hint: <scripts-path-or-id>\nallowed-tools: Bash(*), Read, Write, Edit, Glob\n---\n\n# /cheat-shoot — 登记拍摄完成 + 建 video folder + (改稿则) 触发 v2 预测\n\n把视频从\"已写预测、未拍摄\"状态推进到\"已拍摄、未发布\"状态。这一步：\n1. **建 `videos/<同 id>/`** 目录（之前没有的话）\n2. **询问用户**：\"实际拍摄时用的稿子和 `scripts/<id>.md` 一致吗？\"\n3. 算 diff——超过 V2_TRIGGER_THRESHOLD (默认 30%) → **delegate 到 `/cheat-predict — mode: v2`** 在原 prediction 文件 append `## 预测 v2` 段\n4. 把 video folder 加进 state.shoots 队列，buffer +1\n\ncheat-shoot 自己**不**写预测内容——所有预测落盘逻辑在 cheat-predict。cheat-shoot 只负责检测改稿 + 派发。\n\n为什么单独一个 skill：\n- buffer 警戒系统需要明确区分\"拍了\" vs \"发了\"。视频可以批量拍（一天拍 5 条），分散发（每天发 1 条）\n- \"实际拍摄稿\" ≠ \"pre-shoot 草稿\"是常态。这一步是把 diff 显式化、触发 v2 重判、采集\"用户改稿 pattern\"信号的入口\n- v2 预测 vs v1 预测的差异本身就是 rubric 升级证据——比如 v1 给 ER=4，v2 给 ER=5（用户改稿改高了 hook 强度），就告诉 rubric \"这个用户的 ER 阈值跟我现在公式不一致\"\n\n## Overview\n\n```\n[用户：拍了 scripts/2026-05-04_abc123_停止期待.md]\n  ↓\n[Phase 0: 解析路径 + 验证 prediction 已存在]\n  ↓\n[Phase 1: 检查是否已登记（避免重复）]\n  ↓\n[Phase 2: 建 videos/<id>/ + 询问\"实际拍摄稿一致吗？\"]\n  ↓\n[Phase 3: 写 videos/<id>/script.md]\n  ↓\n[Phase 4: append state.shoots]\n  ↓\n[Phase 5: 输出 buffer 状态]\n```\n\n## Constants\n\n- **REQUIRE_PREDICTION = true** — 拍前必须先有 v1 prediction 文件\n- **V2_TRIGGER_THRESHOLD = 0.30** — normalize 后 char-level diff 超过 30% → 默认建议 v2 重判；低于 30% 询问用户是否仍要 v2\n- **DIFF_METRIC = char_levenshtein_normalized**（**默认**）—— 通过 [`tools/diff_pct.py`](../../tools/diff_pct.py) 调用：先 normalize（去 markdown header / 分隔线 / 列表标记 / 装饰标点 / 折叠所有空白），再算 char-level Levenshtein / max(len_a, len_b)。preferred backend `rapidfuzz`，fallback `difflib.SequenceMatcher`（stdlib，永远可用）。**旧版 line-level 在口语化转录场景误报严重**（draft 长 markdown 句 vs whisper 转录的短断句，内容几乎不变但 line-level 算出 ~200% diff）—— PR #14 修复\n- **DIFF_METRIC=lines** —— legacy fallback：当 python3 完全不可用或 tools/diff_pct.py 找不到时降级到 `diff -u | grep '^[+-]' | wc -l` 算法\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<scripts-path-or-id>` | 用户参数；缺失则询问 |\n| `.cheat-state.json` | 状态文件 |\n| `scripts/*.md` | pre-shoot 草稿 |\n| `predictions/*.md` | 验证对应预测存在 |\n\n## Workflow\n\n### Phase 0：解析 + 验证\n\n1. 解析用户给的路径——支持几种形态：\n   - 完整路径 `scripts/2026-05-04_abc123_停止期待.md`\n   - 简写 `2026-05-04_abc123_停止期待`\n   - id 简写 `abc123` → glob `scripts/*_abc123_*.md` 找匹配\n2. 验证 `scripts/<id>.md` 存在：不存在 → 报错\"找不到 pre-shoot 草稿\"\n3. 验证有对应 prediction `predictions/<同名>.md`：\n   - 不存在 → **拒绝登记**，提示\"先跑 /cheat-predict 写预测，否则违反盲预测原则——你不能拍完才写预测，那等于事后看了画面写\"\n   - 存在 → 通过\n\n### Phase 1：检查重复\n\n读 `.cheat-state.json`，检查 `shoots[]` 是否已含此 id：\n- 已存在 → 警告\"已登记过（X 天前）。是要重新登记，还是要用 /cheat-publish 发布？\"\n- 不存在 → 进入 Phase 2\n\n### Phase 2：建 video folder + 询问稿子一致性\n\n1. 建目录 `videos/<id>_<short>/`（同 scripts/ + predictions/ 的命名）\n2. **询问用户**：\n\n```\n拍 「<title>」 的时候，你实际用的稿子和 scripts/<id>.md 一致吗？\n\na) 一致——按草稿拍的\nb) 改了一些——你能给我看下实际拍摄稿吗？我重新打分一次（v2 预测）\nc) 大改了，基本是另一条 → 走 _redo 流程：\n   scripts/<id>_redo.md → 重新 cheat-predict → 再 cheat-shoot（原 prediction 留档脱钩）\n```\n\n### Phase 3：写 videos/<id>/script.md + (b 路径) 触发 v2 预测\n\n**a 路径（一致）**：\n- `cp scripts/<id>.md → videos/<id>/script.md`\n- `script_consistency = consistent`\n- 不重判，进 Phase 4\n\n**b 路径（改了）**：\n1. 询问用户实际拍摄稿——粘贴文本 / 文件路径 / 转录文件\n2. 若用户提供 → 写入 `videos/<id>/script.md`\n3. 若用户没保留（即兴）→ 标 `script_lost`，写占位文件 + 警告\"v2 重判跳过——下次建议留稿（哪怕 voice memo 转录）\"，进 Phase 4\n4. 提供了的话：算 diff\n   ```bash\n   # 解析 cheat-on-content 源码根（cheat-shoot 是 symlink 装的）\n   SKILL_REAL=\"$(readlink -f ~/.claude/skills/cheat-shoot 2>/dev/null || readlink ~/.claude/skills/cheat-shoot 2>/dev/null)\"\n   if [[ -n \"$SKILL_REAL\" ]]; then\n     REPO_ROOT=\"$(cd \"$SKILL_REAL/../..\" && pwd)\"\n     DIFF_TOOL=\"$REPO_ROOT/tools/diff_pct.py\"\n   fi\n\n   if [[ -n \"${DIFF_TOOL:-}\" && -f \"$DIFF_TOOL\" ]] && command -v python3 >/dev/null 2>&1; then\n     # 默认 char-level Levenshtein on normalized text（rapidfuzz preferred, difflib fallback）\n     diff_pct=$(python3 \"$DIFF_TOOL\" \"scripts/<id>.md\" \"videos/<id>/script.md\")\n   else\n     # legacy line-level fallback——只在 python3 或 diff_pct.py 都不可用时用\n     added=$(diff -u scripts/<id>.md videos/<id>/script.md | grep -c '^+')\n     removed=$(diff -u scripts/<id>.md videos/<id>/script.md | grep -c '^-')\n     total_orig=$(wc -l < scripts/<id>.md)\n     diff_pct=$(( (added + removed) * 100 / total_orig ))\n     echo \"⚠️  fallback 到 line-level diff——口语化转录会 inflate diff_pct，可能误触发 v2\"\n   fi\n   ```\n\n   **为什么 normalize + char-level**：line-level diff 在创作者真实场景（draft 是 markdown 长句、拍摄稿是 whisper 转录的口语化短行）算出 ~200% 差异但内容几乎不变。char-level Levenshtein 在 normalize 后稳定反映**内容**差异，而非格式差异。详见 [`tools/diff_pct.py`](../../tools/diff_pct.py) + `tools/diff_pct_test.sh`（3 fixture 在两个 backend 上全过）。\n5. **判定 v2 触发**：\n   - `diff_pct >= 30` → 默认建议 v2 重判，**主动调用** `/cheat-predict — mode: v2 — prediction-file: predictions/<id>.md` 传 `videos/<id>/script.md` 作 input。cheat-predict 走 v2 模式 append `## 预测 v2`\n   - `diff_pct < 30` → 询问用户：\"只改了 N% 的内容，要重判吗？默认不（v1 预测仍有效）\"。用户说要 → 同上调用；用户说不 → 跳过 v2，继续 Phase 4\n6. cheat-predict 完成 v2 落盘后，控制权回到 cheat-shoot 进 Phase 4\n\n**c 路径（大改）**：\n- 不写 `videos/<id>/script.md`，提示走 `_redo` 流程\n- 退出 cheat-shoot（不进 Phase 4）\n\n### Phase 4：state 更新\n\n```json\n{\n  \"shoots\": [\n    ...,\n    {\n      \"video_folder\": \"videos/2026-05-04_abc123_停止期待/\",\n      \"prediction_file\": \"predictions/2026-05-04_abc123_停止期待.md\",\n      \"scripts_path\": \"scripts/2026-05-04_abc123_停止期待.md\",\n      \"shot_at\": \"<ISO timestamp>\",\n      \"script_consistency\": \"consistent\" | \"modified\" | \"lost\",\n      \"script_diff_pct\": <0-100 int 或 null>,\n      \"v2_prediction_written\": <true/false>,\n      \"script_hash_at_shoot\": \"<sha256:12 of videos/<id>/script.md>\"\n    }\n  ]\n}\n```\n\n`v2_prediction_written: true` 表示 prediction 文件里现在有 `## 预测 v2` 段，cheat-retro 应读 v2 算偏差；`false` 表示沿用 v1。\n\n### Phase 5：输出 buffer 状态\n\n读完 state 后立即算 buffer + 颜色（按 [cadence-protocol.md](../../shared-references/cadence-protocol.md) 的派生规则）：\n\n```\n✅ 已登记拍摄：videos/2026-05-04_abc123_停止期待/\n   预测文件：predictions/2026-05-04_abc123_停止期待.md\n\n📦 当前 buffer：3 篇（🟢 绿色，正常）\n   按你的 cadence（隔日更）= 6 天 buffer，节奏稳定。\n\n下一步：拍其他候选 / 等下个发布日 / 不动\n```\n\n如果 buffer 颜色变了（如从绿到蓝）→ 高亮提醒：\n```\n📦 当前 buffer：6 篇（🔵 蓝色，**积压**）\n⚠️  建议暂停拍摄，全力发布存货 + 复盘。\n   按你的 cadence（日更）= 6 天预备，已超过健康上限。\n```\n\n## Key Rules\n\n1. **不写 prediction**——拍了 ≠ 发了。预测在 /cheat-predict 锁，拍只是事件\n2. **不动 video folder 内容**——script.md / draft-v0.md 都不改\n3. **必须先有 prediction**——否则违反盲预测（拍完看了画面再写预测 = 数据泄漏到判断）\n4. **buffer 计算实时**——每次 shoot / publish 后立刻重算，state.shoots 是真值\n5. **支持批量**：用户可以一天连说 \"拍了 X / 拍了 Y / 拍了 Z\" 三次连续登记\n\n## Refusals\n\n- 「拍了 X，但我从来没跑过 cheat-predict」 → 拒绝。v1 预测**必须拍前写**——拍完才写预测会被画面诱导事后修改。请先 /cheat-predict 写 v1 再来 /cheat-shoot。（v2 重判是另一回事——v1 已存在 + 拍后改稿才允许）\n- 「我没有 video folder，我直接拍的」 → 询问用户 → 帮他建 video folder + 提示下次走完整流程；登记时标 `ad_hoc: true`\n- 「我改稿了但你直接覆盖 v1 吧，别留 v2 段」 → 拒绝。v1 是档案，v2 才是当前判断——append 不覆盖。两段一起留是 rubric 学习的关键证据\n\n## Integration\n\n- 上游：`/cheat-predict` 写完 prediction → 用户拍摄 → `/cheat-shoot` 登记\n- 下游：`/cheat-publish` 发布时把对应项从 state.shoots 移除\n- `/cheat-status` 看板的 buffer 数字直接来自 `state.shoots.length`\n- `/cheat-recommend` 看 buffer 颜色调推荐策略\n- SessionStart hook 看 buffer 颜色决定报告第一行\n\n## state.shoots 数据结构\n\n```json\n{\n  \"shoots\": [\n    {\n      \"video_folder\": \"videos/2026-05-04_abc123_停止期待/\",\n      \"prediction_file\": \"predictions/2026-05-04_abc123_停止期待.md\",\n      \"shot_at\": \"2026-05-04T18:30:00+08:00\",\n      \"ad_hoc\": false  // true if user shot without going through full flow\n    }\n  ]\n}\n```\n\n按 `shot_at` 升序——最早拍的在前面。`/cheat-status` 显示最早一项的 days-since-shoot 警告（避免有视频拍了 30 天没发）。\n","skills/cheat-status/SKILL.md":"---\nname: cheat-status\ndescription: cheat-on-content 的状态看板。显示当前模式 / rubric 版本 / 校准进度 / 待复盘 / pool 状态 / 是否该升级 SQLite / 是否该 bump rubric。**任何时候都可调，无副作用**。触发词：\"状态\"/\"看板\"/\"status\"/\"我现在该做什么\"/\"进度怎么样\"。\nallowed-tools: Bash(*), Read, Glob, Grep\n---\n\n# /cheat-status — 状态看板\n\n读 state file + 扫描用户项目 → 汇总当前进度 → 输出\"今天该做什么\"清单。\n\n## Overview\n\n```\n[用户：状态]\n  ↓\n[Phase 1: 读 .cheat-state.json + 扫文件系统]\n  ↓\n[Phase 2: 计算派生指标]\n  ↓\n[Phase 3: 检测建议触发器（升级 / bump / 清算）]\n  ↓\n[Phase 4: 输出看板]\n```\n\n## Constants\n\n- **SQLITE_UPGRADE_THRESHOLD = 30** — calibration_samples 达到 N 时建议升 SQLite\n- **CLEANUP_LINE_THRESHOLD = 600** — rubric_notes.md 行数超 N 时建议清算\n- **STALE_PREDICTION_DAYS = 30** — in_progress prediction 超 N 天未发布提示清理\n\n## Inputs\n\n| 来源 | 用途 |\n|---|---|\n| `.cheat-state.json` | 主要状态 |\n| `predictions/*.md` | 校准样本数 / pending retros |\n| `candidates.md` | 候选池规模 |\n| `rubric_notes.md` | 行数 / 当前版本 |\n| `.cheat-cache/usage.jsonl`（如有） | meta-logging 数据，用于\"距上次 bump 多少次预测\" |\n\n## Workflow\n\n### Phase 1: 读状态\n\n```python\nstate = read_json('.cheat-state.json')\nif not state:\n    return \"你还没初始化。请先跑 /cheat-init。\"\n\npredictions = glob('predictions/*.md')\ncandidates_count = parse_candidates_md_entries()\nrubric_lines = wc -l rubric_notes.md\n```\n\n### Phase 2: 派生指标\n\n| 指标 | 算法 |\n|---|---|\n| **Buffer 数** | `len(state.shoots)` |\n| **Buffer 颜色** | 按 [cadence-protocol.md](../../shared-references/cadence-protocol.md) 派生：`buffer_days = buffer_count × target_publish_cadence_days`，`<1 红 / 1-2 橙 / 3-5 绿 / >5 蓝`。如 `target_publish_cadence_days=null` → 颜色禁用 |\n| **Confidence 等级** | 按 [state-management.md confidence 表](../../shared-references/state-management.md) 派生：从 `calibration_samples` 整数派生 emoji + 标签 |\n| **最早一拍至今天数** | `now - state.shoots[0].shot_at`，用于警告\"拍了 N 天没发\" |\n| 校准样本数 | predictions 中含完整复盘段（实绩数据非空）的文件数 |\n| 待复盘 | state.pending_retros 中已过 RETRO_WINDOW_DAYS 的 |\n| 池大小 | candidates.md 中 tier!=skip 的 entry 数 |\n| 上次 bump 至今几次预测 | predictions 中 published_at > state.last_bump_at 的数量 |\n| 同向偏差队列 | state.consecutive_directional_errors |\n| in_progress 陈旧度 | now - state.in_progress_session.started_at（如有） |\n\n### Phase 3: 检测建议触发器\n\n按优先级（高→低）逐项检查：\n\n1. **Buffer 颜色 = 🔴 红** → 第一行高优先级警戒：\"buffer 已 0/1 篇，下个发布日可能断更——今天必须拍 ≥1 条。说'推荐选题'我只推 top 1 稳分（不推实验性）\"\n2. **Buffer 颜色 = 🔵 蓝** → 高优先级提示：\"buffer 已 N 篇积压。**暂停拍摄**，先发存货 + 复盘。说'已发布 ...'我帮你出队\"\n3. **state.shoots 中最早一项 shot_at > 14 天** → \"你有视频拍了 N 天还没发——议题时效流失风险，建议尽快发或弃稿\"\n4. **in_progress 陈旧** (>= STALE_PREDICTION_DAYS) → 高优先级提示\"清理或 publish\"\n5. **待复盘 ≥ 1** → 高优先级\"今天该复盘 X 篇\"\n6. **`pool_status=none` + `calibration_samples=0` + 距 init >24h** → \"🌱 你 init 完已经 N 天但还没拍——是因为没选题吗？跑 /cheat-seed 5 分钟拿 5 个候选 + 5 个 draft\" 高优先级\n7. **Claude 判断系统性偏差信号**（**不是死磕 ≥3 同向**） → 提示\"建议跑 /cheat-bump\"\n   - **默认参考**：连续 ≥3 次同向偏差\n   - **但 Claude 可以更早**：1 次极端偏差（≥10x）或 2 次同向 + 评论区强反向证据\n   - **也可以更晚**：3 次同向但每次幅度都 <25%（可能只是噪声）\n   - 提示时显式标注：\"本次是 [default-aligned] / [judgment-driven]\"\n8. **calibration_samples 跨入新 confidence 等级**（0→1, 2→3, 5→6, 10→11, 20→21）→ 提示\"🎉 confidence 升级：<旧等级> → <新等级>。bucket 中枢精度从 ±X% 提到 ±Y%\"。**仅作通知，无任何用户必须确认的操作**——所有 skill 都已经按 calibration_samples 自动调整\n9. **calibration_samples 跨过 5** → \"你的 rubric 形态可以第一次正式 bump 了。回顾 rubric_notes.md 看观察记录段是否有 ≥3 样本支持的 pattern → 跑 /cheat-bump\"\n10. **calibration_samples 跨过 10** → \"可以跑 /cheat-bump --bucket-only --scheme percentile 让 bucket 边界改用 percentile（永远自洽）\"\n11. **calibration_samples 跨过 SQLITE_UPGRADE_THRESHOLD** 且 data_layer=markdown → \"建议跑 tools/md-to-sqlite.py\"（planned — batch 3, not yet available）\n12. **rubric_notes.md 行数 > CLEANUP_LINE_THRESHOLD** → \"建议清算观察段（手动或下次 bump 触发）\"\n13. **calibration_samples ≥ 5 + pool_status=none** → \"可以开始建立选题池了\"\n14. **calibration_samples ≥ 15 + pool_status=none** → \"强烈建议建池：/cheat-trends 或手动建 candidates.md\"\n15. **state.hooks_installed=false** → \"你的 immutability 是君子协定，建议跑 /cheat-init 装 hook\"\n16. **state.last_bump_self_audited=true** → \"上次 bump 是自审。建议配置 mcp__llm-chat__chat 后下次 bump 走外部审\"\n17. **state.rubric_form_mismatch=true** → \"你的 content_form 不是 opinion-video，用了内置观点 rubric。前几篇预测会更不准，下次 bump 时建议自行调整权重适配你的形态\"\n18. **state.benchmark_status=pending** → \"🎯 你 init 时答应等下找对标账号但还没找。跑 /cheat-learn-from 导入 ≥3 条对标视频，工具就有 anchor 了\"\n19. **state.benchmark_status=imported + Claude 判断用户数据信号已超过 benchmark** → \"📊 你的真实数据已经成为主信号，benchmark 影响淡出\"\n   - **默认参考**：calibration_samples ≥ 10\n   - **但 Claude 可以更早**：N=5 但用户的 (打分, 实绩) 配对里出现 ≥3 条与 benchmark pattern 不一致的——说明你的账号已经走出对标的路径\n   - **也可以更晚**：N=15 但用户的样本都很相似，没足够多样性 → benchmark 仍有信号价值\n   - 提示是**通知不是 gate**——benchmark.md 永远保留作 sanity check，cheat-seed 仍可读\n\n### Phase 4: 输出看板\n\n```\n🎛️ cheat-on-content 状态（更新于 2026-05-04 15:00）\n\n内容形态：opinion-video / 时长 3-5min / cadence: 隔日更\n当前 rubric：v2 (上次 bump: 2026-04-22)\n校准样本：18 篇\nConfidence: 🟢 较高 (中枢 ±15%，rubric 形态稳定)\nBaseline: 4.2w 中位数\n\n📦 Buffer：3 篇（🟢 绿色）\n   按你的 cadence (隔日更)= 6 天 buffer，节奏稳定\n\n📊 进度条\n  [█████████████░░░░░] 18 / 30 → SQLite 升级建议门槛\n  [██████████░░░░░░░░] 18 / 10 → percentile 桶可用（已超过门槛）\n\n🎬 待办（按紧急度）\n  🚨 复盘 1 篇（已过 T+3d）\n     - predictions/2026-05-01_db063817_你已不在关系里.md（T+3d 到了）\n  ⚠️  同向偏差 3 次（high, high, high）→ 建议 /cheat-bump\n  💤 in-progress prediction 已陈旧 35 天\n     - predictions/2026-04-01_xxx.md → 是已发了忘登记？还是弃稿？\n\n🔥 候选池\n  - candidates.md: 27 条（tier1: 12, tier2: 9, tier3: 6）\n  - 距上次抓热点: 4 天 — 可以再跑 /cheat-trends\n\n📈 健康度\n  - rubric_notes.md: 412 行（健康，<600 警戒线）\n  - hooks_installed: ✅\n  - external audit configured: ❌ → 建议配 mcp__llm-chat__chat\n\n下一步建议（按推荐优先级）：\n1. /cheat-retro predictions/2026-05-01_db063817_你已不在关系里.md  ← 最紧急\n2. /cheat-bump  ← 同向偏差 3 次的处理\n3. 处理陈旧 in-progress（手动或回 \"清理 in-progress\"）\n\n完整的命令清单见主 SKILL.md。\n```\n\n输出风格：**直白、具体、可点击**。每个建议附确切的命令——用户应能 copy-paste 直接执行。\n\n## Key Rules\n\n1. **无副作用**。读多写零。任何状态修改是其他 skill 的事\n2. **不假装数据可用**。state file 字段缺失 → 显式标\"未知\"，不猜\n3. **建议带优先级**。10 个建议同时显示用户会麻木——按紧急度排\n4. **每个建议附命令**。不能只说\"该 bump 了\"——要给 `/cheat-bump --propose \"...\"` 的精确入口\n\n## Refusals\n\n- 「顺便帮我自动跑一下 retro」 → 拒绝。status 是只读，retro 是另一个动作（避免一次操作做两件事）\n- 「我不想看 rubric_notes 行数，太琐碎」 → 输出仍包含但折叠到底部\"健康度\"区——状态信息的存在让用户在出问题前可见\n\n## Integration\n\n- 上游：所有其他 skill 完成时更新 .cheat-state.json，status 是这些更新的可视化\n- 下游：每个建议都路由到具体子 skill\n- meta-logging hook（如启用） → 写 usage.jsonl，status 用它算\"距上次 X 多少次\"\n","skills/cheat-trends/SKILL.md":"---\nname: cheat-trends\ndescription: 从配置的热点源（HN / Reddit / YouTube trending / B 站热门 / 等）抓今天的热门话题，去重 + 粗打分 + 写入 candidates.md。**绝大部分人没有候选池——这是让\"我没素材\"问题在 onboarding 第二步就消失的钥匙**。触发词：\"抓热点\"/\"fetch trends\"/\"今天有什么可做的\"/\"trending now\"/\"找选题\"。\nargument-hint: \"[— sources: <comma-separated>] [— max-per: 20]\"\nallowed-tools: Bash(*), Read, Write, Edit, Glob, WebFetch, Skill\n---\n\n# /cheat-trends — 热点抓取\n\n多 adapter 模式：读各 `trend-sources` adapter 的输出 → 去重 → 粗打分 → 写入 `candidates.md`。\n\n## Overview\n\n```\n[用户：抓热点]\n  ↓\n[Phase 0: 读 .cheat-state.json 拿 enabled adapters]\n  ↓\n[Phase 1: 对每个 adapter 调 fetch]\n  ↓\n[Phase 2: normalize 到 candidate-schema]\n  ↓\n[Phase 3: 去重（vs candidates / predictions / trends-history）]\n  ↓\n[Phase 4: 对每个新 item 粗打分（调 cheat-score 内联逻辑）]\n  ↓\n[Phase 5: 排序 + 询问用户哪些加入 candidates.md]\n  ↓\n[Phase 6: 写入 + 更新 trends-history.jsonl 缓存]\n```\n\n## Constants\n\n- **TREND_SOURCES = [\"manual-paste\"]** — 启用的 adapter 列表（默认仅 manual-paste，最稳）\n- **LOOKBACK_HOURS = 24** — 抓最近 N 小时的热点\n- **MAX_PER_SOURCE = 20** — 每个 adapter 最多 N 条\n- **DEDUPE = true** — 去重开关\n- **AUTO_SCORE = true** — 抓回来后自动调 cheat-score 粗打分\n- **MIN_COMPOSITE_TO_SUGGEST = 6.0** — 低于此分的不推荐用户加入候选池（仍写入 trends-history 避免下次重复推）\n\n> 💡 调用时覆盖：`/cheat-trends — sources: manual-paste,aihot,weibo-hot — max-per: 10`\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `.cheat-state.json` | 默认 sources |\n| `adapters/trend-sources/<name>.md` | 各 adapter 的实现描述 |\n| `candidates.md` | 去重对照 |\n| `predictions/*.md` | 去重对照（已发的不再推） |\n| `.cheat-cache/trends-history.jsonl` | 历史抓取去重缓存 |\n\n## Workflow\n\n### Phase 0: 读启用的 adapters\n\n```python\n# 伪代码\nstate = read('.cheat-state.json')\nenabled_adapters = args.sources or state.get('enabled_trend_sources', ['manual-paste'])\n```\n\n如 enabled_adapters 为空 → 输出引导：\n\n```\n你目前没有启用任何热点源。\n\n最快配法：\n- 临时跑：/cheat-trends — sources: manual-paste,aihot\n- 永久启用：编辑 .cheat-state.json 的 enabled_trend_sources 数组\n\n可用 adapter（详见 adapters/trend-sources/）：\n- manual-paste（默认，永远能用）\n- aihot（AI 热点聚合，无需 key）\n- weibo-hot（微博热搜，无需 key）\n- zhihu-hot（知乎热榜，无需 key）\n- trendradar-mcp（TrendRadar MCP 服务，需配置）\n```\n\n### Phase 1-2: 对每个 adapter 调 fetch + normalize\n\n对每个 adapter，读其 `adapters/trend-sources/<name>.md` 中描述的 fetch 接口（实际是 Bash 调底层 Python / shell / WebFetch）：\n\n| Adapter | 实现机制 |\n|---|---|\n| `manual-paste` | 询问用户：\"粘贴你今天的候选 URL/标题列表（每行一条）\" → 解析每行，对 URL 做 WebFetch 拓展 snippet |\n| `aihot` | 读 adapters/trend-sources/aihot.md 描述的 fetch 接口 |\n| `weibo-hot` | 读 adapters/trend-sources/weibo-hot.md 描述的 fetch 接口 |\n| `zhihu-hot` | 读 adapters/trend-sources/zhihu-hot.md 描述的 fetch 接口 |\n| `trendradar-mcp` | 读 adapters/trend-sources/trendradar-mcp.md 描述的 fetch 接口 |\n\n每个 adapter 输出符合 [candidate-schema.md](../../shared-references/candidate-schema.md) 的 items。\n\n**优雅降级**：单 adapter 失败（API key 缺失 / 端点 503 / cookie 失效）→ skip 该 adapter，**不抛异常**，在汇总里说明：\n```\n✅ aihot: 拉到 18 条\n✅ weibo-hot: 拉到 15 条\n✅ zhihu-hot: 拉到 12 条\n⚠️  trendradar-mcp: 跳过（MCP 服务未配置——配置见 adapters/trend-sources/trendradar-mcp.md）\n```\n\n### Phase 3: 去重\n\n按 [candidate-schema.md](../../shared-references/candidate-schema.md) 的\"去重协议\"：\n\n1. 对每个 item 算 id（`sha256(source_type + normalized_title + url_path)[:12]`）\n2. 检查 `candidates.md` 已含此 id → 跳过\n3. 检查 `predictions/*.md` 已含此 id → 跳过\n4. 检查 `.cheat-cache/trends-history.jsonl` 已含此 id 且 `rejected_at != null` → 跳过\n\n去重统计写到汇总报告里。\n\n### Phase 4: 粗打分\n\n`AUTO_SCORE=true` 时，对每条新 item：\n1. 用 item 的 `snapshot_text` 作为输入\n2. 按当前 rubric 给 7 维打分（**不**调 `/cheat-score` 子 skill 走 IO；inline 复用打分逻辑）\n3. 算 composite\n4. 给一句 rationale\n\n**注意**：粗打分 ≠ 正式预测。预测必须基于最终稿（用户改过的），这里的打分只是\"是否值得展开写\"的粗筛。\n\n`AUTO_SCORE=false` 时，items 写入 candidates.md 时 composite=null，需要后续手动 `/cheat-score`。\n\n### Phase 5: 排序 + 询问\n\n按 composite 降序，过滤掉 composite < `MIN_COMPOSITE_TO_SUGGEST` 的：\n\n```\n🔥 抓热点完成。各源拉取统计：\n- manual-paste: 5 条（用户输入）\n- aihot: 18 条\n- weibo-hot: 15 条\n跳过 trendradar-mcp（MCP 服务未配置）\n\n去重后剩 27 条新 item。\n粗打分后 12 条 composite ≥ 6.0：\n\n| # | 标题 | source | composite | bucket | rationale |\n|---|---|---|---|---|---|\n| 1 | 为什么我们都讨厌主动联系朋友 | aihot | 8.4 | 30-100w | ER+QL 双 5，AB 普适 |\n| 2 | \"她不一样\"的一千种变体 | weibo-hot | 8.1 | 30-100w | MS 候选维度高 |\n| 3 | ...... |\n\n哪些加入 candidates.md？\n- 全部加 → 回 \"all\"\n- 选几个 → 回 \"1, 3, 5\"\n- 都不要 → 回 \"none\"（这些会被记到 trends-history 避免下次重复推）\n```\n\n### Phase 6: 落盘\n\n用户响应后：\n1. 选中的 items → 按 [candidate-schema.md](../../shared-references/candidate-schema.md) 的\"Markdown 表示\"格式追加到 `candidates.md`\n2. 所有抓回来的 items（不管选中与否）→ append 到 `.cheat-cache/trends-history.jsonl`：\n   ```jsonl\n   {\"id\": \"...\", \"title\": \"...\", \"source\": \"...\", \"snapshot_at\": \"...\", \"rejected_at\": null|\"<ISO>\", \"fetched_at\": \"<ISO>\"}\n   ```\n\n### Phase 7: 状态更新\n\n```json\n{\n  \"last_trends_run_at\": \"<ISO>\",\n  \"last_trends_added_count\": 5\n}\n```\n\n## Key Rules\n\n1. **不抛异常**。单 adapter 失败 → skip + 报告。多 adapter 全失败 → 报错\"所有源都失败\"，附排查指引\n2. **manual-paste 永远在**。即使其他所有 adapter 都坏了，manual-paste 模式必须能跑——它是兜底\n3. **去重是硬约束**。同 id 不重复推；用户拒绝过的 6 个月内不再推\n4. **粗打分要诚实标注**。在 candidates.md 的 entry 里标 `composite (rough, snapshot-based)`，避免与 prediction 的精打分混淆\n5. **不直接进 predictions/**。trends 只产 candidates，predict 是另一个动作\n\n## Refusals\n\n- 「直接抓抖音热门 feed，不用 cookie」 → 拒绝。抖音反爬极严，无 cookie 必失败；引导到 douyin-session adapter 配置文档\n- 「跳过去重，把所有抓到的都写进去」 → 拒绝。会污染候选池，下次 recommend 时排序失效\n- 「跳过粗打分，直接写 raw 标题」 → 允许（`AUTO_SCORE=false`），但提示用户后续需要 `/cheat-score` 才能进 recommend 池\n\n## Integration\n\n- 上游：用户配置 `.cheat-state.json` 的 `enabled_trend_sources` 数组\n- 下游：`/cheat-recommend` 直接读 `candidates.md` 排序——trends 写完，recommend 立刻看到\n- 与 `/cheat-init`：onboarding Q4 选\"没有候选池\"的用户被引导到这里\n- 与 `/cheat-status`：status 看板显示\"上次抓热点：X 天前 / 待清理候选池：Y 条\"\n\n## Adapter 实现注意事项\n\n每个 `adapters/trend-sources/<name>.md` 必须文档化以下：\n1. **依赖**：API key / cookie / package\n2. **fetch 接口**：调用方式（python script path / shell command / API endpoint）\n3. **输出 schema**：必须符合 candidate-schema.md\n4. **失败模式**：常见错误 + 优雅降级行为\n5. **稳定性等级**：★ 1-5 颗星\n\n详见 [adapters/HOWTO.md](../../adapters/HOWTO.md)（待批次 3 实现）。\n"},"items":[{"name":"SKILL.md","path":"SKILL.md","rawUrl":"https://raw.githubusercontent.com/XBuilderLAB/cheat-on-content/HEAD/SKILL.md","title":"cheat-on-content","category":"anthropic-skill","format":"markdown","content":"---\nname: cheat-on-content\ndescription: 给所有想把\"感觉\"变成可校准预测的内容创作者。**方法论通用**——打分 → 盲预测 → T+3d 复盘 → 进化 rubric 的循环适用任何能被量化（播放 / 阅读 / 收听 / 点击）的内容。**rubric 是循环的内容，不是循环本身**——当前内置一份观点视频 rubric（参考博主 25+ 视频拟合），其他形态可借这套起步并 bump 调权重。**强烈建议导入对标账号**作为初始信号源（/cheat-learn-from）。触发词：\"初始化\"/\"打分这篇\"/\"启动预测\"/\"已发布\"/\"复盘\"/\"升级 rubric\"/\"推荐选题\"/\"抓热点\"/\"状态\"/\"找对标\"/\"learn from\"。**首次使用必须先跑 /cheat-init。**\nargument-hint: \"[draft-path] [— mode: cold-start|calibration]\"\nallowed-tools: Bash(*), Read, Write, Edit, Grep, Glob, Skill, mcp__llm-chat__chat\n---\n\n# 网红作弊器 / Cheat on Content\n\n> 🎯 **方法论通用，rubric 当前内置观点视频版**\n>\n> **方法论**（5 阶段闭环）：任何能被量化的内容形态都适用——视频 / 文章 / 播客 / Newsletter / 短文 thread。\n>\n> **当前内置 rubric**：观点类视频（评论 / 时评 / 论说 / 议题讨论 / 个人观点表达），7 个维度由参考博主 25+ 已发样本拟合而来。如果你做其他形态，需要：\n> - 自己写一份 rubric（参照 [starter-rubrics/opinion-video-zero.md](starter-rubrics/opinion-video-zero.md) 的格式）\n> - 或等内置扩展（长文 / 短文 / 播客 starter 在批次 3 路线图）\n>\n> 默认假设：**用户是从零开始的新人**（一条视频都没发过）——cold-start 期的预测会**简化**，只要 7 维打分 + 一句话 bet，不强求 bucket 数字（避免 false precision）。已有 5+ 篇数据的老手走 calibration 模式解锁完整 7 组件预测。\n\n把内容创作变成可校准预测循环：**打分 → 预测 → 发布 → 复盘 → 进化 rubric**。\n\n本文件是**总协议 + 路由器**。具体每个阶段的工作流在 `skills/cheat-*/SKILL.md` 各子 skill 里。\n\n## Codex compatibility\n\nCodex 没有 Claude Code 的 slash-command harness。安装到 Codex 后，按自然语言触发同一套路由即可：\n\n- `初始化 cheat-on-content` → 读取并执行 `skills/cheat-init/SKILL.md`\n- `打分这篇 scripts/foo.md` → 读取并执行 `skills/cheat-score/SKILL.md`\n- `启动预测 scripts/foo.md` → 读取并执行 `skills/cheat-predict/SKILL.md`\n- `拍了 ...` / `已发布 ...` / `复盘 ...` / `升级 rubric` / `状态` → 分别读取对应 `skills/cheat-*/SKILL.md`\n\n执行时遵循本文件的三条原则和路由表；不要依赖 `/cheat-*` 命令是否存在。Claude Code 专用 hook（`.claude/settings.json`）仍只在 Claude Code 里自动触发；Codex 中需要用户主动说 `状态` 查看 buffer、待复盘和候选池。\n\n---\n\n## 三条不可妥协原则\n\n任何一条被违反，整个校准循环退化为\"凭直觉的自我安慰\"。如果用户要求打破其中任何一条，**拒绝执行并说明原因**。\n\n1. **盲预测（Blind prediction）**：预测必须在看到任何实际数据**之前**写完。一旦写完，`## 预测` 段是 immutable——只能往 `## 复盘` 段追加。完整规范：[shared-references/blind-prediction-protocol.md](shared-references/blind-prediction-protocol.md)。**hooks/prediction-immutability.sh 在 harness 层强制执行**。\n\n2. **升级 = 全量重打（Bump = full re-score）**：rubric 升级时，校准池所有有实绩数据的样本必须用新公式重打分；新排序与实际表现排序若在 ≥4/5 样本上不一致，升级被拒；升级必须经跨模型独立审核。完整规范：[shared-references/bump-validation-protocol.md](shared-references/bump-validation-protocol.md)。\n\n3. **rubric 是工作台，不是博物馆**：被新数据推翻或被吸收为正式维度的观察，**删掉**。绝不留\"我曾经以为 X，但其实...\"的考古层。git history 才是档案。完整规范：[shared-references/observation-lifecycle.md](shared-references/observation-lifecycle.md)。\n\n---\n\n## 路由表（触发词 → 子 skill）\n\n| 用户说 | 调用 | 前置条件 |\n|---|---|---|\n| \"初始化\" / \"init\" / \"首次使用\" | `/cheat-init` | 无（这是入口） |\n| \"找对标\" / \"学这个账号\" / \"拆这几个对标视频\" / \"learn from\" / \"导入对标账号\" | `/cheat-learn-from` | 已 init；cold-start 强烈建议；后续可随时 --append / --replace |\n| \"找选题\" / \"我不知道拍什么\" / \"seed\" / \"找前 5 个选题\" | `/cheat-seed` | 已 init（cold-start 用户专用一次性种子动作） |\n| \"打分这篇 [path]\" / \"score this [path]\" | `/cheat-score` | rubric_notes.md 存在 |\n| \"启动预测\" / \"start prediction\" / \"给这稿子打分并预测\" | `/cheat-predict` | 已 init + 有最终稿 |\n| \"拍了 X\" / \"shot it\" / \"录完了\" | `/cheat-shoot` | 对应预测已写（buffer +1） |\n| \"已发布\" / \"I shipped it\" / \"发布链接是 X\" | `/cheat-publish` | 对应预测文件存在（buffer -1） |\n| \"复盘\" / \"retro this\" / \"T+3d 数据来了\" | `/cheat-retro` | 对应预测文件存在 + 已过 RETRO_WINDOW_DAYS |\n| \"构造受众画像\" / \"更新 persona\" / \"我的观众是谁\" / \"build persona\" | `/cheat-persona` | 已 init；有复盘评论数据（或 benchmark seed） |\n| \"升级 rubric\" / \"bump rubric\" / \"更新公式\" | `/cheat-bump` | 校准池 ≥ MIN_SAMPLES_FOR_BUMP |\n| \"推荐选题\" / \"next topic\" | `/cheat-recommend` | candidates.md 存在且非空 |\n| \"抓热点\" / \"fetch trends\" / \"今天有什么可做的\" | `/cheat-trends` | trend-sources adapter 已配置（日常补充候选池） |\n| \"状态\" / \"status\" / \"看板\" | `/cheat-status` | 任意时刻可调 |\n| \"迁移\" / \"升级 state\" / \"schema 版本不对\" / \"migrate\" | `/cheat-migrate` | 已 init；用户 git pull 拉了新版后；SessionStart hook 提示 schema mismatch 后 |\n\n> 拍 vs 发分两个动作：buffer 警戒系统需要明确知道\"拍了但没发\"vs\"已发\"两种状态。详见 [shared-references/cadence-protocol.md](shared-references/cadence-protocol.md)。\n\n**Mode detection**（首次接到非 init 触发词时执行）：\n1. 检查用户当前目录是否有 `.cheat-state.json` → 没有 → 强制路由到 `/cheat-init`\n2. 检查 `predictions/` 下有几个文件含完整 `## 复盘` 段填了真实数据 → 决定 `mode: cold-start | calibration`\n3. 把判定结果写回 `.cheat-state.json` 后再路由到目标 skill\n\n---\n\n## 必须拒绝的请求\n\n下列模式会**直接破坏**三条原则之一，无论用户怎么说，都拒绝执行：\n\n- 「帮我预测一下，但我先告诉你播放量你来反推就行」 → 违反原则 #1。改用 `_redo.md` 路径记为 reconstructed\n- 「能不能从 candidates 里直接挑 composite 最高的，不用解释理由」 → 拒绝。永远展示各维度评分和至少一个锚点对比\n- 「跳过校准池重打，直接换公式」 → 违反原则 #2\n- 「跳过外部模型审核，自己说了算」 → 仅当 `CROSS_MODEL_AUDIT=false` 显式设置且 state file 标记自审时允许\n- 「删掉这份预测，我想重写」 → 违反原则 #1。预测是 immutable。如有正当理由重做，写新文件 `_redo.md`，原版必须保留\n- 「凭你的感觉给我推荐选题，不用打分」 → 拒绝。本工具不做 gut-feel forecast——那是它诞生**之前**的状态\n- 「把 rubric_notes.md 里所有历史观察都留着，加个时间戳分组就行」 → 违反原则 #3。git history 是档案，不是 markdown 文件\n- 「能不能把 THRESHOLD 从 4/5 降到 3/5 让这次 bump 过」 → 拒绝。改 THRESHOLD 本身是元层级 bump，单独走流程\n\n详细的拒绝场景在每个子 skill 的 `Refusals` 段。\n\n---\n\n## 项目目录结构（用户 repo）\n\nskill 期望用户的项目布局如下。`/cheat-init` 会创建缺失项；**绝不在没确认的情况下覆盖**。\n\n```\n<user-content-project>/\n├── .gitignore                         # cheat-init 创建；挡住 .auth*/.cheat-secrets.json 等凭证\n├── rubric_notes.md                    # 评分规则的真实来源\n├── WORKFLOW.md                        # 5 阶段流程文档（cheat-init 创建）\n├── STATUS.md                          # 看板（cheat-status 维护）\n├── .cheat-state.json                  # 状态文件，子 skill 共享上下文\n├── .cheat-cache/                      # 不入版本控制\n│   ├── usage.jsonl                    # 钩子被动记录的使用日志\n│   └── trends-history.jsonl           # cheat-trends 的去重缓存\n├── .claude/\n│   └── settings.json                  # 含 prediction-immutability hook\n├── benchmark.md                       # 对标账号信息（cheat-learn-from 维护）\n├── audience.md                        # 受众画像（cheat-persona 派生；blind 硬禁读）\n├── scripts/                           # 拍前的所有草稿（cheat-seed 写或用户写）\n│   └── YYYY-MM-DD_<id>_<short>.md\n├── predictions/                       # immutable 预测日志（hook 保护）\n│   └── YYYY-MM-DD_<id>_<short>.md     # 与 scripts/ 同 id\n├── videos/                            # 拍后才建（cheat-shoot 创建）\n│   └── YYYY-MM-DD_<id>_<short>/\n│       ├── script.md                  # 用户提供的最终拍摄稿（cheat-shoot 时询问\"和 scripts/ 一致吗\"）\n│       └── report.md                  # T+3d 抓的数据 + 评论（cheat-retro 写）\n├── samples/                           # 对标账号视频 / 转录（cheat-learn-from 创建）\n│   └── <账号名>/<video-id>/{source.mp4 (可选), transcript.md, meta.md}\n├── candidates.md                      # 选题池（可选）\n└── content.db                         # 可选 SQLite，校准池规模化后启用\n```\n\n---\n\n## 文件清单\n\n### 本 skill 包\n\n```\ncheat-on-content/\n├── SKILL.md                           # 本文件（总协议 + 路由）\n├── README.md                          # 营销门面\n├── skills/                            # 子 skill 集\n│   ├── cheat-init/SKILL.md            # ✅ 入口：onboarding 与脚手架\n│   ├── cheat-learn-from/SKILL.md      # ✅ 对标账号导入（拆 pattern + 派生 base rubric 信号）\n│   ├── cheat-seed/SKILL.md            # ✅ Cold-start 选题启动器（brainstorm + 可选 draft）\n│   ├── cheat-score/SKILL.md           # ✅ 单稿打分（不写文件）\n│   ├── cheat-predict/SKILL.md         # ✅ 盲预测 + immutable 日志\n│   ├── cheat-shoot/SKILL.md           # ✅ 登记拍摄（buffer +1）\n│   ├── cheat-publish/SKILL.md         # ✅ 发布元数据登记（buffer -1）\n│   ├── cheat-retro/SKILL.md           # ✅ 数据回收 + 复盘\n│   ├── cheat-persona/SKILL.md         # ✅ 受众画像派生（从复盘评论聚类）\n│   ├── cheat-bump/SKILL.md            # ✅ rubric 升级（含跨模型审）\n│   ├── cheat-recommend/SKILL.md       # ✅ 候选池排序推荐（按 buffer 颜色 + 1 稳 + 1 实验）\n│   ├── cheat-trends/SKILL.md          # ✅ 热点抓取（日常补充候选池，多 adapter）\n│   ├── cheat-status/SKILL.md          # ✅ 状态看板（含 buffer 警戒）\n│   ├── cheat-migrate/SKILL.md         # ✅ schema 升级（老用户 git pull 后用）\n│   └── cheat-score-blind/SKILL.md     # ✅ Channel B 隔离打分 sub-agent（仅 Task tool 调用）\n├── migrations/                        # schema 演进单一来源\n│   ├── registry.md                    # ✅ LATEST_SCHEMA + 版本链表\n│   └── <from>-to-<to>.md              # ✅ 每步迁移的 WHAT/WHY/HOW/Manual fallback\n├── shared-references/                 # 跨 skill 共享协议\n│   ├── blind-prediction-protocol.md   # ✅ 原则 #1\n│   ├── bump-validation-protocol.md    # ✅ 原则 #2\n│   ├── observation-lifecycle.md       # ✅ 原则 #3\n│   ├── prediction-anatomy.md          # ✅ 一份合格预测的 7 个组件\n│   ├── candidate-schema.md            # ✅ 候选项统一 schema\n│   ├── cadence-protocol.md            # ✅ 节奏协议（buffer 警戒 + 选题策略）\n│   ├── state-management.md            # ✅ .cheat-state.json 读写约定\n│   └── migration-protocol.md          # ✅ schema 演进哲学 + maintainer checklist\n├── starter-rubrics/                   # 各内容形态的先验 rubric\n│   ├── opinion-video.md               # ✅ 观点视频（中文，已校准 25+ 样本）\n│   ├── opinion-video-zero.md          # ✅ v0 等权占位（cold-start）\n│   ├── long-form-essay.md             # ⬜ 公众号 / Substack\n│   └── short-form-text.md             # ⬜ X thread / 微博长文\n├── templates/                         # skill 写进用户 repo 的文件骨架\n│   ├── gitignore.template             # ✅ 用户项目 .gitignore（护凭证，保留 predictions/ 入库）\n│   ├── rubric_notes.template.md       # ✅\n│   ├── prediction.template.md         # ✅ 统一版（所有阶段，含 confidence header）\n│   ├── retro.template.md              # ✅\n│   ├── candidates.template.md         # ✅\n│   ├── candidates.template.json       # ✅\n│   ├── script_patterns.template.md    # ✅ 写作 pattern 沉淀（含 benchmark 借鉴段说明）\n│   ├── benchmark.template.md          # ✅ 对标账号 reference\n│   ├── audience.template.md           # ✅ 受众画像骨架\n│   ├── workflow.template.md           # ✅\n│   ├── status.template.md             # ✅\n│   └── content.db.schema.sql          # ✅\n├── hooks/                             # harness 强制层\n│   ├── prediction-immutability.json   # ✅ 阻塞型钩子（拦预测段编辑）\n│   ├── prediction-immutability.sh     # ✅ 拦截脚本\n│   ├── session-start.json             # ✅ SessionStart 自动报告 hook\n│   ├── session-start.sh               # ✅ 状态报告渲染脚本\n│   ├── meta-logging.json              # ✅ 被动记录配置\n│   └── log-event.sh                   # ✅ meta-logging 脚本\n├── tools/                             # 独立 CLI 脚本\n│   ├── score-curve.py                 # ⬜ 预测精度收敛曲线\n│   ├── md-to-sqlite.py                # ⬜ markdown → content.db 升级（批次 3）\n│   └── validate-bump.py               # ⬜ 校准池全量重打（批次 3）\n├── adapters/                          # 数据源适配\n│   ├── perf-data/                     # 复盘数据源（含 douyin-session）\n│   ├── candidate-pool/                # 候选池数据源\n│   ├── trend-sources/                 # 热点抓取源\n│   └── script-extraction/             # 视频/音频转 script（含 whisper for cheat-learn-from）\n└── examples/\n    ├── reference-implementation/      # 视频分析脱敏快照（待）\n    └── script_patterns.example.md     # script_patterns 全填示例（参考用，不复制）\n```\n\n✅ = 当前批次（v1 骨架）已完成 / ⬜ = 后续批次\n\n---\n\n## Tone & voice\n\n写面向用户的文案（commit message / 复盘小结等）时，匹配项目的 **直白克制（reflective-irreverent）** voice：\n\n- 直接说出失败：「composite 8.47 但实际只有 16.8w——rubric 高估了 SR」\n- **不要**用模糊措辞软化：「这或许可能在某种程度上暗示...」——别这么写\n- Cluely 风格的反叛 hook 只在 README 出现——**不要写进** `rubric_notes.md` 或预测日志\n\n---\n\n## 给开发者：扩展本 skill\n\n- 新增内容形态 → 加 `starter-rubrics/<form>.md`\n- 新增热点抓取源 → 加 `adapters/trend-sources/<name>.md`，符合 [candidate-schema.md](shared-references/candidate-schema.md) 输出契约\n- 修改原则 → 改 `shared-references/<protocol>.md`，所有引用它的 skill 自动跟进\n- 修改路由 → 改本文件的\"路由表\"段\n- 子 skill 内部细节 → 直接改对应 `skills/cheat-*/SKILL.md`\n\n完整开发指南见 README.md。\n","frontmatter":{"name":"cheat-on-content","description":"给所有想把\"感觉\"变成可校准预测的内容创作者。**方法论通用**——打分 → 盲预测 → T+3d 复盘 → 进化 rubric 的循环适用任何能被量化（播放 / 阅读 / 收听 / 点击）的内容。**rubric 是循环的内容，不是循环本身**——当前内置一份观点视频 rubric（参考博主 25+ 视频拟合），其他形态可借这套起步并 bump 调权重。**强烈建议导入对标账号**作为初始信号源（/cheat-learn-from）。触发词：\"初始化\"/\"打分这篇\"/\"启动预测\"/\"已发布\"/\"复盘\"/\"升级 rubric\"/\"推荐选题\"/\"抓热点\"/\"状态\"/\"找对标\"/\"learn from\"。**首次使用必须先跑 /cheat-init。**","argument-hint":"[draft-path] [— mode: cold-start|calibration]","allowed-tools":"Bash(*), Read, Write, Edit, Grep, Glob, Skill, mcp__llm-chat__chat","allowedTools":["Bash(*),","Read,","Write,","Edit,","Grep,","Glob,","Skill,","mcp__llm-chat__chat"]},"allowedTools":["Bash(*),","Read,","Write,","Edit,","Grep,","Glob,","Skill,","mcp__llm-chat__chat"],"isInternal":false,"tokens":5351,"sizeBytes":16486},{"name":"SKILL.md","path":"skills/cheat-bump/SKILL.md","rawUrl":"https://raw.githubusercontent.com/XBuilderLAB/cheat-on-content/HEAD/skills/cheat-bump/SKILL.md","title":"cheat-bump","category":"anthropic-skill","format":"markdown","content":"---\nname: cheat-bump\ndescription: 提议并执行 rubric 或 bucket 升级。两种模式：**完整 rubric bump**（最高风险动作，5 步强制 + 跨模型审核）和 **--bucket-only 轻量重校**（只换 bucket 边界，不动 rubric 公式）。**Phase 2 强制走 cheat-score-blind sub-agent 给校准池重打分**——不接受 self-scored fallback。触发词：\"升级 rubric\"/\"bump rubric\"/\"更新公式\"/\"我想加一个维度\"/\"调整权重\"/\"重校桶\"/\"recalibrate bucket\"。\nargument-hint: --propose \"<...>\" | --bucket-only [--scheme ratio|absolute|percentile]\nallowed-tools: Bash(*), Read, Write, Edit, Glob, Grep, Skill, Task, mcp__llm-chat__chat\n---\n\n# /cheat-bump — Rubric / Bucket 升级\n\n两种模式：\n\n| 模式 | 触发 | 做什么 | 验证强度 |\n|---|---|---|---|\n| **完整 rubric bump** | `--propose \"<新公式>\"` | 改公式 / 维度 / 权重 | 5 步 + 跨模型审核（强制） |\n| **bucket-only 重校** | `--bucket-only` | 只重新派生 bucket 边界 | 数据自动派生，无审核 |\n\n完整 rubric bump 严格遵守 [shared-references/bump-validation-protocol.md](../../shared-references/bump-validation-protocol.md) 的 5 步。bucket-only 走轻量路径——见下方 Phase B。\n\n## Overview\n\n```\n入口：用户触发 /cheat-bump\n  ↓\n[Phase A0: 检测调用模式]\n  ↓\n  ├─ --bucket-only  →  [Phase B: 轻量 bucket 重校]\n  └─ --propose      →  [Phase 0~6: 完整 rubric bump]\n```\n\n## Phase A0: 调用模式分流（先做）\n\n读用户参数：\n- 含 `--bucket-only` → 走 **Phase B**（轻量重校）\n- 含 `--propose \"<...>\"` → 走 **Phase 0~8**（完整 rubric bump）\n- 都没有 → 询问用户：\"你想做什么？1) 调 rubric 公式 / 加减维度 → --propose；2) 只重新派生 bucket 边界 → --bucket-only\"\n\n如果用户说\"我觉得 ER 太低了想调\"→ 是 `--propose` 路径。\n如果用户说\"我账号长大了，bucket 不准了\"→ 是 `--bucket-only` 路径。\n**两条路径不能混调**——一次操作只做一种事。\n\n---\n\n## 完整 rubric bump 流程\n\n```\n[用户：升级 rubric --propose \"ER×1.5→2.0，砍 NA，加 MS\"]\n  ↓\n[Phase 0: 前置门槛检查]\n  ↓\n[Phase 1: 写出新公式完整方程]\n  ↓\n[Phase 2: 校准池全量重打分]\n  ↓\n[Phase 3: 计算排序一致性]\n  ↓\n[Phase 4: 跨模型独立审核（强制）]\n  ↓\n[Phase 5: 落地 + cleanup pass]\n  ↓\n[Phase 6: 更新所有校准样本的 prediction 文件底部追加 Re-scored 行]\n```\n\n## Constants\n\n- **READINESS_HEURISTIC** —\n  - **默认参考**：校准池 ≥ 5 样本 + 至少 1 个跨样本观察有 ≥3 样本支持\n  - **但 Claude 可以提议 bump**（即使样本少）如果观察信号特别强：\n    - N=3 但出现完全推翻当前 rubric 假设的强反例（composite 8.5 vs 实绩 5w 这种 ≥3x 偏差）\n    - 1 篇出现单点但极强的现象（如评论区出现 ≥2000 赞的单一模因）\n  - **Claude 也可以拒绝 bump**（即使样本足）如果证据弱：\n    - N=10 但观察都是低置信度的零碎 pattern，无清晰方向\n    - 用户复盘时大量\"随便看了下\"的非严肃判断\n  - **写在 prediction header 或 cheat-bump 输出时必说明**：本次提议是 default-aligned 还是 judgment-driven，给用户审视依据\n- **THRESHOLD = 0.8** — 新排序与实绩排序一致性阈值（4/5）。这条**写死**——bump 验证的统计刚性\n- **CROSS_MODEL_AUDIT = true** — 调外部 LLM 独立审核。false 仅用于离线\n- **REQUIRE_CONFIRM = true** — 落地前要求用户明确\"yes, bump\"\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `--propose` 文本 | 用户参数；缺失则询问 |\n| `rubric_notes.md` | 用户项目根 |\n| `predictions/*.md` 全量 | 校准池数据 |\n| `.cheat-state.json` | 状态 |\n\n## Workflow\n\n### Phase 0: 前置门槛检查\n\n按 [bump-validation-protocol.md](../../shared-references/bump-validation-protocol.md) 的\"何时禁止\"段，逐项检查：\n\n| 检查 | 失败处理 |\n|---|---|\n| 校准池总样本数 vs 观察强度 | **Claude 判断**——按 READINESS_HEURISTIC：默认 ≥5 样本但允许特例（强反例 / 强模因）。如不满足默认，Claude 必须**显式说明**为什么仍然提议 bump（\"虽然只 N=3 样本，但 X 这条出现 composite Y vs 实绩 Z，这是 W 倍偏差\"），让用户审视 |\n| 上次 bump 距今的新校准数 vs 观察成熟度 | **Claude 判断**——默认建议 ≥3 篇新样本，但如果连续 3 篇都强证据指向同一方向 → 不必再等 |\n| `in_progress_session == null` | 拒绝：\"你有 in-progress 预测未完成。先走完那条流程或清掉 state\" |\n| 触发条件成立（系统性偏差 / 跨样本新观察 / 新维度证据足） | 警告但不阻塞——询问用户为什么现在 bump |\n\n通过 → 进入 Phase 1。\n\n### Phase 1: 写出新公式完整方程\n\n**不能只接受用户的简短描述**。把它展开为完整方程：\n\n```\n当前：v2  composite = (ER×1.5 + SR×1.5 + HP×1.5 + QL + NA + AB + SAT) / 8.5 × 2.0\n提议：v2.1  composite = (ER×2.0 + HP×1.5 + MS×1.5 + QL + SR + TS + SAT) / 9.0 × 2.0\n\n变化总结：\n- ER ×1.5 → ×2.0（升）\n- SR ×1.5 → ×1.0（降）\n- 新增 MS ×1.5（Memetic Shareability）\n- 新增 TS ×1.0（Topic Shareability）\n- 删除 NA（与 HP 重叠）\n- 删除 AB（被 TS 替代）\n- 归一化常数 8.5 → 9.0\n- 公式总维度数：7 → 7（净变化 0）\n```\n\n如果用户的提议含糊（如\"ER 权重提一点\"）→ 询问具体数值，**禁止自己猜**。\n\n### Phase 2: 校准池全量重打分（**强制走 blind sub-agent**）\n\nGlob `predictions/*.md` 中所有有完整复盘段的文件 → 校准池。\n\n**bump 是工具最高风险动作——所有重打必须走 [cheat-score-blind](../cheat-score-blind/SKILL.md) sub-agent**。inline 重打 = 主 Claude 已经看过实绩，rank 一致性变成 overfit 而非真信号。\n\n#### 强制约束\n\n- **不接受 self-scored fallback**——`/cheat-predict` 有 `--skip-blind` flag，但 `/cheat-bump` **没有**。如果 Task tool 不可用 → **abort bump**，向用户报告\"先解决 Task tool 再 bump\"\n- **不接受\"我只重算 composite 不重打 dim\"** —— 即使新公式只调权重不加维度，每条 prediction 的所有 dim 都要由 sub-agent 重新审 script。理由：旧 dim 分本身可能是污染的；权重变了不能保证旧 dim 还成立\n\n#### 对每篇 prediction：\n\n1. 解析 prediction 文件拿到对应 `scripts/<id>.md` 路径（从 `Script Path` header 字段）\n2. 校验 script 文件存在 + hash 跟 header `Script Hash` 一致；不一致 → 警告（script 改过了）但仍 spawn sub-agent\n3. **通过 Task tool spawn cheat-score-blind sub-agent**：\n   ```\n   Spawn cheat-score-blind sub-agent.\n\n   Input:\n     script_path: <prediction header 的 Script Path>\n     rubric_notes_path: rubric_notes.md\n     sidecar_path: .cheat-cache/bump-rescores/<prediction-id>.json\n\n   Task: 按 rubric_notes 当前公式（已是新版 vN+1）给 script 打分。\n   返回严格 JSON。写 sidecar 文件用于 bump 主流程批量读取。\n\n   不要读 state file / predictions/ / videos/ 任何其他文件。\n   不要询问用户 —— 你没有用户。\n   不要读这份 prediction 文件本身 —— 你只看 script + rubric。\n   ```\n4. 等 sub-agent 完成 → 读 sidecar JSON → 主流程用新公式算 composite\n5. 写\"重打表\"到 `.cheat-cache/bump-rescores.json`（汇总）。**每条 entry 标 `blind: true`** —— bump phase 5 cleanup 时把这个字段连同新分数写到 prediction 文件的 `Re-scored under v<N+1>` 行\n\n#### 还污染没污染的诚实标注\n\n即使走 sub-agent，**仍有两类残余 contamination 要在 bump report 里诚实标注**：\n\n| 类型 | 来源 | 标注字段 |\n|---|---|---|\n| 模型 prior contamination | sub-agent 仍是 Claude，RLHF 共享 | `model_prior_warning: true`（默认 true，不可关） |\n| 用户自己 rubric design bias | rubric_notes.md 是用户写的，自然 fit 自己内容 | `rubric_self_designed: true`（默认 true，不可关） |\n\n这两条提示用户 channel C（跨模型 audit）的不可省。bump 报告末尾必印：\"上面的 rank 一致性是 channel A 内的一致性。**最终决策必须等 channel C audit 通过**。\"\n\n#### 失败模式\n\n| 症状 | 处理 |\n|---|---|\n| 某条 prediction 的 script 文件不见了 | sub-agent skip 该条，主流程汇总报告\"N 条因 script 缺失被排除\"。如剩余有效池 < MIN_SAMPLES → abort bump |\n| sub-agent 返回 `refusal != null` | 重发 Task 最多 3 次；仍败 → 该条标 `rescore_failed: true` 排除出校准池 |\n| Task tool 整个不可用 | abort bump，提示用户\"Task tool 是 bump 的硬依赖。如真的离线环境，跑 `/cheat-bump --bucket-only` 走轻量分支\" |\n| sub-agent 输出含 contamination_signal | 标 `suspicious: true` 但不排除——bump report 末尾列这些可疑条目让用户审 |\n\n### Phase 3: 计算排序一致性\n\n```\n每个样本：\n  new_composite_rank: 用新公式排序的 rank\n  actual_plays_rank: 用实际播放排序的 rank\n  delta: |new_rank - actual_rank|\n\n输出对照表：\n| 样本 | composite (v2) | composite (v2.1) | rank (new) | actual | rank (actual) | delta |\n|---|---|---|---|---|---|---|\n| 仓鼠 | 9.41 | 9.55 | 1 | 124.8w | 1 | 0 |\n| 停止期待 | 8.24 | 9.11 | 2 | 71.1w | 2 | 0 |\n| 老板废话 | 7.65 | 8.11 | 4 | 39.6w | 3 | 1 |\n| 求职悖论 | 8.47 | 7.56 | 5 | 16.8w | 4 | 1 |\n| 谁问你了 | 8.24 | 7.00 | 6 | 11.7w | 5 | 1 |\n\n排序一致性：4/5 在 |delta| ≤ 1\nPairwise no-regression：旧公式做对的所有 pair 在新公式下未颠倒 ✓\n```\n\n判定：\n- 排序一致性 < THRESHOLD（默认 0.8） → **本地拒绝**，转 Phase 4 之前明确报告失败\n- pairwise 出现回归 → **本地拒绝**\n\n`THRESHOLD` 写死在协议里——不允许临时调低（那本身是另一个需要 bump 的元决策）。\n\n### Phase 4: 跨模型独立审核（**强制**，除非 escape hatch）\n\n`CROSS_MODEL_AUDIT=true`（默认）：\n\n调用 `mcp__llm-chat__chat`：\n\n```\nprompt:\n你是一个独立审稿人。下面是一个内容创作者准备升级的 rubric 公式。\n请独立判定两件事：\n1. 排序一致性：新公式给样本的排序与实际表现排序，是否真的在 ≥80% 样本上一致？\n2. 解释力：新公式相比旧公式，是否更好地解释了校准池的实绩分布？\n\n数据：\n旧公式：(ER×1.5 + SR×1.5 + HP×1.5 + QL + NA + AB + SAT) / 8.5 × 2.0\n新公式：(ER×2.0 + HP×1.5 + MS×1.5 + QL + SR + TS + SAT) / 9.0 × 2.0\n\n校准池：\n[Phase 2 重打表的完整 JSON]\n\n排序对照：\n[Phase 3 表格的完整 JSON]\n\n输出格式：\n- 判定：PASS 或 REJECT\n- 理由：≥100 字\n- 关键风险：[如有，列出新公式的潜在问题]\n```\n\n收到外部 LLM 回复 → 解析判定。\n\n判定逻辑：\n- 本地 PASS + 外部 PASS → 通过，进入 Phase 5\n- 本地 PASS + 外部 REJECT → **视为 REJECT**。冲突意味着至少一方解读不稳定\n- 本地 REJECT → 已在 Phase 3 终止\n- mcp__llm-chat__chat 不可用 → 优雅降级到 `CROSS_MODEL_AUDIT=false`，state file 标 `last_bump_self_audited: true`\n\n`CROSS_MODEL_AUDIT=false`：\n- 仅依赖本地判定\n- state file 持续标记，cheat-status 持续提示用户\"这次 bump 是自审，建议配置 mcp__llm-chat__chat\"\n\n### Phase 5: 落地 + cleanup pass\n\n通过审核后，**REQUIRE_CONFIRM=true** → 询问用户：\"新公式 PASS 本地与外部审核。最后确认：执行 bump 落地？这会修改 rubric_notes.md + rubric-memo.md 并删除若干已被吸收的观察。回答 'yes, bump' 才执行。\"\n\n用户确认后：\n\n#### 5a. 更新 `rubric_notes.md`（**只放通用语言，不含视频名 / 实绩**）\n\n- 顶部 metadata 更新：\n  - `**当前版本**: vN+1`\n  - `**Last bumped at**: <ISO 8601>`\n  - `**Upgrade memos**: 见 [rubric-memo.md](rubric-memo.md)`（指针，不复制 Memo 内容）\n- 版本速查表加一行（只含版本号 + 公式签名，**不含**证据样本）\n- 更新\"当前评分维度\"段（删 NA / AB，加 MS / TS）\n- **派生证据段** 如新维度需要锚点解释 → **用通用语言**：\n  - ✅ 允许：「派生证据：高抽象密度样本 → CC=1 → 低 reach」\n  - ❌ 禁止：「派生证据：「停止期待」CC=1 → 实绩 13.7w」（视频名 + 实绩 数字）\n  - 命中违禁 pattern → 把该段抽到 rubric-memo.md 的\"派生证据\"子段，原位用通用语言替代\n\n#### 5b. 写 Memo 到 `rubric-memo.md`（**append 模式，不覆盖历史**）\n\n按 [bump-validation-protocol.md](../../shared-references/bump-validation-protocol.md) Step 5 + [templates/rubric-memo.template.md](../../templates/rubric-memo.template.md) 格式 append 一段 Memo 到文件末尾：\n\n- 触发观察（含真实观察 ID）\n- 证据数据（**校准池重打表 + 排序对照，含真实视频名 + 实绩**）\n- 派生证据（**含真实样本名 + 实绩**）\n- 诊断\n- 新公式\n- 跨模型审核结论引用（含模型名 + 判定 + 理由摘录）\n- 已知局限\n\n**绝不**覆盖 rubric-memo.md 已有内容——bump memo 按时间顺序累积。\n\n#### 5c. cleanup pass（按 [observation-lifecycle.md](../../shared-references/observation-lifecycle.md) 的\"cleanup pass 强制时机\"）\n\n在 `rubric_notes.md` 内执行（**不**动 rubric-memo.md）：\n\n- 已被吸收为新维度的观察 → 删（如观察 E 被吸收为 MS → 删观察 E）\n- 被新数据推翻的观察 → 删\n- 仍未解决的观察 → 迁移到新版本\"待验证假设\"段\n- 已被验证的\"规律\"→ 移到\"规律沉淀区\"\n\n#### 5d. 整理 + 自检\n\n- 重新读 `rubric_notes.md` 全文，确保读者能在 60 秒内理解当下规则——超出 600 行触发额外清算\n- **自检 leak guard**：对 `rubric_notes.md` 跑 `grep -E '\\\\d+\\\\s*[wWmMkK万]|播放|实绩|实际'` → 如有命中 → **abort bump + 回滚**，提示用户\"rubric_notes.md 写入了违禁内容（实绩 / 播放数）\"。这些内容应在 rubric-memo.md，不在 rubric_notes.md\n\n### Phase 6: 校准样本批量更新\n\n对每个校准样本的 prediction 文件，**底部追加**（不动预测段、不动复盘段）：\n\n```markdown\n\n---\n**Re-scored under v2.1 on 2026-05-04**: composite=8.24 → 9.11 (blind: true)\n（rubric bump 时全量重算，由 cheat-score-blind sub-agent 独立打分；详见 rubric-memo.md 的 v2 → v2.1 升级 Memo）\n```\n\n`blind: true` 字段**必填**——告诉未来读这条记录的人\"这是 channel B 隔离打分，不是主 Claude 自评\"。如果某条 prediction 在 Phase 2 因 sub-agent 失败被排除 → 不会有 Re-scored 行（保持原样）。\n\n用 Edit 工具，匹配每个文件的最末尾。\n\n### Phase 7: 更新 state file\n\n```json\n{\n  \"rubric_version\": \"v2.1\",\n  \"last_bump_at\": \"<ISO timestamp>\",\n  \"last_bump_self_audited\": false,\n  \"consecutive_directional_errors\": [],\n  \"calibration_samples_at_last_bump\": <current value>\n}\n```\n\n清空 `consecutive_directional_errors`——新 rubric 重新计数。\n\n### Phase 8: 控制台报告\n\n```\n✅ Rubric 已升级 v2 → v2.1\n\n变化：\n- ER ×1.5 → ×2.0\n- SR ×1.5 → ×1.0\n- 新增 MS / TS\n- 删除 NA / AB\n\n校准池重打：5/5 通过排序检查（4/5 一致 + 0 pairwise 回归）\n跨模型审核：✅ PASS\nCleanup pass：删除观察 D 和 E（已吸收为 QL 重定义和 MS 维度）\n\n下一篇预测起按 v2.1 公式打分。\n所有历史预测文件已追加 Re-scored 标记。\n```\n\n---\n\n## Phase B：bucket-only 重校（轻量分支）\n\n`/cheat-bump --bucket-only [--scheme ratio|absolute|percentile]`\n\n**与完整 bump 的本质区别**：bucket 边界不是规则的一部分，是数据派生量。重新派生它**不需要跨模型审核**——派生算法是确定性的，没有\"判断\"成分。\n\n### B1: 选择算法（按可用样本数自动派生，**state 不存 scheme**）\n\n| 算法 | 适用 | 边界派生方式 |\n|---|---|---|\n| `ratio`（默认 N=1-4） | 小样本 | 上一篇 / 最近 3 篇中位数 × {0.3 / 1 / 3 / 10 / 30} |\n| `absolute`（默认 N=5-9）| 中等样本 | 校准池中位数 × {0.3 / 1 / 3 / 10 / 30}，固定边界 |\n| `percentile`（默认 N≥10）| 大样本 | 校准池实绩 percentile {30 / 60 / 85 / 95 / 100} |\n\n`--scheme` 参数允许用户**显式覆盖默认**：\n- `--scheme ratio` 强制用 ratio（即使 N≥5）\n- `--scheme absolute` 强制用 absolute\n- `--scheme percentile` 强制用 percentile（要求 N≥3，否则报错）\n\n未指定 `--scheme` → 按上表自动派生。\n\n> 旧设计有 `bucket_scheme` state 字段——v1.1 删了。所有 skill 实时按 calibration_samples 派生算法，不需要持久化\"当前用哪个\"。这避免了\"切换 scheme 后忘了同步\"的状态不一致问题。\n\n### B2: 派生新边界\n\n读 `predictions/*.md` 中所有有 `actual_plays` 的样本。\n\n**ratio 模式**：\n```\nbaseline = median(最近 3 篇 actual_plays)\nbuckets = {\n  \"退步\": (-inf, baseline * 0.3),\n  \"持平\": (baseline * 0.3, baseline * 1),\n  \"命中\": (baseline * 1, baseline * 3),\n  \"小爆\": (baseline * 3, baseline * 10),\n  \"大爆\": (baseline * 10, +inf),\n}\n```\n\n**absolute 模式**：\n```\nbaseline = median(全部校准池 actual_plays)\nbuckets = {\n  \"底部\": (-inf, baseline * 0.3),\n  \"基础盘\": (baseline * 0.3, baseline * 1),\n  \"命中\": (baseline * 1, baseline * 3),\n  \"爆款\": (baseline * 3, baseline * 10),\n  \"现象级\": (baseline * 10, +inf),\n}\n```\n\n**percentile 模式**：\n```\nsorted_plays = sorted(全部校准池 actual_plays)\nbuckets = {\n  \"底部\":   ≤ p30,\n  \"基础盘\": p30 - p60,\n  \"命中\":   p60 - p85,\n  \"小爆\":   p85 - p95,\n  \"大爆\":   ≥ p95,\n}\n```\n\n### B3: 报告变化 + 用户确认\n\n```\n当前 bucket scheme: ratio\nproposed scheme: absolute\nbaseline: 4.2w 中位数（基于 5 篇校准样本）\n\n新边界：\n- 底部:   < 1.3w\n- 基础盘: 1.3w - 4.2w\n- 命中:   4.2w - 12.6w\n- 爆款:   12.6w - 42w\n- 现象级: > 42w\n\n派生说明：\n- 5 篇实绩：1.5w / 3.8w / 4.2w / 5.6w / 18w\n- 中位数 4.2w，新桶按 ×{0.3, 1, 3, 10} 派生\n\n确认应用？(yes / no)\n```\n\n### B4: 落地\n\n用户确认后：\n1. 编辑 `rubric_notes.md` 的 \"Bucket 方案\" 段，替换为新表\n2. 更新 `.cheat-state.json` 的 `baseline_plays` 字段（bucket scheme 不持久化——下次 cheat-predict 实时派生）\n3. 在 `rubric_notes.md` 的 bucket 段顶部追加一行变更记录：`v2 buckets recalibrated on YYYY-MM-DD: scheme=absolute, baseline=4.2w (基于 N=10 个样本)`\n4. **不**修改任何 prediction 文件——历史预测的 bucket 标签保持原样（在该样本写入时的方案下做出的判断）\n\n### B5: 对未来预测的影响\n\n下一次 `/cheat-predict` 起按新 bucket 派生。历史 prediction 文件里的 bucket 标签**不重算**——bucket 是预测时的语义判断，事后改写会破坏盲度。\n\n### Phase B 不做的事\n\n- 不重打 composite（公式没变）\n- 不重新审核观察段（rubric 没变）\n- 不调跨模型审核（确定性派生无需判断）\n- 不要求严格的样本数门槛（按 READINESS_HEURISTIC 由 Claude 判断；ratio 模式 N=1 就能跑）\n\n---\n\n## Key Rules\n\n1. **5 步不可跳**（仅完整 rubric bump）。任何\"先简化跑一下\"的请求都拒绝\n2. **THRESHOLD 写死**（仅完整 rubric bump）。不允许动态调整\n3. **跨模型审核是默认**（仅完整 rubric bump）。关闭审核需要在 state file 显式标记\n4. **cleanup pass 是 bump 的一部分**（仅完整 rubric bump）。不允许 bump 完不清理观察段\n5. **REQUIRE_CONFIRM**（两种模式都要）。最后落地前必须用户明确说 \"yes, bump\" 或 \"yes, recalibrate\"\n6. **bucket 重校不动历史预测**。bucket 是预测时语义，事后改写破坏盲度\n\n## Refusals\n\n- 「跳过校准池重打，直接换公式」 → 拒绝。原则 #2\n- 「跳过 cheat-score-blind sub-agent，主 Claude 直接重打就行」 → 拒绝。bump **不接受**任何 self-scored fallback——sub-agent 不可用 → abort bump，不接受\"自审\"\n- 「跳过外部 LLM 审核」 → 仅当 `CROSS_MODEL_AUDIT=false` 显式设置\n- 「这次 THRESHOLD 调到 3/5 让它过」 → 拒绝。改 THRESHOLD 是元层级 bump\n- 「保留所有旧观察作为历史」 → 违反原则 #3\n- 「先 bump，cleanup 下次再做」 → 拒绝。cleanup 是 bump 的一部分\n- 「只重算 composite 不重打 dim」 → 拒绝。新权重 × 旧 dim 仍是旧污染。每个 dim 都由 sub-agent 重审 script\n- 「把 Memo 全文写进 rubric_notes.md 顶部，方便我读」 → 拒绝。rubric_notes.md 是 blind sub-agent 白名单——含视频名 / 实绩 → 通过白名单泄漏。Memo 写 rubric-memo.md（白名单**外**），rubric_notes.md 只放公式 + 通用语言维度定义 + 指针\n- 「派生证据段保留真实视频名，让 rubric 读起来更具体」 → 拒绝。在 rubric_notes.md 必须用通用语言（\"高抽象密度样本\"）；带视频名的派生证据写 rubric-memo.md\n\n## Integration\n\n- 上游：`/cheat-retro` 检测到 ≥3 同向偏差 → 提议跑 `/cheat-bump`\n- 依赖：`mcp__llm-chat__chat`（如配置）+ Task tool（spawn cheat-score-blind）\n- 修改：\n  - `rubric_notes.md`（结构性更新，**绝不**写真实视频名 / 实绩）\n  - `rubric-memo.md`（**新**——append Memo 全文，含证据 + 派生证据）\n  - 所有 `predictions/*.md`（追加 Re-scored 行，不动预测段）\n  - `.cheat-state.json`\n- 下游：下一篇 `/cheat-predict` 自动按新 rubric_version 打分\n","frontmatter":{"name":"cheat-bump","description":"提议并执行 rubric 或 bucket 升级。两种模式：**完整 rubric bump**（最高风险动作，5 步强制 + 跨模型审核）和 **--bucket-only 轻量重校**（只换 bucket 边界，不动 rubric 公式）。**Phase 2 强制走 cheat-score-blind sub-agent 给校准池重打分**——不接受 self-scored fallback。触发词：\"升级 rubric\"/\"bump rubric\"/\"更新公式\"/\"我想加一个维度\"/\"调整权重\"/\"重校桶\"/\"recalibrate bucket\"。","argument-hint":"--propose \"<...>\" | --bucket-only [--scheme ratio|absolute|percentile]","allowed-tools":"Bash(*), Read, Write, Edit, Glob, Grep, Skill, Task, mcp__llm-chat__chat","allowedTools":["Bash(*),","Read,","Write,","Edit,","Glob,","Grep,","Skill,","Task,","mcp__llm-chat__chat"]},"allowedTools":["Bash(*),","Read,","Write,","Edit,","Glob,","Grep,","Skill,","Task,","mcp__llm-chat__chat"],"isInternal":false,"tokens":8002,"sizeBytes":21594},{"name":"SKILL.md","path":"skills/cheat-init/SKILL.md","rawUrl":"https://raw.githubusercontent.com/XBuilderLAB/cheat-on-content/HEAD/skills/cheat-init/SKILL.md","title":"cheat-init","category":"anthropic-skill","format":"markdown","content":"---\nname: cheat-init\ndescription: cheat-on-content 的首次 onboarding 与脚手架创建器。统一流程——所有用户都走相同 5 阶段闭环，唯一区别是\"发过视频的人\"会在 init 时多一步：抓取已有视频建立历史 context（用于后续 cheat-seed 给更贴合的选题、更准的 baseline）。触发词：\"初始化\"/\"init\"/\"首次使用\"/\"我是新用户\"/\"setup cheat-on-content\"。**必须在用户第一次会话执行；其他子 skill 在 .cheat-state.json 不存在时自动路由到此。**\nargument-hint: \"[— form: opinion-video|long-essay|short-text|podcast]\"\nallowed-tools: Bash(*), Read, Write, Edit, Glob, WebFetch, Skill\n---\n\n# /cheat-init — 首次 onboarding\n\n让用户从零到能跑第一篇预测，全程 ≤ 5 分钟（没发过历史的）或 ≤ 10 分钟（已发过、要 import 历史的）。\n\n## Overview\n\n```\n[用户首次说\"初始化\"]\n  ↓\n[Phase 0: 检测当前状态]\n  ↓\n[Phase 1: 首屏文案 — 适用性 + 期望管理]\n  ↓\n[Phase 2: 6 个问题（Q1-Q5 都问；Q2 决定是否走 user-history import）]\n  ↓\n[Phase 2.5: 对标账号 — 强烈建议（cold-start 必须问，已发用户可选）]\n  ↓\n[Phase 3: 创建脚手架（含 scripts/ + videos/ + samples/ 空目录 + 模板文件含 benchmark.md）]\n  ↓\n[Phase 3.5: user-history import 流程（仅 Q2=有发过历史 + 用户同意）]\n  ↓\n[Phase 4: 测试 hook 是否生效]\n  ↓\n[Phase 5: 给\"下一步该说什么\"清单]\n```\n\n## Constants\n\n- **DEFAULT_RETRO_WINDOW_DAYS = 3**\n- **INSTALL_HOOKS = ask** — 默认询问；用户选 `auto` 直接装；`skip` 不装\n- **TREND_DEFAULT_SOURCES = [\"manual-paste\"]**\n\n## Inputs\n\n无。所有信息从 6 个对话问题里收集。\n\n## Workflow\n\n### Phase 0: 检测当前状态\n\n1. 读用户当前工作目录（**用户的 content project，不是 cheat-on-content 自己**）\n2. 检查是否已存在 `.cheat-state.json`：\n   - 存在 → 提示\"项目似乎已初始化（state file 存在）。要重新初始化会覆盖现有配置——确认？\" 等用户明确确认才继续\n   - 不存在 → 进入 Phase 1\n3. 检查是否已存在 `rubric_notes.md` / `predictions/` 等核心文件——存在但 state file 不存在 → 是\"半初始化\"状态，提示用户并询问\"要从现有文件推断状态还是重置？\"\n\n### Phase 1: 首屏直白告知期望（含适用性验证）\n\n向用户输出（一字不漏，不要软化）：\n\n```\n🎯 Cheat on Content / 网红外挂 — 初始化\n\n你的下一条内容已经在改写 3 个月后的你。\n规律是客观存在的，区别是你**看见**还是**没看见**。\n这套让你看见。\n\n接下来 5-10 分钟我会问你 5-6 个问题搞清楚你做什么、有什么、怎么用。\n两件事先说在前面：\n\n1. **早期预测会不准**——前 5 篇精度大概 ±50%，这是数学事实。\n   工具用 🔴🟠🟡🟢🔵 标 confidence 等级，不藏数字——\n   你自己判断这次能不能信。\n\n2. **强烈建议导对标账号**——5-10 条对标视频，工具立刻有 anchor。\n   不然第一批预测基本是占星。后面 Q5 会再问一次。\n\n准备好开始吗？\n```\n\n如果用户答\"继续\"或类似肯定回应 → Phase 2。\n不再因为 content_form 拒绝继续——任何形态都允许，只是 `rubric_form_mismatch` 字段标真，cheat-status 后续会持续提示用户\"你的形态需要 bump 调权重\"。\n\n### Phase 2: 6 个问题（一问一答，**不**批量提问）\n\n**Q1: 内容形态**\n\n> \"你的内容更接近哪一种？\n> a) **观点视频**（评论 / 时评 / 论说 / 议题讨论 / 个人观点）— 直接匹配内置 rubric\n> b) **长文 essay**（公众号 / Substack / Medium）— 可借观点视频 rubric 起步，bump 时调权重\n> c) **短文 / thread**（X / 微博 / 即刻）— 同上\n> d) **播客 / 视频长内容**（YouTube 长片 / 播客）— 同上\n> e) **教程 / 工具教学 / Builder**（教别人怎么用 X 工具 / 怎么做 Y 项目）— 同上\n> f) **其他**（游戏 / 美食 / 妆教 / 新闻 / 剧情）— 工作流通用，但 rubric 维度需要调\n>      （ER / SR / HP 这套对你形态可能不太预测，需要自己拆出适合的维度）\n> g) **混合**\"\n\n记录到 `content_form` + `rubric_form_mismatch`。\n\n**Q1 → `content_form` enum 映射**（**必须存 enum 值，不是字母**）：\n\n| 用户答 | `content_form` 写入值 |\n|---|---|\n| a | `\"opinion-video\"` |\n| b | `\"long-essay\"` |\n| c | `\"short-text\"` |\n| d | `\"podcast\"` |\n| e | `\"tutorial-builder\"` |\n| f | `\"other\"` |\n| g | `\"mixed\"` |\n\n`rubric_form_mismatch` 派生：\n- 选 a → `false`\n- 选 b/c/d/e/f/g → `true`，cheat-status 持续提示\"你的形态可能需要 bump 调权重\"\n- **不再有\"严重不匹配\"档**——所有形态都能跑工作流，只是有的 rubric 需要更激进的 bump\n\n**Q1.5: 典型时长**（仅 Q1=a/d/f 时问）\n\n> \"你的视频典型时长？\n> a) 30秒-1分钟  b) 1-3分钟  c) 3-5分钟（推荐起步）\n> d) 5-10分钟   e) 10分钟以上\"\n\n记录到 `typical_duration_seconds`（30 / 90 / 240 / 450 / 900）。\n\n**Q1.6: 发布频率**\n\n> \"你打算多久发一篇？\n> a) 日更   b) 隔日   c) 每周   d) 灵活 / 不固定（关闭 buffer 监控）\"\n\n记录到 `target_publish_cadence_days`（1 / 2 / 7 / null）。\n\n**Q2: 你这个频道发过视频吗？**\n\n> \"a) 没发过 — 我会帮你从兴趣 + 热点 brainstorm 5 个候选 + 写 5 份初稿\n>  b) 发过 — 不管 1 条还是 100 条，我会帮你抓历史让后续 brainstorm 更贴合你做过什么\"\n\n如选 a → state 写 `calibration_samples: 0`，**Phase 3.5 跳过**，直接进入 Phase 4。\n如选 b → 进入 **Q2.1**。\n\n**Q2.1: 平台 + 抓取计划**（仅 Q2=b）\n\n> \"你内容主要在哪个平台？\n> a) 抖音 — 装 douyin-session adapter（Playwright + 扫码登录抖音创作者中心）\n> b) 小红书 — 装 xhs-explore adapter（Playwright + 扫码登录小红书创作者中心）\n> c) YouTube — 装 youtube-data-api adapter（需 API key）\n> d) B 站 — bilibili-stat adapter\n> e) LinkedIn — 装 linkedin-session adapter（Playwright + 登录 LinkedIn，抓单帖分析）\n> f) 微信视频号 — 装 wechat-channels adapter（Playwright + 扫码登录视频号助手，仅抓自己账号后台数据）\n> g) 其他 / 多平台 — 走 manual paste 模式\"\n\n如选 a/b/c/d/e/f → 询问 Q2.2；如选 g → 跳到 Q2.3 manual。\n\n**Q2.2: adapter 安装时机**（仅 Q2.1=a/b/c/d/e/f）\n\n> \"现在装 adapter 自动抓取，还是先手动告诉我？\n> - 现在装 — 引导你装 Playwright + 扫码 → 抓回最近 N 条数据\n> - 等下再装 — 先 manual 模式，state 标 'pending_adapter_setup'，\n>            cheat-status 持续提示装\"\n\n如选\"现在装\"→ 走 adapter install 引导（详见各 adapter README）→ 验证抓取可用 → Q2.3。\n如选\"等下\"→ 跳到 Q2.3 manual。\n\n**Q2.3: 抓取范围 / 历史规模**\n\n如 adapter 已装并验证可用：\n> \"我可以抓你最近多少条作为基础？\n> （建议 10-25 条；样本越多 baseline 越准。最多到你账号实际数量）\"\n→ 用户给数字 N，Phase 3.5 抓取 N 条\n\n如 manual 模式：\n> \"你大概发过多少条？给个范围就行（比如 '5-10 条' / '20+ 条'），\n>  这只用来标 calibration_samples 估值，不用准确。\"\n→ 用户给一个估值，Phase 3.5 跳过抓取，calibration_samples 写估值\n\n**Q3: 数据回收方式**\n\n> \"T+3 天复盘怎么拿数据？\n>\n> a) 手动粘 — 候补方案。**你必须粘 top 20+ 评论（带赞数）**，不是只粘播放数。\n>    评论才是真信号——'她不一样'这种模因爆发只能从评论看出，\n>    播放数永远告诉不了你什么内容真的击中了观众。\n> b) **[推荐默认]** adapter 自动抓 — 评论 + 数据全要。\n>    如果你现在没装 adapter，没关系，state 标 'pending_adapter_setup'，\n>    第一次 publish 之前装上就行（cheat-status 会持续提醒装）。\n>    装的指引在 adapters/perf-data/<platform>/README.md。\"\n\n**Q3 → `data_collection` enum 映射**：\n\n| 用户答 | `data_collection` 写入值 |\n|---|---|\n| a | `\"manual\"` |\n| b（默认） | `\"adapter\"` |\n\n默认推荐 b——除非用户明确说 \"a 我就要手动\"。\n\n**Q4: 候选选题**\n\n> \"你现在有候选选题列表吗？（如有外部 markdown / Notion 维护的）\n> a) 没有（默认）— 一会儿我帮你 brainstorm，或日常用 /cheat-trends 抓\n> b) 有，markdown 列表\n> c) 有，Notion / 其他\"\n\n**Q4 → `pool_status` enum 映射**：\n\n| 用户答 | `pool_status` 写入值 |\n|---|---|\n| a（默认） | `\"none\"` |\n| b | `\"markdown\"` |\n| c | `\"notion\"` |\n\n**Q5: 装几个 hook（默认装，不需要你决定）**\n\n> \"Q5：我顺便装几个 hook，回 'yes' 或 'enter' 就装：\n>\n> 1. **预测锁** — 我们一起做完预测后，文件被锁。你或我都不能改预测段。\n>    复盘只能往同一文件下半段追加，不污染上半段判断。\n>    （没这个锁，事后看到数据想\"修一下当时的预测\"几乎是必然的——你或我都会犯）\n>\n> 2. **SessionStart 自动报告** — 每次开新会话顶部显示 buffer / 待复盘 / 候选 top\n>\n> 3. **静默使用日志** — 异步记录使用频率，不阻塞，给将来诊断用\n>\n> 三个一起装。**不装也可以**（回 'no'）但你失去预测锁，校准价值会下降。\n>\n> 回 yes / no。\"\n\n**Q5 → `hooks_installed` 映射**：\n\n| 用户答 | `hooks_installed` 写入值 |\n|---|---|\n| yes / enter / 默认 | `true`（bool，**不是字符串 `\"yes\"`**） |\n| no | `false` |\n\n默认 yes——除非用户明确说 no。\n\n### Phase 2.5: 对标账号（**所有用户都问**，cold-start 强烈建议）\n\n> 工具早期最重要的信号源是**对标账号**——你 init 完没数据，rubric 等权 v0 等于占星。\n> 但如果你能找一个你想做成那样的账号，导入 5-10 条它的高 / 中 / 低样本，工具就有了 anchor。\n\n询问：\n\n```\n🎯 对标账号\n\n你能找一个对标账号吗？至少 3 条该账号的视频。\n\n  - 你**完全没发过历史**（Q2=a）→ **强烈建议**——rubric 没 anchor 全靠对标。\n    不找的话用通用 v0 等权 rubric，前 5 篇精度更差更久\n  - 你**已发历史**（Q2=b）→ **可选**——你也可以只用自己历史 calibrate；\n    但建议至少导 1 个对标做 sanity check（看你账号是否真的偏离对标方向）\n\na) 现在找 → 立刻进入 /cheat-learn-from（5-15 分钟，看你材料准备程度）\nb) 等下找 → state 标 `benchmark_status: pending`，cheat-status 持续提醒\nc) 不找 → state 标 `benchmark_status: none`，用通用 v0 起步\n\n回 a / b / c。\n```\n\n行为：\n- 选 a → Phase 3 创建脚手架完毕后，**自动 dispatch 到 /cheat-learn-from**（不让用户手动跑——已经在 init 流程里了）。完成后回 init Phase 4\n- 选 b → state 标 `benchmark_status: pending` + `benchmark_name: null`\n- 选 c → state 标 `benchmark_status: none`\n\n记录到 `benchmark_status` / `benchmark_name`（如 a 选则在 cheat-learn-from 里写入）。\n\n### Phase 3: 创建脚手架（逐项解释）\n\n按顺序创建并**解释每一项的作用**：\n\n0. **`.gitignore`（安全 — 必须第一步创建）**\n   ```\n   \"先创建 .gitignore，把账号凭证挡在版本控制外——这是第一件事。\n    .auth/ / .auth-xhs/ / .auth-linkedin/ / .auth-wechat-channels/ 存的是各平台登录态（等同账号密码），\n    .cheat-secrets.json 存 API key / cookie——一旦被 commit 或云同步就等于泄露账号。\n    注意：predictions/ videos/ scripts/ 这些**不**忽略——原则 #1/#3 依赖\n    git history 作为预测的不可变档案，必须入库。\"\n   ```\n   - 复制 `cheat-on-content/templates/gitignore.template` → `<user-repo>/.gitignore`\n   - 如 `<user-repo>/.gitignore` **已存在** → 不覆盖；逐行检查并**追加缺失行**，至少确保\n     `.auth/`、`.auth-xhs/`、`.auth-linkedin/`、`.auth-wechat-channels/`、`.cheat-secrets.json` 五行存在\n   - 即使用户项目当前**还不是 git 仓库**也照常创建——它会在用户 `git init` 的那一刻立即生效\n   - 创建后提醒一句：如果项目已经 `git init` 过且可能误加过 `.auth/`，让用户跑\n     `git rm -r --cached .auth .auth-xhs .auth-linkedin .auth-wechat-channels .cheat-secrets.json` 把已暂存的凭证移出\n\n1. **`.cheat-state.json`**\n   ```\n   \"正在创建 .cheat-state.json — 各子 skill 共享上下文的地方。\n    这次 init 收集的所有答案都会写在这里。\"\n   ```\n   写入（**所有 `<...>` 占位必须查上面 Q 的映射表换成具体 enum 值，绝不直接存字母**）：\n   ```json\n   {\n     \"schema_version\": \"1.4\",\n     \"skill_version\": \"1.0.0\",\n     \"rubric_version\": \"v0\",\n     \"content_form\": \"<查 Q1 映射表，写 enum 字符串如 \\\"opinion-video\\\">\",\n     \"typical_duration_seconds\": <Q1.5 派生：30/90/240/450/900>,\n     \"target_publish_cadence_days\": <Q1.6 派生：1/2/7/null>,\n     \"rubric_form_mismatch\": <Q1=a→false；其他→true>,\n     \"benchmark_status\": \"<Phase 2.5 派生：a→\\\"imported\\\"/b→\\\"pending\\\"/c→\\\"none\\\">\",\n     \"benchmark_name\": <imported 则字符串名，否则 null>,\n     \"benchmark_sample_count\": <imported 则数字，否则 0>,\n     \"baseline_plays\": null,\n     \"calibration_samples\": <Q2=a→0；Q2=b→Phase 3.5 import 回填或 Q2.3 估值>,\n     \"data_collection\": \"<查 Q3 映射表，写 \\\"manual\\\" 或 \\\"adapter\\\">\",\n     \"pool_status\": \"<查 Q4 映射表，写 \\\"none\\\"/\\\"markdown\\\"/\\\"notion\\\">\",\n     \"data_layer\": \"markdown\",\n     \"hooks_installed\": <查 Q5 映射表，写 bool true/false>,\n     \"enabled_trend_sources\": [\"manual-paste\"],\n     \"enabled_perf_adapters\": <Q2.1=a→[\\\"douyin-session\\\"]；b→[\\\"xhs-explore\\\"]；c→[\\\"youtube-data-api\\\"]；d→[\\\"bilibili-stat\\\"]；e→[\\\"linkedin-session\\\"]；f→[\\\"wechat-channels\\\"]；其他→[]>,\n     \"last_bump_at\": null,\n     \"last_bump_self_audited\": false,\n     \"last_published_at\": null,\n     \"last_published_file\": null,\n     \"last_retro_at\": null,\n     \"last_trends_run_at\": null,\n     \"last_trends_added_count\": 0,\n     \"last_prediction_self_scored\": false,\n     \"last_self_scored_at\": null,\n     \"consecutive_directional_errors\": [],\n     \"pending_retros\": [],\n     \"shoots\": [],\n     \"in_progress_session\": null,\n     \"initialized_at\": \"<本地 ISO 8601 含时区，如 \\\"2026-05-05T20:11:13+08:00\\\"，**不要用 UTC 的 Z 后缀**>\"\n   }\n   ```\n\n2. **`rubric_notes.md`**\n   ```\n   \"正在创建 rubric_notes.md — 你的评分维度的真实来源。\n    用的是 v0 占位 rubric——等权 7 维（每个维度同等重要）。\n    \n    为什么叫 v0：v0 是没校准前的占位。你的账号自己的真权重要从你\n    的数据反推，不是预设。跑完 5 篇有数据的内容后，会自动提议\n    升级到「校准 v1」（你的第一个真正校准过的 rubric）。\n\n    ⚠️ rubric_notes.md 是 blind sub-agent (channel B) 的白名单文件——\n    只能含通用语言（公式 / 维度定义 / bucket 边界），不能含真实视频名 / 实绩。\n    每次 bump 升级时的 Memo（含证据数据 + 派生证据）写到 rubric-memo.md（下一步创建）。\"\n   ```\n   - 复制 `cheat-on-content/starter-rubrics/<form>-zero.md`（cold-start）或 `<form>.md`（已有数据时仍可参考）\n\n2.5. **`rubric-memo.md`**（**新**——配合 cheat-score-blind 隔离协议）\n   ```\n   \"正在创建 rubric-memo.md — bump 升级 Memo 累积档案。\n    这是 cheat-bump Phase 5 写入 Memo 全文（含真实视频名 + 实绩 + 派生证据）的位置。\n\n    为什么单独一个文件：blind sub-agent 的白名单是 rubric_notes.md，\n    历史上 bump Memo 写进 rubric_notes.md 会让 blind sub-agent 通过白名单\n    拿到本该看不到的实绩数据——本文件是隔离修复，sub-agent 硬禁读本文件。\n    \n    现在是空的，等第一次 cheat-bump 升级后 append 第一段 Memo。\"\n   ```\n   - 复制 `cheat-on-content/templates/rubric-memo.template.md` → `<user-repo>/rubric-memo.md`\n\n3. **`script_patterns.md`**\n   ```\n   \"正在创建 script_patterns.md — 你的写作 pattern 沉淀（与 rubric 解耦）。\n    rubric_notes.md 教 Claude 怎么打分；\n    script_patterns.md 教 Claude 怎么写。\"\n   ```\n   - 复制 `cheat-on-content/templates/script_patterns.template.md`\n\n4. **四个目录**：`scripts/` + `predictions/` + `videos/` + `samples/`（都加 `.gitkeep`）\n   ```\n   \"正在创建四个目录：\n    \n    scripts/      — 拍前的草稿（cheat-seed 写或你写）\n    predictions/  — immutable 预测日志（hook 保护）\n    videos/       — 拍后的工作目录（cheat-shoot 创建子目录）\n    samples/      — 对标账号视频 / 转录（cheat-learn-from 创建子目录）\n    \n    前三处用同一组 <date>_<id>_<short> 命名相互关联。\n    samples/ 按对标账号名分组：samples/<账号名>/<video-id>/。\"\n   ```\n\n4.5. **`benchmark.md`**（仅 Phase 2.5 选 a/b 时）\n   ```\n   \"正在复制 benchmark.md 占位模板（实际内容由 cheat-learn-from 填）—— \n    这是你的对标账号的中央 reference。\n    前期工具的 rubric / pattern / 选题方向感大量从这里推；\n    后期 N≥10 后影响淡出，但保留作 sanity check。\"\n   ```\n   - 复制 `cheat-on-content/templates/benchmark.template.md` → `<user-repo>/benchmark.md`\n   - **Phase 2.5 选 c 不创建** → benchmark.md 不存在，state 标 `benchmark_status: none`\n\n4.7. **`audience.md`**\n   ```\n   \"正在创建 audience.md — 你账号的受众画像（'谁在看'）。\n    \n    现在是空骨架。它和 rubric_notes.md 平行——rubric 教 Claude 怎么打分，\n    audience 告诉 Claude 你的观众是谁。跑够几篇复盘后跑 /cheat-persona，\n    它会从评论数据聚类出真实画像，cheat-seed 选题写稿时就有了一面镜子。\n    \n    注意：audience.md 由评论派生 → 含实绩信号 → blind 打分 sub-agent 硬禁读它。\"\n   ```\n   - 复制 `cheat-on-content/templates/audience.template.md` → `<user-repo>/audience.md`\n   - 如 Phase 2.5 选了 a/b（有 benchmark）→ Phase 5 清单里提示\"可跑 `/cheat-persona — seed-from-benchmark` 先 seed 一份未验证画像\"\n\n5. **`WORKFLOW.md`** + **`STATUS.md`**\n   - 复制 templates/ 对应文件\n\n6. **如果 Q5=是 → 安装 hooks**\n   - 读 `.claude/settings.json`（如不存在则创建空 `{}`）\n   - merge 进 `hooks/prediction-immutability.json` 的 `hooks.PreToolUse`\n   - merge 进 `hooks/session-start.json` 的 `hooks.SessionStart`\n   - merge 进 `hooks/meta-logging.json` 的 hooks（如同时启用）\n   - 复制 `prediction-immutability.sh` + `session-start.sh` + `log-event.sh` 到 `.cheat-hooks/`，chmod +x\n   - settings.json 里的 command 路径用 `${CLAUDE_PROJECT_DIR}/.cheat-hooks/`\n\n7. **(Pool 选项 c—Notion)** 仅记录到 state file 的 `pool_status: notion`，后续 cheat-trends 调用时再处理\n\n### Phase 3.5: import 流程（仅 Q2=b 且用户同意抓取）\n\n如 Q2.2=现在装 → 走 adapter install + login（详见 [adapters/perf-data/<platform>/README.md](../../adapters/perf-data/)）。\n\n抓取成功后，对每条已发视频：\n\n1. **建 video folder**：`videos/<date>_<id>_<short>/`\n   - `<date>` = 视频实际发布日\n   - `<id>` = 12 位 hash，对 (title + 平台 ID) 做 sha256\n   - `<short>` = 标题前 3-8 字\n2. **写 report.md**：从 adapter 抓回的数据（播放 / 点赞 / 评论 / 转发 / top 评论）填入\n3. **询问用户原稿**：\"video 「{标题}」 你保留了原稿吗？\"\n   - 是 → 用户提供 → 存为 `videos/<id>/script.md`\n   - 否 → 标 `script_lost`（仍建 video folder，只是 script.md 缺失）\n4. **写 reconstructed prediction**：`predictions/<date>_<id>_<short>.md`\n   - header 标 `**Reconstructed retrospective — NOT a blind prediction**`\n   - 7 维打分基于 script + 复盘段实绩数据**反向打**——明确这是非校准用途\n   - 不计入 calibration_samples（这是导入的历史，不是校准积累）\n\nimport 完成后：\n- 派生 `baseline_plays` = 抓回视频的播放中位数 → 写入 state file\n- 派生 confidence 等级 → 后续 cheat-predict 写预测时直接用\n- 输出汇总：\"已 import N 条历史。最近一条 X w 播放，中位数 Y w，已建 N 个 video folder + reconstructed predictions\"\n\n### Phase 4: 测试 hook 是否生效（仅当 Q5=是）\n\n跑一次假的 Edit 拦截测试：\n1. 创建临时文件 `predictions/_test_hook.md`，含 `## 预测\\n[test]\\n## 复盘\\n`\n2. 尝试 Edit 这个文件的 `## 预测` 段\n3. 钩子应 exit 1 阻塞 → 报告\"✅ immutability 钩子生效\"\n4. 删除测试文件\n5. SessionStart hook 验证：直接调一次 `bash .cheat-hooks/session-start.sh` → 应输出报告（即使是空的也行）\n\n如果钩子未生效 → **不要假装成功**，明确告诉用户：\"钩子安装失败，可能是 .claude/settings.json 配置没生效。建议手动检查或重启 Claude Code。\"\n\n### Phase 4.5: 如 Phase 2.5 选 a → dispatch 到 /cheat-learn-from\n\n如果用户在 Phase 2.5 选了 a（现在导对标账号）→ **自动触发 /cheat-learn-from**：\n\n```\n✅ 脚手架 + hooks 装完。\n\n下面立刻进入 /cheat-learn-from 帮你导入对标账号——\n你 init 时选了\"现在找\"，不让你又开一个会话才跑。\n\n[invoke /cheat-learn-from]\n```\n\ncheat-learn-from 完成后回到 init 的 Phase 5。\n\n如 Phase 2.5 选 b/c → 跳过 Phase 4.5，直接 Phase 5。\n\n### Phase 5: 给\"下一步该说什么\"清单\n\n```\n✅ 初始化完成（rubric: v0，calibration_samples: <N>，confidence: <emoji 等级>）\n\n下次你可以直接说这些：\n\n📝 写完一篇稿子 → \"打分这篇 scripts/<...>.md\"\n🎯 准备发布前  → \"启动预测 scripts/<...>.md\"\n🎬 拍完了      → \"拍了 scripts/<...>.md\" → 建 video folder + buffer +1\n🚀 发布后      → \"已发布 https://...\"\n📊 T+3 天      → \"复盘 videos/<...>/\"\n📈 任何时候    → \"状态\"（看完整看板）\n\n<如果 Q4=没有候选选题:>\n🌱 现在跑 /cheat-seed 找选题？\n   - 没发过历史的：纯 brainstorm（兴趣 × 热点）\n   - 发过历史的（已 import）：brainstorm 会基于你过去做过什么给推荐\n   回 \"yes, seed\" 立刻跑，回 \"no\" 你自己想。\n\n💡 你的 confidence 是 <当前等级> —— 它会随着你跑更多复盘自动提升。\n   不要因为 confidence 低就跳过预测——预测的纪律本身就是工具的核心，\n   早期预测的\"价值\"是数据采集，不是决策。第 5 次复盘后 rubric 第一次校准，\n   confidence 会跨入 🟡 偏低；第 10 次后 🟢 中。\n```\n\n## Key Rules\n\n1. **不假装成功**：任何步骤失败 → 明确告诉用户哪一步出错。绝不写\"✅ 初始化完成\"如果实际没完成\n2. **不批量提问**：5 个问题一次问一个\n3. **不静默 mkdir**：每创建一个文件都解释它的作用\n4. **不强推 SQLite**：所有用户给 markdown，提一句\"将来到 30 篇会建议升级\"就够了\n5. **state 字段统一**：删掉 mode / prediction_complexity / bucket_scheme 等枚举字段——单一用 calibration_samples 整数 + confidence 派生\n6. **import 失败不阻塞**：Q2=b 但 adapter 装失败 / 抓取失败 → 优雅降级到\"标 calibration_samples 估值，不导入历史 video folder\"\n\n## Refusals\n\n- 「跳过 Q1-Q5，直接给我创建所有文件」 → 拒绝。问题答案直接影响默认配置（content_form、cadence、hooks）\n- 「我已经在别处初始化过了，把那个项目的配置同步过来」 → 慎重。提示用户手动 cp 现有 `.cheat-state.json` 和 `rubric_notes.md`，不自动跨项目同步\n- 「不装 hook 但保留 immutability 承诺」 → 允许，state 标 `hooks_installed: false`，cheat-status 持续提示\"你的 immutability 是君子协定\"\n\n## Integration\n\n- 写完后，主 SKILL.md 的路由就解锁了所有其他子 skill\n- `cheat-status` 读 `.cheat-state.json` 的 `calibration_samples` 字段决定显示哪个 confidence 等级\n- 如 Q2=b 走了 import → 历史 reconstructed predictions 进 `predictions/` 和 `videos/<...>/`，但**不**计入 calibration_samples（不是真校准样本）\n- `/cheat-seed` 读 `predictions/` 的所有历史 reconstructed prediction → brainstorm 时知道\"用户过去做过什么\"\n\n## State 字段写入清单\n\n| 字段 | 写入时机 | 来源 |\n|---|---|---|\n| `schema_version` | Phase 3 | 硬编码 \"1.4\" |\n| `skill_version` | Phase 3 | 硬编码 \"1.0.0\" |\n| `rubric_version` | Phase 3 | \"v0\" |\n| `content_form` | Phase 3 | Q1 → 查映射表换 enum 值（**不是字母**） |\n| `typical_duration_seconds` | Phase 3 | Q1.5 派生 |\n| `target_publish_cadence_days` | Phase 3 | Q1.6 派生 |\n| `rubric_form_mismatch` | Phase 3 | Q1≠a → true |\n| `benchmark_status` | Phase 3 / 2.5 | Q2.5 答案派生 |\n| `benchmark_name` | Phase 3 / 2.5 | Q2.5 用户提供 |\n| `benchmark_sample_count` | Phase 3 / 2.5 | cheat-learn-from import 后回填 |\n| `baseline_plays` | Phase 3.5（如 import 成功） | import 数据中位数；否则 null |\n| `calibration_samples` | Phase 3 / Phase 3.5 | Q2=a→0；Q2=b→Q2.3 估值或 import 数 |\n| `data_collection` | Phase 3 | Q3 → 查映射表换 enum 值 |\n| `pool_status` | Phase 3 | Q4 → 查映射表换 enum 值 |\n| `enabled_perf_adapters` | Phase 3 | Q2.1 派生（如 Q2=a 则 `[]`） |\n| `hooks_installed` | Phase 3-4 | Q5 → bool（不是字符串） |\n| `last_bump_at` / `last_published_at` / `last_published_file` / `last_retro_at` / `last_trends_run_at` | Phase 3 | 全部 `null` |\n| `last_bump_self_audited` | Phase 3 | `false` |\n| `last_trends_added_count` | Phase 3 | `0` |\n| `last_prediction_self_scored` | Phase 3 | `false` |\n| `last_self_scored_at` | Phase 3 | `null` |\n| `initialized_at` | Phase 3 | now() 本地 ISO 8601，含 `+08:00` 时区，**不要 UTC `Z`** |\n","frontmatter":{"name":"cheat-init","description":"cheat-on-content 的首次 onboarding 与脚手架创建器。统一流程——所有用户都走相同 5 阶段闭环，唯一区别是\"发过视频的人\"会在 init 时多一步：抓取已有视频建立历史 context（用于后续 cheat-seed 给更贴合的选题、更准的 baseline）。触发词：\"初始化\"/\"init\"/\"首次使用\"/\"我是新用户\"/\"setup cheat-on-content\"。**必须在用户第一次会话执行；其他子 skill 在 .cheat-state.json 不存在时自动路由到此。**","argument-hint":"[— form: opinion-video|long-essay|short-text|podcast]","allowed-tools":"Bash(*), Read, Write, Edit, Glob, WebFetch, Skill","allowedTools":["Bash(*),","Read,","Write,","Edit,","Glob,","WebFetch,","Skill"]},"allowedTools":["Bash(*),","Read,","Write,","Edit,","Glob,","WebFetch,","Skill"],"isInternal":false,"tokens":9299,"sizeBytes":26119},{"name":"SKILL.md","path":"skills/cheat-learn-from/SKILL.md","rawUrl":"https://raw.githubusercontent.com/XBuilderLAB/cheat-on-content/HEAD/skills/cheat-learn-from/SKILL.md","title":"cheat-learn-from","category":"anthropic-skill","format":"markdown","content":"---\nname: cheat-learn-from\ndescription: 从对标账号导入 script + 数据 → 拆 pattern + 派生 base rubric 信号 → 写到 benchmark.md / script_patterns.md / rubric_notes.md。**这是工具最早期信号的来源**——cold-start 用户没自己历史时全靠对标，发过历史的用户也建议至少 1 个对标做 sanity check。触发词：\"学这个账号\"/\"拆这几个对标视频\"/\"learn from\"/\"导入对标账号\"/\"找对标\"。\nargument-hint: <账号名> [— way: a (default) | b] [— append | --replace]\nallowed-tools: Bash(*), Read, Write, Edit, Glob, WebFetch, Skill\n---\n\n# /cheat-learn-from — 对标账号导入\n\n工具早期最重要的信号源是**对标账号**——你 init 完没数据，rubric 等权 v0 等于占星。但如果你能找一个你想做成那样的账号，导入 5-10 条它的高/中/低样本，工具就有了 anchor。\n\n后期当你自己 calibration_samples ≥ 10 时，benchmark 影响自然减弱——你的真实数据成为主要信号源。但 benchmark.md **不删**，仍是 cheat-seed brainstorm 的 reference frame。\n\n## Overview\n\n```\n[用户：学这个账号 / 启动 cheat-learn-from]\n  ↓\n[Phase 0: 检查 benchmark 状态]\n  ↓\n[Phase 1: 选 input 方式（Way a 默认）]\n  ↓\n[Phase 2: 收集材料]\n  Way a: 用户粘 N 条 script 文本 + 数据\n  Way b: 用 whisper 转录 samples/ 目录里的视频\n  ↓\n[Phase 3: 询问每条样本的\"印象判断\"（高/中/低 + 为啥）]\n  ↓\n[Phase 4: Claude 拆 pattern + 派生 rubric 信号]\n  ↓\n[Phase 5: 用户 review → 改 → 落盘]\n  ↓\n[Phase 6: 写 benchmark.md / script_patterns.md / rubric_notes.md]\n  ↓\n[Phase 7: 更新 state.benchmark_status]\n```\n\n## Constants\n\n- **MIN_SAMPLES = 3** — 最少 3 条样本（少于拆不出 pattern）\n- **RECOMMENDED_SAMPLES = 5-10** — 推荐区间，平衡信号量 vs 用户工作量\n- **MAX_SAMPLES_PER_RUN = 15** — 单次导入上限——再多 Claude context 不够 + 用户也累\n- **DEFAULT_WAY = a** — Way a 简单 + 准确，是 default\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<账号名>` | 用户参数；缺失则询问 |\n| `.cheat-state.json` | 状态文件 |\n| Way a: 用户粘的 script 文本 + 数据 | 对话 |\n| Way b: `samples/<账号名>/*.mp4` 等视频文件 | 用户提前下载好放进去 |\n\n## Workflow\n\n### Phase 0: 检查 benchmark 状态\n\n读 `.cheat-state.json` 的 `benchmark_status`：\n\n| 状态 | 处理 |\n|---|---|\n| `none` | 首次导入——继续 Phase 1 |\n| `pending` | 用户之前答应等下找——继续 Phase 1 |\n| `imported` 已有 benchmark | 询问\"你已有 benchmark [当前名]，N 条样本。要做什么？  a) 追加新视频到当前 benchmark  b) 替换为新 benchmark  c) 只看不改\" |\n\n参数解析：\n- `--append` → 追加到现有 benchmark\n- `--replace <new-name>` → 用新 benchmark 替换（旧的归档到 benchmark.archived/）\n- 没标志 + 已有 benchmark → 走上面询问\n\n### Phase 1: 选 input 方式（**两个独立维度**）\n\n每条样本 = **script** + **数据**。两者怎么拿是独立的——你可以混搭。\n\n#### Phase 1a: script source（怎么拿稿子）\n\n```\nscript 怎么拿？\n\na) **粘文本（最简单，推荐）**\n   - 你自己整理过 / 用工具提取过——直接粘进对话\n   - 工具推荐（按方便程度排）：\n   \n     抖音 / 小红书：\n     - 微信小程序「轻抖」—— 粘视频链接 → 自动提取文案 + 评论。最快\n     - 类似工具：\"视频解析助手\" / \"短视频文案提取\" 等小程序\n     - 通常有免费额度，重度使用收费\n     \n     B 站 / YouTube：\n     - 视频页面有\"显示字幕/文字记录\"按钮（如果 UP 主开了）\n     - 第三方：DownSub / SaveSubs / yt-dlp --write-auto-sub\n     \n     公众号 / Substack：\n     - 直接复制网页文字\n   \nb) **whisper 转录视频文件**\n   - 你下载了视频到 samples/<账号名>/<video>/source.mp4\n   - 需要装 whisper-cpp + ffmpeg（见 adapters/script-extraction/whisper/README.md）\n   - 转录可能有错别字 / 漏字 / 标点不准——准确度比 a 差\n\nc) **跳过 script，只用元数据 + 印象**\n   - 你拿不到稿子也懒得用工具\n   - 后果：pattern 拆不深（只能看标题 / 数据 / 你的印象），但 rubric 信号还行\n   - 适合\"先快速搭起来，将来补\"\n\n回 a / b / c。\n```\n\n#### Phase 1b: data source（怎么拿播放/点赞/评论）\n\n```\n数据（播放 / 点赞 / 评论 / 转发）怎么拿？\n\na) **手填数字（最简单）**\n   - 你查一下账号后台或视频页面，告诉我数字\n   - 不需要装任何工具\n\nb) **adapter 自动抓（如已配置）**\n   - 你已经装了 perf-data adapter（如 douyin-session）\n   - 给我视频 URL，工具自己抓数据 + top 评论\n   - 评论数据更全（手填只能告诉我数字，adapter 能拿到具体评论文本）\n\n回 a / b。\n```\n\n**最常见的组合**：\n- 完全零依赖路径：1a + 1b（粘文本 + 手填）—— 5 分钟搞定\n- 评论质量优先：1a + 2b（粘文本 + adapter 抓）—— 装了 adapter 就走这个\n- 拿不到稿子兜底：1b + 1b（whisper + 手填）\n\n### Phase 2: 收集材料\n\n按 Phase 1a + 1b 的组合走对应路径。\n\n**通用纪律**：每条样本最少要有 (script 或 transcript 或 N/A标记) + 数据 (4 项基础：播放/点赞/评论/转发)。\n\n#### Path A: 粘文本（Phase 1a=a）\n\n```\n好。我们一条一条来。最少 3 条，推荐 5-10 条。\n\n第 1 条 script，把整段粘下面（段落版，不要字幕格式）：\n```\n\n收到 script → 算 video_id（sha256(script_content)[:12]）→ 进 Phase 2 数据采集。\n\n#### Path B: whisper 转录（Phase 1a=b）\n\n```\n先确认 whisper 装了：\n\n[运行 `command -v whisper-cpp` 或 `command -v whisper` 检测]\n\n如果没装：\n  ❌ whisper 没装。三选一：\n  - brew install whisper-cpp（推荐——快）\n  - pip install openai-whisper（Python 慢些）\n  - 切回 Path A 自己粘文本（推荐用轻抖等小程序）\n\n装好后：把视频文件放到 samples/<账号名>/<video-name>/source.mp4\n（一个视频一个子目录）\n\n放好后告诉我\"放好了 N 条\"，我转录。\n```\n\n用户放好后：\n1. Glob `samples/<账号名>/*/source.*` 找视频文件\n2. 对每个视频跑 `bash adapters/script-extraction/whisper/run.sh <video> samples/<账号名>/<id>/`\n3. 失败项报告但继续其他\n4. 进 Phase 2 数据采集\n\n#### Path C: 跳过 script（Phase 1a=c）\n\n直接进 Phase 2 数据采集——告诉用户\"没 script 我能拆的 pattern 仅限标题级别 / 你的印象，rubric 信号正常拆\"。\n\n#### Phase 2 数据采集（Phase 1b=a 手填 / b adapter）\n\n**如 Phase 1b=a（手填）**：\n\n```\n第 1 条数据：告诉我\n- 标题\n- 播放量\n- 点赞\n- 评论数（不是评论内容，是数字）\n- 转发 / 分享数\n\n格式随意，能识别就行。例如：\n  \"标题：怎么停止期待\n   播放：71w / 点赞 2.4w / 评论 899 / 转发 1.8w\"\n\n如果你能再粘 top 5-10 条评论（带赞数）更好——pattern 拆能挖到模因层。\n```\n\n**如 Phase 1b=b（adapter）**：\n\n```\n你说装了 adapter（如 douyin-session）。给我每条视频的 URL，\n我跑 adapter 自动抓数据 + top 评论。\n\n第 1 条 URL：\n```\n\n收到 URL → 调对应 adapter → 写数据 + 评论到 samples/<账号名>/<id>/meta.md。\n\n**通用**：继续问第 2 条 / 第 3 条 / ... 用户说\"够了\"或达到 MAX_SAMPLES_PER_RUN 时进 Phase 3。\n\n### Phase 3: 询问\"印象判断\"\n\n对每条样本（不管 Way a 或 b），收完数据后**追加问印象**：\n\n```\n你看完 / 听完这条视频的印象，算这个账号的：\n  a) 高表现样本（代表作 / 你想做成这样的）\n  b) 中表现样本（普通水准 / 不上不下）\n  c) 低表现样本（不算这个账号的代表 / 你不想做成这样）\n\n为什么？（一句话——这个判断比数据更能告诉我你想做什么风格）\n```\n\n记录 (impression_label, impression_reason) 到内存。\n\n> 注意：印象**可以**和数据冲突——比如某条数据高但用户觉得\"不算代表作\"。这种冲突本身是有用信号，记录下来。\n\n### Phase 4: Claude 拆 pattern + 派生 rubric 信号\n\n阅读所有 (script, 数据, 印象) → 自己分析：\n\n#### 4a. Script patterns\n\n按 script_patterns.md 的 cheat sheet 框架拆：\n- 开头钩子：3 种类型分布（场景代入 / IS 戏仿 / 数据反转）\n- 主体结构：几段 / 怎么切\n- 句式 / 句长 / 节奏：短句还是长句、有没有标志性句式\n- emotional 标记 / 双声道\n- 致谢段 / 收尾\n- 高频词汇 / 词汇风格\n\n输出 N 个具体 pattern（每个引用具体样本作证据）。\n\n#### 4b. Rubric 信号（**仅定性，不给数值权重**）\n\n对每条样本打 7 维分（用通用维度），然后看：\n- 高表现样本（按用户印象）共有哪些维度高？\n- 低表现样本共有哪些维度低？\n- 哪些维度在高/低样本之间无差异（说明不是关键维度）？\n\n输出**定性方向**（不是数值权重）：\n- \"ER 看起来重要\"（3/3 高样本 ER ≥4）\n- \"SR 看起来不显著\"（高低样本 SR 分布无差异）\n- \"MS 高的样本评论区有明显模因爆发\"\n\n### Phase 5: 用户 review\n\n一次性展示所有结果给用户：\n\n```\n我从你给的 N 条对标视频拆出：\n\n📝 N 个 script pattern：\n  1. **[Pattern 1 名称]**：[一句话描述] → 证据：[样本 X / Y]\n  2. ...\n  \n🎯 Rubric 定性信号：\n  - 看起来重要的维度：ER / QL / MS（每个有 N 条高样本支持）\n  - 看起来不显著的维度：SR / NA\n  - 给的初始建议：你的对标账号是 [情感+金句驱动型] / [数据驱动型] / [类比讲解型] / ...\n  - **不直接给数值权重**——5-10 样本拟合容易过拟合，先用作 tier-2 信号\n  \n🎨 选题方向感：\n  - 主题分布大概：[主题 A 40% / 主题 B 30% / ...]\n  - 调性：[一句话]\n\n回 \"ok\" 我落盘，\n或指出哪些 pattern / 信号你不认同（\"Pattern X 我觉得不准\" / \"Rubric 信号 Y 错了\"）。\n```\n\n用户反馈循环：\n- \"ok\" → Phase 6 落盘\n- 用户挑刺 → Claude 改 → 重新展示 → 直至确认\n\n### Phase 6: 落盘\n\n#### 6a. benchmark.md\n\n按 [templates/benchmark.template.md] 格式写到 `<user-channel>/benchmark.md`：\n- 账号信息（账号名、URL、调性、粉丝量级——用户提供）\n- 导入的样本表\n- 基础 rubric 派生（仅定性）\n- 选题方向感\n\n如 `--append` → 在现有 benchmark.md 的样本表追加新行 + 重新拆 pattern；不重写整个文件。\n如 `--replace` → 把现有 benchmark.md 移到 `benchmark.archived/<旧账号名>_<日期>.md`，写新的。\n\n#### 6b. samples/<账号名>/\n\n为每条样本建子目录：\n```\nsamples/<账号名>/<video-id>/\n├── source.mp4 (Way b 才有，Way a 没有)\n├── transcript.md (从粘文本写 / whisper 转出来)\n└── meta.md (标题 / 数据 / 印象 / 印象理由)\n```\n\n#### 6c. script_patterns.md\n\n在 `<user-channel>/script_patterns.md` 加新段：\n\n```markdown\n## 对标 [账号名] 借鉴（imported on YYYY-MM-DD，N 条样本）\n\n> 这些 pattern 来自对标账号——**Imported, untested on my channel**。\n> 实拍验证后（≥2 次跑出 + 复盘确认有效）再去掉这个标记，升入正式 pattern。\n\n### Pattern A: [一句话名]\n**来自**: [样本 X]\n**描述**: [详细]\n\n### Pattern B: ...\n```\n\n#### 6d. rubric_notes.md\n\n在 `<user-channel>/rubric_notes.md` 加 / 更新\"benchmark-derived initial signals\"段：\n\n```markdown\n## Benchmark-derived initial signals\n\n> 来自 benchmark.md 的对标账号 [账号名]（N=N 样本，import on YYYY-MM-DD）。\n> **仅定性方向，不直接采纳为数值权重**——5-10 样本拟合容易过拟合。\n> 等你自己 N≥5 校准样本后正式 bump 时**再决定**是否调权重。\n\n- 看起来重要的维度: ER / QL / ...\n- 看起来不显著的维度: SR / NA / ...\n- Claude 给的初始建议: [一句话定性]\n```\n\n### Phase 7: 更新 state file\n\n```json\n{\n  \"benchmark_status\": \"imported\",\n  \"benchmark_name\": \"<账号名>\",\n  \"benchmark_sample_count\": <N>\n}\n```\n\n## Key Rules\n\n1. **Way a 默认**——简单 + 准确。Way b 仅给\"找不到 script 只有视频\"的兜底\n2. **必须问印象**——纯看 transcript 拆 pattern 容易抓表面，加用户印象才挖到深层\n3. **Rubric 信号仅定性**——不直接给数值权重。5-10 样本拟合过拟合\n4. **pattern 默认标 untested**——避免污染用户自己的 pattern 库\n5. **不直接抓视频**——下载是用户的事，避免 TOS + 反爬\n6. **可重复跑**——`--append` 加新视频，`--replace` 换账号\n7. **MIN_SAMPLES = 3**：少于 3 拆不出 pattern，拒绝继续\n\n## Refusals\n\n- 「跳过印象判断，直接拆」 → 拒绝。印象是关键 input\n- 「我只能给 1 条样本」 → 拒绝。最少 3 条\n- 「直接给我数值权重」 → 拒绝。Phase 4 只给定性信号\n- 「能不能不写 transcript 文件，只在内存里拆」 → 不行。transcript 持久化是后续 cheat-retro Phase 4b diff 的依据\n- 「帮我下载对标视频」 → 拒绝。引导用户用 yt-dlp / BBDown 等工具自己下\n\n## Integration\n\n- 上游：`/cheat-init` Phase 2.5 在 cold-start 用户时**强烈建议**跑 `/cheat-learn-from`；calibration 用户**可选**\n- 上游：`/cheat-status` 在 `benchmark_status=pending` + 距 init >24h 时持续提醒\n- 下游：`/cheat-seed` brainstorm 时读 benchmark.md → 知道用户对标方向\n- 下游：`script_patterns.md` 加新段，cheat-seed 写 draft 时按 pattern 选结构\n- 下游：`rubric_notes.md` 加 benchmark-derived signals 段，cheat-bump 时作为参考之一\n- N≥10 提示：`cheat-status` 在用户 calibration_samples ≥10 时提示\"你已有足够自己数据，benchmark 影响淡出（保留作 sanity check）\"\n\n## benchmark 何时淡出\n\n不是死磕样本数，是 **Claude 判断\"用户数据信号是否已超过 benchmark\"**：\n\n- **默认参考**：calibration_samples ≥ 10 → benchmark 影响淡出\n- **可以更早**：N=5 但用户的 (打分, 实绩) 配对里出现 ≥3 条与 benchmark pattern 不一致的——说明你账号已经走出对标的路径\n- **可以更晚**：N=15 但用户的样本都很相似（都做同一类内容），benchmark 仍有信号价值\n\n判断条件 + 默认值都在 cheat-status 触发器 #19 / cheat-seed Phase 0 里实现。\n\n**淡出后**：\n- cheat-seed brainstorm 仍读 benchmark.md，但**优先级低于用户自己的 predictions/**\n- rubric_notes 的 benchmark signals 段标 `**Status: superseded by user data**`，不删但不再主导\n- benchmark.md **不删**——保留作 sanity check（看你账号是否真的偏离对标方向太远）\n\n**任何时候用户主动**：跑 `/cheat-learn-from --replace none` 完全解除 benchmark 影响\n\n## 与其他 skill 的区别\n\n| Skill | 用途 |\n|---|---|\n| `/cheat-learn-from` | **从对标账号**导入 pattern / rubric 信号（一次性 / 偶尔追加） |\n| `/cheat-seed` | brainstorm 选题 + 写 draft（读 benchmark.md 作参考） |\n| `/cheat-trends` | 抓今天的热点（与 benchmark 无关） |\n| `/cheat-bump` | 升级 rubric（用户自己 N≥5 后用真实数据，不直接用 benchmark signals） |\n","isInternal":false,"tokens":5471,"sizeBytes":15337},{"name":"SKILL.md","path":"skills/cheat-migrate/SKILL.md","rawUrl":"https://raw.githubusercontent.com/XBuilderLAB/cheat-on-content/HEAD/skills/cheat-migrate/SKILL.md","title":"cheat-migrate","category":"anthropic-skill","format":"markdown","content":"---\nname: cheat-migrate\ndescription: 把老用户的 .cheat-state.json 升级到当前 schema_version。读 migrations/registry.md 算迁移链，按顺序应用每一步迁移文件。幂等：跑两次结果一样。失败停在中间版本不前进。触发词：\"迁移\"/\"升级 state\"/\"migrate\"/\"我的 state 是老版本\"/\"schema 版本不对\"。\nargument-hint: \"[— from: <version>] [— to: <version>] [— dry-run]\"\nallowed-tools: Bash(*), Read, Write, Edit, Skill\n---\n\n# /cheat-migrate — Schema 版本迁移\n\n把用户 `.cheat-state.json` 从旧 `schema_version` 升级到 cheat-on-content 当前期望的 `LATEST_SCHEMA`。\n\n---\n\n## Overview\n\n```\n[用户：迁移 / 或 SessionStart 提示后用户跑]\n  ↓\n[Phase 0: 读 .cheat-state.json + migrations/registry.md → 确定迁移链]\n  ↓\n[Phase 1: dry-run（默认）展示迁移计划，等用户确认]\n  ↓\n[Phase 2: 备份 .cheat-state.json → .cheat-state.json.backup-<timestamp>]\n  ↓\n[Phase 3: 按顺序对每个 step 应用对应迁移文件的 HOW 段]\n  ↓\n[Phase 4: 验证升级后 state 文件能被解析 + schema_version 已更新]\n  ↓\n[Phase 5: 报告 + 提示如有备份需要清理]\n```\n\n---\n\n## Constants\n\n- **REGISTRY_PATH = `${SKILL_DIR}/../../migrations/registry.md`** — 版本链单一来源\n- **MIGRATIONS_DIR = `${SKILL_DIR}/../../migrations/`** — 迁移文件目录\n- **DRY_RUN_BY_DEFAULT = true** — 首次跑展示计划，不直接改文件\n- **BACKUP_BEFORE_WRITE = true** — 写之前必备份；备份文件保留至下次成功 init / 用户手动清理\n- **STOP_ON_STEP_FAILURE = true** — 任何 step 失败 → 停在中间版本，不前进，不回滚\n\n> 💡 调用时覆盖：`/cheat-migrate — dry-run: false` 直接执行 / `/cheat-migrate — to: 1.2` 仅升到指定版本\n\n---\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `.cheat-state.json` | 用户项目根 |\n| `migrations/registry.md` | LATEST_SCHEMA + 版本链表 |\n| `migrations/<from>-to-<to>.md` | 每步具体迁移指令 |\n\n---\n\n## Workflow\n\n### Phase 0: 确定迁移链\n\n1. 读 `.cheat-state.json` → 解析 `current_version = state.schema_version`\n2. 读 `migrations/registry.md` → 解析 `LATEST_SCHEMA` 字段（行：`LATEST_SCHEMA = \"X.Y\"`）\n3. 解析 `args.to` 覆盖（如有）；否则 target = LATEST_SCHEMA\n4. 解析 `args.from` 覆盖（罕见场景：用户的 state 文件 schema 字段坏了，强制指定起点）\n5. **状态判断**：\n   - `current_version == target` → 输出\"✅ state 已是 {target}，无需迁移\" → 退出\n   - `current_version > target`（比如用户跑了 dev 版又切回 release）→ 报错\"无法降级，请手动调整或重新 init\"\n   - `current_version < target` → 继续，从注册表查出迁移链\n6. 从注册表\"版本链\"表算出 `chain = [(from, to, file), ...]`，按顺序串起 current → target\n\n如果某一步在注册表里缺失（比如 `current_version` 不在表里）→ 报错并展示\"目前已知版本：[1.0, 1.1, ...]\"，让用户检查。\n\n### Phase 1: dry-run\n\n输出迁移计划：\n\n```\n📋 迁移计划\n\n当前版本: 1.0\n目标版本: 1.2\n将按顺序跑 2 步：\n\n  [1/2] 1.0 → 1.1（MINOR）\n       新增字段：typical_duration_seconds, target_publish_cadence_days, ...（共 12 字段）\n       删除字段：mode, prediction_complexity, bucket_scheme\n       详见: migrations/1.0-to-1.1.md\n\n  [2/2] 1.1 → 1.2（MINOR）\n       新增字段：[...]\n       详见: migrations/1.1-to-1.2.md\n\n⚠️ 备份位置: .cheat-state.json.backup-<timestamp>\n\n继续吗？回 yes 执行 / no 退出 / dry-run-detail 看每步具体改什么。\n```\n\n如 `args[\"dry-run\"] == false` 或用户回 yes → 进 Phase 2。\n\n### Phase 2: 备份\n\n```bash\ncp .cheat-state.json .cheat-state.json.backup-$(date +%s)\n```\n\n输出：\"📦 备份到 .cheat-state.json.backup-1714838400\"\n\n### Phase 3: 按顺序应用每步\n\n对 chain 里的每个 (from, to, file)：\n\n1. 输出 \"→ [N/M] 应用 {file}...\"\n2. 读 `migrations/<file>` → 找到 `## HOW (Claude steps for /cheat-migrate)` 段\n3. **按段内自然语言步骤逐项执行**——这是关键：迁移是 Claude 读 markdown 跑的，不是 python 脚本\n4. 每步完成后：\n   - 更新内存里的 state.schema_version = to\n   - **原子写**到磁盘（写 .tmp → rename）\n5. 如某步失败：\n   - 输出\"❌ {file} 第 N 步失败：{error}\"\n   - 不前进、不回滚（state 已停在前一步成功的中间版本）\n   - 提示：\"已停在 schema_version: {last_success_version}。修复后重跑 /cheat-migrate 会从这里继续\"\n   - 退出\n\n### Phase 4: 验证\n\n升完后：\n1. 读 `.cheat-state.json` → 解析 → 应能成功\n2. 检查 `schema_version == target`\n3. 检查所有\"必填字段\"非缺失（参照 [shared-references/state-management.md](../../shared-references/state-management.md) 完整 schema）\n4. 失败 → 报错\"迁移完成但验证失败：{detail}。state 文件可能不一致——查看备份恢复\"\n\n### Phase 5: 报告\n\n```\n✅ 迁移完成\n\n  从: 1.0\n  到: 1.2\n  应用步骤: 2\n\nstate 文件现在含 X 字段，全部通过验证。\n\n📦 备份保留：.cheat-state.json.backup-1714838400\n   （确认一切正常后可手动 rm；下次成功 /cheat-init 也会清理过期备份）\n\n下一步建议：\n  - 跑 /cheat-status 确认看板正常\n  - 如有 hooks 重装需求，跑 bash <skill_repo>/install.sh --reinstall-hooks\n```\n\n---\n\n## Key Rules\n\n1. **幂等**：在已升过的 state 上重跑应该立刻退出\"无需迁移\"，**不**重复应用步骤。靠对比 `current_version == target` 实现\n2. **不跳版**：1.0 → 1.3 必须按 1.0→1.1→1.2→1.3 顺序，每步独立可恢复。不允许\"直接升 1.0 → 1.3 的合并 migration\"\n3. **不静默兼容**：state 文件 schema_version 不识别 → 明确报错\"未知版本 X，最近已知版本 Y\"，不假装能继续\n4. **失败停在原地**：第 N 步失败时 schema_version 停在 N-1 已成功的版本，不回滚到迁移前。重跑能从断点继续\n5. **备份是硬约束**：写之前必有备份。即使用户跑 `--dry-run: false`，备份动作仍执行\n6. **不动 predictions / rubric / videos**：只改 `.cheat-state.json`。其他用户数据由各自 skill 负责，迁移 skill 不碰\n7. **MAJOR vs MINOR 透明**：dry-run 输出必标 (MAJOR) / (MINOR)。MAJOR 时额外提示\"老 skill 用旧字段读会出问题，迁移完不能回退到老 skill 版本\"\n\n---\n\n## Refusals\n\n- 「跳过 dry-run，立刻覆盖我的 state」 → **允许**（`--dry-run: false`），但备份仍强制执行\n- 「我的 state 损坏了 / schema_version 字段没了，能不能猜一个版本来跑」 → 允许指定 `--from: 1.0`，但要警告\"基于猜测的迁移可能导致字段错位\"\n- 「降级到旧版本（current > target）」 → 拒绝。schema 演进单向。要降级请手动 cp 历史 git 快照\n- 「合并多步迁移成一个 atomic」 → 拒绝。每步独立可恢复是设计核心\n- 「在跑 cheat-bump / cheat-predict 中途调 migrate」 → 拒绝。等其他 skill 完成再跑，避免 in_progress_session 状态被破坏\n\n---\n\n## Integration\n\n- 上游：SessionStart hook 检测 `state.schema_version != LATEST_SCHEMA` → 输出红色警告 + 建议跑 `/cheat-migrate`\n- 上游（手动）：用户 git pull 拉了新版后，看 CHANGELOG 标 BREAKING → 主动跑\n- 下游：跑完后所有其他 skill 读 state 都能拿到最新字段\n- 与 `cheat-init`：init 写新 state 时直接用 LATEST_SCHEMA，不需要走 migrate\n- 与 `install.sh --reinstall-hooks`：迁移**不**重装 hook 脚本（hook 脚本属于 skill 包代码，不属于用户 state）。这两件事解耦\n\n---\n\n## State 字段读写\n\n本 skill **写**：\n- `schema_version`（每步成功后更新）\n\n本 skill **读**：\n- 所有现有字段（取决于具体 migration 文件的 HOW 步骤）\n\n本 skill **绝不**写：\n- `calibration_samples` / `pending_retros` / `shoots` 等业务状态（这些是其他 skill 的职责）\n- 例外：迁移文件明确说\"派生新字段值时需要扫 predictions/ 算 baseline_plays\"，那是初始化新字段，不是改老字段\n\n---\n\n## Examples\n\n### 示例 1：用户从 v0.1.0 升到 v0.2.0（假设 0.2 引入 schema 1.2）\n\n```\n用户：迁移\nClaude: [跑 cheat-migrate]\n  Phase 0: current=1.1, target=1.2, chain=[(1.1, 1.2)]\n  Phase 1: dry-run 输出计划\n  用户: yes\n  Phase 2: 备份\n  Phase 3: 应用 1.1-to-1.2.md（MINOR：新增 platform_metrics_url 等字段）\n  Phase 4: 验证 OK\n  Phase 5: 报告 ✅\n```\n\n### 示例 2：用户跳了多版\n\n```\n用户：我从 v0.1.0 升到 v0.5.0，state 还是 1.0\nClaude: [跑 cheat-migrate]\n  Phase 0: current=1.0, target=1.4 (LATEST), chain=[(1.0, 1.1), (1.1, 1.2), (1.2, 1.3), (1.3, 1.4)]\n  Phase 1: dry-run 输出 4 步计划\n  ...\n```\n\n### 示例 3：迁移中途失败\n\n```\nPhase 3:\n  → [1/4] 应用 1.0-to-1.1.md ✓\n  → [2/4] 应用 1.1-to-1.2.md ✓\n  → [3/4] 应用 1.2-to-1.3.md... ❌ 失败：用户 baseline_plays 字段含非数字值，无法转 int\n  \nstate 已停在 schema_version: 1.2。\n修复 .cheat-state.json 后重跑 /cheat-migrate 会从 1.2 → 1.3 继续。\n```\n","frontmatter":{"name":"cheat-migrate","description":"把老用户的 .cheat-state.json 升级到当前 schema_version。读 migrations/registry.md 算迁移链，按顺序应用每一步迁移文件。幂等：跑两次结果一样。失败停在中间版本不前进。触发词：\"迁移\"/\"升级 state\"/\"migrate\"/\"我的 state 是老版本\"/\"schema 版本不对\"。","argument-hint":"[— from: <version>] [— to: <version>] [— dry-run]","allowed-tools":"Bash(*), Read, Write, Edit, Skill","allowedTools":["Bash(*),","Read,","Write,","Edit,","Skill"]},"allowedTools":["Bash(*),","Read,","Write,","Edit,","Skill"],"isInternal":false,"tokens":3313,"sizeBytes":9207},{"name":"SKILL.md","path":"skills/cheat-persona/SKILL.md","rawUrl":"https://raw.githubusercontent.com/XBuilderLAB/cheat-on-content/HEAD/skills/cheat-persona/SKILL.md","title":"cheat-persona","category":"anthropic-skill","format":"markdown","content":"---\nname: cheat-persona\ndescription: 从复盘评论数据派生 / 刷新账号的受众画像，写入 audience.md。这是和 rubric 平行的第二个派生物——rubric 答\"怎么打分\"，persona 答\"谁在看\"。cheat-seed 选题 / 写稿时读它。**audience.md 含实绩信号，cheat-score-blind 硬禁读**。触发词：\"构造受众画像\"/\"更新 persona\"/\"我的观众是谁\"/\"build persona\"/\"刷新受众画像\"/\"看看我的受众画像\"。\nargument-hint: \"[— seed-from-benchmark] [— rebuild]\"\nallowed-tools: Bash(*), Read, Write, Edit, Glob, Grep\n---\n\n# /cheat-persona — 受众画像派生\n\n从 `predictions/*.md` 复盘段的评论数据，聚类出账号真实受众画像，写入 `audience.md`。\n\n---\n\n## 核心定位\n\npersona 是和 rubric **平行**的第二个派生物，**不是 rubric 的一部分**：\n\n```\n复盘数据（评论 + 完播 + 转粉）\n   ├──→ rubric 进化（cheat-bump）   —— \"怎么打分\"\n   └──→ 受众画像（cheat-persona）    —— \"谁在看\"\n两者都喂给 cheat-seed，但用途不同\n```\n\n- **rubric**：这稿子会不会爆 → 喂 cheat-predict 打分\n- **persona**：谁会因为这条多看 3 秒 / 留评论 / 转发 → 喂 cheat-seed 选题 + 写稿\n\n**绝不混**：persona 不进打分公式。rubric 的 AB 维度（受众广度）可以参考 persona，但那是 cheat-bump 的事，不是这里。\n\n## ⚠️ 污染隔离（不可省）\n\n`audience.md` 从复盘评论派生 = **含已发布作品的实绩信号**。因此：\n\n- `audience.md` 在 [cheat-score-blind](../cheat-score-blind/SKILL.md) 的 hard refusal list 里，refusal_code `blocked_audience`\n- persona 影响 cheat-seed **写什么**（creative direction），不影响 cheat-predict **怎么打分**（blind sub-agent 永远不读 audience.md）\n- 这是干净的：persona 塑造的内容进了成稿，blind sub-agent 照成稿本身打分——没有 leak。leak 只会发生在 sub-agent 能读 audience.md \"因为这受众爱 X 所以加分\" 的情况，而它读不到\n\n## Overview\n\n```\n[用户：构造受众画像 / 更新 persona]\n  ↓\n[Phase 0: 收集数据 — 扫 predictions/*.md 复盘段评论 + benchmark.md]\n  ↓\n[Phase 1: 数据量判定 → 派生 Confidence 等级]\n  ↓\n[Phase 2: 评论聚类 — 自我认同 / 情绪寄存 / 反驳点 / 语言]\n  ↓\n[Phase 3: persona × rubric 交叉检验]\n  ↓\n[Phase 4: 写 audience.md（覆盖式重建，header 记 version + last_rebuilt）]\n  ↓\n[Phase 5: 控制台报告 + 跟上次画像的 diff]\n```\n\n## Constants\n\n- **AUDIENCE_PATH = audience.md** — 受众画像落盘位置\n- **MIN_RETROS_FOR_DATA_GROUNDED = 3** — 复盘数 ≥3 才算\"数据扎实\"（可基于评论质量软判断）\n- **MIN_COMMENTS_PER_TRAIT = 3** — 一条\"验证特征\"至少要 3 条评论证据，否则降到\"假设特征\"\n- **SEED_FROM_BENCHMARK = auto** — 无自己复盘数据但有 benchmark 时，seed 一份未验证画像\n\n> 💡 调用覆盖：`/cheat-persona — seed-from-benchmark`（强制用 benchmark seed）/ `— rebuild`（即使数据没变也重建）\n\n## Inputs\n\n| 来源 | 用途 |\n|---|---|\n| `predictions/*.md` 的 `## 复盘` 段 | **主数据源**——top 评论（带赞数）。persona 的金矿 |\n| `videos/*/report.md` | 完播 / 转粉率——薄信号，推\"留得住 vs 留不住\" |\n| `benchmark.md` | 冷启动 seed——\"看对标的人 ≈ 你想要的人\" |\n| `rubric_notes.md` | Phase 3 交叉检验用——persona 食欲 vs rubric 校准现实 |\n| `audience.md`（如已存在） | 上一版画像，用于 Phase 5 diff |\n\n## Workflow\n\n### Phase 0: 收集数据\n\n1. Glob `predictions/*.md`，对每个文件读 `## 复盘` 段（**只读复盘段——这是 channel A，本来就看实绩**）\n2. 抽取每篇的 top 评论（带赞数）+ 实绩 bucket\n3. 统计：有评论的复盘篇数 `N_retros`、评论总数 `N_comments`\n4. 读 `benchmark.md`（如存在）\n5. 读 `audience.md`（如已存在）→ 留作 Phase 5 diff\n\n### Phase 1: 数据量判定 + Confidence\n\n| 情况 | Confidence | 行为 |\n|---|---|---|\n| `N_retros == 0` 且无 benchmark | 🔴 无数据 | **不强行造**——告诉用户\"persona 需要复盘数据。先跑几篇 cheat-retro，或导 benchmark\"，退出 |\n| `N_retros == 0` 但有 benchmark | 🟠 benchmark-seed 未验证 | seed 一份 aspirational persona，**全文标\"未验证\"** |\n| `N_retros` 1-2 | 🟡 早期信号 | 能产出但特征多落\"假设\"段 |\n| `N_retros` 3-5 | 🟢 数据扎实 | 正常产出 |\n| `N_retros` ≥6 | 🔵 稳健 | 正常产出 + 可做更细的食欲分层 |\n\nConfidence 等级写进 audience.md header。\n\n### Phase 2: 评论聚类\n\n对收集到的所有评论，按四个维度聚类：\n\n1. **自我认同**——\"我也是…\" / \"这就是我\" / \"作为一个…\" 模式。统计哪类身份反复出现（\"大厂打工人\" / \"一人公司\" / \"考研党\" / ...）\n2. **情绪寄存**——观众来评论是为了什么情绪？被验证（\"说得太对了\"）/ 宣泄（\"我也好累\"）/ 抬杠（\"我不同意\"）/ 求助（\"那该怎么办\"）。统计占比\n3. **反驳点**——哪些观点引来稳定的反对声。这是 persona 边界\n4. **语言**——他们怎么说话。玩梗密度、真诚 vs 戏谑、有没有复制你的金句\n\n**聚类纪律**：\n- 一条\"验证特征\"至少 `MIN_COMMENTS_PER_TRAIT`（3）条评论证据。不够 → 降到\"假设特征\"段\n- 每条特征**必须**能引出具体评论 + 出处（哪篇 prediction 的复盘段）+ 条数\n- 发现\"反画像\"信号（你以为的受众 vs 实际评论的人不一样）→ 写\"反画像\"段\n\n### Phase 3: persona × rubric 交叉检验\n\n读 `rubric_notes.md` 当前 rubric + 校准历史。检查：\n\n- persona 说\"受众爱 X 类主题\" → rubric 校准池里 X 类主题真的 over-perform 吗？\n- 不一致 → 在 audience.md 的\"persona × rubric 交叉检验\"段 **flag 出来**\n\n诚实要求：两个派生物矛盾时**不要**强行调和。明确写\"persona 说 A，rubric 校准说 B，待下次复盘澄清\"——矛盾本身是信号。\n\n### Phase 4: 写 audience.md\n\n**覆盖式重建**（不是 append）——persona 是活文档，每次 rebuild 重写全文。但：\n\n- header 的 `Persona 版本` +1（v0 → v1 → v2）\n- header 记 `Last rebuilt` 日期 + `数据基础`（N 篇复盘 / M 条评论）+ `Confidence`\n- 文件底部\"版本历史\"段 **append 一行**：`vN — 基于 M 篇复盘 / K 条评论，主要变化：...`（这是唯一保留的历史；不搞 memo 累积——persona 是活文档不是公式）\n\n用 [templates/audience.template.md](../../templates/audience.template.md) 的结构。\n\n⚠️ **不走版本 memo / 不调跨模型审**——persona 不是高风险不可逆动作（写错了重跑一次就好），过度工程没必要。\n\n### Phase 5: 报告\n\n```\n✅ 受众画像已更新：audience.md（v2，🟢 数据扎实）\n\n数据基础：4 篇复盘 / 87 条评论\n核心画像：25-35 岁职场人，来找情绪共鸣不来找信息……\n\n跟 v1 的主要变化：\n- 新验证：\"深夜刷手机\" 场景共鸣强（v1 是假设，本次 17 条评论验证）\n- 新反画像：原以为\"学生党\"是受众，但评论里学生占比 <5% → 移到反画像\n- ⚠️ 交叉检验 flag：persona 说受众爱\"职场吐槽\"，但 rubric 校准显示职场类 composite 偏低——下次复盘留意\n\n下一步：\n- cheat-seed 选题 / 写稿时会自动参考这份画像\n- 再跑 3 篇复盘后建议再 /cheat-persona 刷新\n```\n\n## Key Rules\n\n1. **数据派生，不手写**——persona 必须来自评论聚类。用户想手动加特征 → 允许，但标 `user-asserted`（未经数据验证）\n2. **证据强制**——验证特征必须带评论条数 + 出处。无证据的进\"假设\"段\n3. **覆盖式重建**——每次 rebuild 重写 audience.md 全文，只在版本历史段 append 一行\n4. **不进打分**——persona 永远不喂 cheat-predict / cheat-score-blind。它是 cheat-seed 的 creative lens\n5. **矛盾不调和**——persona × rubric 冲突时如实 flag，不强行编一个故事\n6. **冷启动诚实**——没数据就说没数据，benchmark seed 全程标\"未验证\"\n\n## Refusals\n\n- 「我觉得我的受众就是 X，你直接写进 audience.md」 → 可以写，但标 `user-asserted` 放\"假设特征\"段，不放\"验证特征\"。persona 的价值在于数据 vs 你的幻想之间的 gap\n- 「persona 也给 cheat-predict 用，让打分更准」 → 拒绝。persona 是实绩派生物，进打分 = 把 channel B 的隔离打穿。persona 只服务 cheat-seed\n- 「跳过评论聚类，你凭感觉给我画一个」 → 拒绝。凭感觉画的是营销话术不是 persona。没评论数据就老实说\"先去复盘\"\n- 「把 persona 写进 rubric_notes.md，省一个文件」 → 拒绝。rubric_notes.md 是 blind 白名单，写 persona（实绩派生）进去 = 实绩泄漏漏洞重演（见 observation-lifecycle.md 的 leak guard）\n\n## Integration\n\n- 上游：`cheat-retro` 每完成一篇复盘 → flag \"已累计 N 篇复盘，可跑 /cheat-persona 刷新画像\"\n- 上游：`cheat-init` 创建空 audience.md 骨架；如导了 benchmark → 提示可 `/cheat-persona — seed-from-benchmark`\n- 下游：`cheat-seed` Mode A/B/C 读 audience.md 作为\"这个 persona 会在乎吗\"的镜子\n- 下游（phase 2 路线）：`cheat-recommend` persona-fit 排序；`cheat-status` persona 新鲜度 nag\n- **隔离**：`cheat-score-blind` 硬禁读 audience.md（refusal_code `blocked_audience`）\n\n## Known limitations\n\n1. **评论 ≠ 全部受众**——留评论的是受众里最活跃的一小撮（沉默大多数不在数据里）。persona 偏向\"会评论的人\"，不是\"所有看的人\"\n2. **平台评论可被污染**——水军 / 引战 / 跑题评论会进数据。cheat-persona 聚类时对明显异常值降权，但不能完全过滤\n3. **persona 滞后于真实受众变化**——画像基于过去 N 篇的评论。受众结构变了，要等新复盘累积才反映\n4. **不解决\"我想要的受众 ≠ 我实际的受众\"**——persona 只如实报告\"现在谁在看\"。想转向另一种受众是选题战略问题，cheat-persona 只提供\"现状 vs 目标\"的 gap，不替你做战略\n","frontmatter":{"name":"cheat-persona","description":"从复盘评论数据派生 / 刷新账号的受众画像，写入 audience.md。这是和 rubric 平行的第二个派生物——rubric 答\"怎么打分\"，persona 答\"谁在看\"。cheat-seed 选题 / 写稿时读它。**audience.md 含实绩信号，cheat-score-blind 硬禁读**。触发词：\"构造受众画像\"/\"更新 persona\"/\"我的观众是谁\"/\"build persona\"/\"刷新受众画像\"/\"看看我的受众画像\"。","argument-hint":"[— seed-from-benchmark] [— rebuild]","allowed-tools":"Bash(*), Read, Write, Edit, Glob, Grep","allowedTools":["Bash(*),","Read,","Write,","Edit,","Glob,","Grep"]},"allowedTools":["Bash(*),","Read,","Write,","Edit,","Glob,","Grep"],"isInternal":false,"tokens":3811,"sizeBytes":10275},{"name":"SKILL.md","path":"skills/cheat-predict/SKILL.md","rawUrl":"https://raw.githubusercontent.com/XBuilderLAB/cheat-on-content/HEAD/skills/cheat-predict/SKILL.md","title":"cheat-predict","category":"anthropic-skill","format":"markdown","content":"---\nname: cheat-predict\ndescription: 给最终稿写一份 immutable 盲预测日志。这是 cheat-on-content 整个校准循环的核心动作——预测段一旦写完不可改，由 hook 强制。**自动检测**：如目标文件已有 `## 预测` / `## 预测 v1` 段（被 cheat-shoot 调用走 v2 模式），改成 append `## 预测 v2` 而非覆盖。**打分通过 Task tool 委派给 `cheat-score-blind` sub-agent**（context-isolated channel B），主 Claude review 后落盘。触发词：\"启动预测\"/\"start prediction\"/\"给这稿子打分并预测\"/\"写预测日志\"。\nargument-hint: <script-path> [— mode: v1|v2] [— prediction-file: <path>] [— skip-blind]\nallowed-tools: Bash(*), Read, Write, Edit, Glob, Task\n---\n\n# /cheat-predict — AI 主导的盲预测 + 用户 review\n\n**这个工具是\"作弊器\"——AI 帮你做判断**。所以 cheat-predict 的核心是：\n- **Claude 自己**读稿子 + 打 7 维分 + 给 bucket + 概率分布 + 反事实场景\n- 用户 **review** 后回 \"ok\" 接受，或指出哪个维度 / 哪个判断不对\n- 默认走快路径：用户直接 ok → 落盘\n- 慢路径：用户挑刺某个维度 → Claude 改 → 再 review → 直至确认\n\n不是用户从 7 维分到概率分布全部自己写，那 Claude 只剩\"格式化器\"——失去工具的核心价值。\n\n**严格遵守 [shared-references/blind-prediction-protocol.md](../../shared-references/blind-prediction-protocol.md)**——见过任何后续数据就不能写预测，只能记 reconstructed。\n完整组件清单见 [shared-references/prediction-anatomy.md](../../shared-references/prediction-anatomy.md)。\nConfidence 派生表见 [shared-references/state-management.md](../../shared-references/state-management.md)。\n\n## Overview\n\n```\n[用户：启动预测 scripts/<id>.md]\n  ↓\n[Phase 0: blind check 自检]                    ← 触犯就拒绝\n  ↓\n[Phase 0.7: 模式判定 — v1 (新建) 还是 v2 (append)]\n  ↓\n[Phase 1: 读 script + rubric + state + 派生 confidence]\n  ↓\n[Phase 2: **委派 cheat-score-blind sub-agent**（Task tool）拿 9 维盲打 + per-dim confidence]\n  ↓\n[Phase 2.5: 主 Claude 对 blind 输出做 review — 若任意维度 |delta| ≥ 2 vs 主估，弹给用户裁定]\n  ↓\n[Phase 3: **Claude 自己**找锚点对比]\n  ↓\n[Phase 4: **Claude 自己**给 bucket + 概率分布 + 中枢]   ← confidence 低时分布更平\n  ↓\n[Phase 5: **Claude 自己**写反事实场景 + 关键校准假设]\n  ↓\n[Phase 5.5: **用户 review**——展示完整草拟版，等用户 \"ok\" 或挑刺]\n  ↓\n   ├─ \"ok\" → Phase 6 落盘\n   └─ \"X 维度应该 Y 不是 Z\" → Claude 改 → 再 review → 循环\n  ↓\n[Phase 6: 落盘 — v1 写新文件 / v2 append 到现有文件 ## 复盘 之前]\n  ↓\n[Phase 7: 更新 state.in_progress_session]\n```\n\n## Constants\n\n- **SCRIPTS_DIR = scripts/** — 草稿源目录\n- **PREDICTION_DIR = predictions/** — 落盘目录\n- **BLIND_CHECK = strict** — strict（默认）/ lenient（仅警告，不推荐）—— 跟 [blind-prediction-protocol.md](../../shared-references/blind-prediction-protocol.md) \"见过数据\"边界相关\n- **BLIND_SCORING = on**（默认）/ off —— 是否走 [cheat-score-blind](../cheat-score-blind/SKILL.md) sub-agent。off 等价于 `--skip-blind` flag，标 `last_prediction_self_scored: true` 给 cheat-status 警告\n- **DISAGREEMENT_THRESHOLD = 2** —— blind 与主 Claude 自评的单维度差异 |Δ| ≥ 此值 → Phase 2.5 弹用户裁定\n- **BUCKET_PRESET = auto** — 自动派生：有 baseline_plays → 按 baseline × {0.3 / 1 / 3 / 10 / 30}；无 baseline → 平台通用默认\n- **MIN_ANCHORS = 2** — 锚点对比期望 2 个；不够时显式标\"锚点 N/A\"段（不删段，不省略）\n\n> 💡 调用时覆盖：`/cheat-predict scripts/<id>.md — BLIND_CHECK: lenient` / `--skip-blind`（都不推荐）\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<video-folder-path>` 或 `<script-path>` | 用户参数；缺失则询问 |\n| `rubric_notes.md` | 用户项目根 |\n| `.cheat-state.json` | 状态文件 |\n| `predictions/*.md`（可选） | 历史预测，作为锚点 |\n\n### 入参解析（Phase 0.5，在 blind check 之前）\n\n用户给的路径**应该是** `scripts/<date>_<id>_<short>.md`。如不在 scripts/ 下：\n\n| 形态 | 处理 |\n|---|---|\n| `scripts/<date>_<id>_<short>.md` | 标准路径，直接用 |\n| `<id>` 或 `<short>` 简写 | glob `scripts/*_<id>_*.md` 或 `scripts/*<short>*.md` 找匹配 |\n| 任意外部 .md 文件（如 `~/Desktop/my-draft.md`） | **警告 + 询问**：\"建议把稿子放到 scripts/<date>_<id>_<short>.md 让 cheat-on-content 管理。要我帮你 cp 过去并算 id 吗？\"用户同意 → 建标准路径再继续 |\n| `videos/<id>/` 路径（user 误以为视频文件夹存稿子）| 提示\"video folder 是拍后才建的——pre-shoot 草稿在 scripts/。你要 predict 哪份稿子？\" |\n\n如 scripts/<id>.md 不存在 → 报错并询问\"你想 predict 的稿子在哪？\"\n\n## Workflow\n\n### Phase 0: Blind check 自检（**最关键**，触犯立即终止）\n\n按 [blind-prediction-protocol.md](../../shared-references/blind-prediction-protocol.md) 的\"子 skill 必须做的检查清单\"执行：\n\n1. 询问用户该作品当前发布状态：\n   - 未发 → 通过\n   - 已发 < `RETRO_WINDOW_DAYS` 天 → 询问\"你看过任何后续数据吗（播放/点赞/评论）？\"\n     - 用户回答\"没看过\" → 通过，标记 `published_before_prediction: true` + `blind_status: confirmed_no_data_seen`\n     - 用户回答含糊 → 视为\"已看\"，按下一项处理\n   - 已发 ≥ `RETRO_WINDOW_DAYS` 天 → **立即拒绝写\"预测\"**，建议改用 `_redo.md` 路径记 reconstructed retrospective\n\n2. 自检对话历史里是否含播放/阅读/点赞/评论/转发等字眼的实际数字 → 命中则视为已见数据，按上面 strict 模式处理\n\n3. `BLIND_CHECK=lenient` 模式：仅警告 + 强制在文件头标注 `**Reconstructed retrospective — NOT a blind prediction**`，但仍允许继续\n\n通过 → 进入 Phase 0.7。\n\n### Phase 0.7: 模式判定（v1 vs v2）\n\n判定本次是新建预测（v1）还是对既有预测的 v2 追加（拍后改稿场景）。\n\n**显式参数优先**：用户/调用方传 `— mode: v2` + `— prediction-file: <path>` → 直接 v2 模式。\n\n**自动检测**（无显式参数）：\n1. 推断目标 prediction 路径：`predictions/<同 scripts/<id> 命名>.md`\n2. 读该路径：\n   - 不存在 → **v1 模式**，进入 Phase 1\n   - 存在但只有空 `## 复盘`（无任何 `## 预测...` 段）→ **v1 模式**（异常状态，覆盖警告 + 进 Phase 1）\n   - 存在且含 `## 预测` 或 `## 预测 v1` 段 → **v2 模式**\n\n**v2 模式额外动作**：\n- 比较输入 script（最终拍摄稿）与 `## 预测` 段引用的原 `Script Hash`\n- 如完全一致（hash 同）→ 警告\"稿子没改，是否真要写 v2？\"——用户确认才继续；不确认则退出\n- 如不同 → 算 diff 概要（行数 / 字数 / 结构变化）→ Phase 5.5 review 时展示给用户\n- 标记 `prediction_basis = \"post_shoot_pre_publish\"`（v1 默认 `pre_shoot`）\n\n### Phase 1: 读最终稿 + rubric + state + 派生 confidence\n\n1. 按 Phase 0.5 解析后的路径，读 `scripts/<id>.md` 全文\n2. 计算 `script_hash` = sha256(script 内容)[:12] → header 用\n3. 读 `rubric_notes.md`，识别当前公式 + 维度（同 cheat-score Phase 2）\n4. 读 `.cheat-state.json` 拿 `rubric_version`、`content_form`、`calibration_samples`、`typical_duration_seconds`、`baseline_plays`\n5. **从 `calibration_samples` 派生 confidence 等级**（按 [state-management.md confidence 表](../../shared-references/state-management.md)）→ 后续写入 prediction header\n6. 询问用户：\"这是你打算实际拍摄发布的最终稿吗？还是会再改？\"——必须是最终稿\n7. 如果稿子字数与 `typical_duration_seconds` 派生范围严重不符（差 >50%）→ 提示用户：\"这条稿子 N 字，按你设的典型时长（X 分钟）应该是 M-K 字。是临时改了时长，还是稿子需要砍/补？\"\n\n### Phase 2: 委派 cheat-score-blind sub-agent 拿盲打分\n\n**BLIND_SCORING=on**（默认）—— 主 Claude 不再 inline 打分。通过 Task tool spawn `cheat-score-blind`，让一个 context-isolated 的 sub-agent 只看 script + rubric_notes.md 给出 N 维分。\n\n详见 [cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md) 的\"主 Claude 调用契约\"段。**Task prompt 必须精简**：\n\n```\nSpawn cheat-score-blind sub-agent.\n\nInput:\n  script_path: <Phase 0.5 解析出的 scripts/<id>.md>\n  rubric_notes_path: rubric_notes.md\n\nTask: 按 rubric_notes 当前公式给上面 script 打分。返回严格 JSON（见 cheat-score-blind SKILL.md Phase 2 schema）。\n不要读 state file / predictions/ / videos/ 任何其他文件。\n不要询问用户 —— 你没有用户。\n```\n\n**调用前自检**：把 Task prompt 串过 `grep -Ei '播放|阅读|点赞|评论数|实际|retro|复盘|实绩|w$|万$'`——命中 → 改 prompt 重发。\n\n**主 Claude 自己也内心估一份**（不发 sub-agent）——纯为 Phase 2.5 disagreement 检测，**不落盘**、**不替代 sub-agent 输出**。这个估值代表\"如果我没用 sub-agent，我会打多少\"，是 contamination 的客观指标。\n\n**沙盒 escape**：`BLIND_SCORING=off` 或 `--skip-blind` —— 主 Claude 自己打 7 维。state 立刻标 `last_prediction_self_scored: true` + `last_self_scored_at: <ISO>`，cheat-status 持续提示警告。仅用于：\n- Task tool 不可用（开发环境 / 离线）\n- 用户主动 audit 主 Claude 的 inline 判分能力（极少数）\n\n按当前公式算 composite——**用 sub-agent 回传的 dim 分**，不用主 Claude 自估。\n\n### Phase 2.5: Blind 输出 review + disagreement detection\n\n拿到 sub-agent JSON 后，主 Claude 必做的事：\n\n1. **JSON validity check**：`python3 -c \"import json; json.loads(...)\"` 应能解析；不能解析 → 主 Claude 重发 Task（最多 3 次重试），仍败 → abort，向用户报告\n2. **Contamination check**：`self_check.any_contamination_signal == true` → 警告用户\"sub-agent 自报疑似 contamination\"，但仍接受打分（confidence 降一档）\n3. **Refusal check**：`refusal != null` → 按 [cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md) Phase 2 的处理表对应路径\n4. **Disagreement detection**（核心）：\n   - 主 Claude 内心估一份 N 维分（Phase 2 末尾的\"自估\"）\n   - 对每个维度算 `delta = |主估 - blind|`\n   - 任何维度 `delta >= DISAGREEMENT_THRESHOLD`（默认 2） → **弹给用户裁定**\n\n弹裁定 UX：\n\n```\n⚠️  blind sub-agent 跟主 Claude 在某些维度差异较大：\n\n| 维度 | blind (sub) | 主 Claude 自估 | delta | sub-agent 理由 |\n|---|---|---|---|---|\n| ER | 5 | 3 | 2 | \"PPT加油猫猫开头—具象画面强\" |\n| AB | 2 | 4 | 2 | \"一人公司视角，受众窄\" |\n\n谁更准？\n  a) 信 sub-agent（隔离打分，但同 Claude 模型）\n  b) 信主 Claude 自估（有更多对话上下文，可能是 contamination）\n  c) 我自己定（你直接给分）\n\n回 a / b / c <你的分数>\n```\n\n用户选：\n- a → 用 sub-agent 全套分进 Phase 3\n- b → 用主 Claude 自估全套分（视为有意接受 contamination）→ 强制标 `last_prediction_self_scored: true`\n- c → 用户给的分覆盖该维度，其他维度仍走 sub-agent → 记到 `User Override`\n\n**所有 delta** —— 即使全 < THRESHOLD —— 都记录到 prediction header 的 `BlindScore Disagreement` 字段（详见 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 组件 1）。delta=0 也要记录。\n\n### Phase 3: 锚点对比\n\n**所有阶段都跑此 phase**——锚点不够时显式标 N/A，不删段。\n\n1. Glob `predictions/*.md`，读每个文件 header（提取 composite、实绩 bucket、duration_seconds）。**注意排除 reconstructed predictions**（标记 \"Reconstructed\" 的不算锚点）\n2. **优先**找同时长样本（`Target Duration (s)` 与本次差 ±20% 内）\n3. 在同时长（或全部）池里，找 2-4 个 composite 与本次预测 ±0.5 范围内的样本\n4. **如果池子太小**（同时长 < 2 个 + 全部 < 2 个）→ 输出\"锚点对比 N/A 段\"（参考 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 组件 5）—— 仍写这段，告诉读者锚点为何缺\n5. 列对照表；如跨时长，每行额外列\"时长 vs 本次\"列\n6. **关键诊断**：如果某个锚点的 composite 几乎相同但实绩差异 ≥3x → 说明 rubric 没捕获关键维度。**在文件里明确标注**作为新观察的种子\n\n> 为什么按时长筛锚点：4 分钟视频 5w 播放 vs 1 分钟视频 5w 播放完全不是一回事——长视频每秒扛了更多注意力损失。跨时长锚点容易得出虚假结论。\n\n### Phase 4: Bucket + 概率分布 + 中枢\n\n**所有阶段都写**——confidence 低时分布**更平**，不是省略。\n\n1. 从 `starter-rubrics/<content_form>.md` 读默认 bucket 边界（除非用户在 rubric_notes.md 自定义了）\n2. 选择最可能的 bucket（headline call）\n3. **必须**给出所有 bucket 的概率分布——加起来 100%\n4. **必须**给出该 bucket 内的\"中枢\"点估计\n\n**反诚实陷阱**：如果你给一个 bucket 95% 概率，下次预测错了你没法说\"我其实不太确定\"。**真实的概率分布通常在 headline bucket 是 40-65%**，剩下 ≥35% 散布在邻近 buckets。\n\n### Phase 5: 反事实场景 + 关键校准假设\n\n**所有阶段都写**——校准池小时关键校准假设可能没有合适对照样本，那就写\"无可对照样本——仍写下我对这次的核心赌注\"+ 1-2 条这次想测的事。\n\n**反事实场景**（4 段，每段对应一个可能的 bucket，写\"如果落在这里，意味着什么 rubric 假设被验证 / 推翻\"）：参考 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 组件 6。\n\n**关键校准假设**（强烈推荐）：\n- 找一个对照样本（最好是上一篇预测）\n- 明确写\"我押本篇 vs 对照 = X 倍\"\n- 写\"如果反过来 / 差距 < N → 哪个 rubric 假设被推翻\"\n\n如 `REQUIRE_HYPOTHESIS=required` → 缺失则不允许落盘。\n\n### Phase 5.5: 用户 review（**核心 — 决定写什么进文件**）\n\nPhase 2-5 全部在内存里做完后，**一次性展示完整草拟版**给用户：\n\n```\n我的预测草稿（写文件前 review）：\n\n📊 7 维分（v0 / v2 / 等当前 rubric）：\n| 维度 | 分 | 理由 |\n|---|---|---|\n| ER | 5 | \"PPT 加油猫猫 + 老板看到 + 大脑空白\"——情感重 |\n| HP | 5 | 开头\"PPT 第七页大屏中央 一只加油猫猫\"具体反差强 |\n| QL | 5 | \"加油猫猫救了我一命\"双金句 |\n| NA | 4 | 单一时间线 + 反思，清晰但不复杂 |\n| AB | 4 | 一人公司题但 AI 焦虑普适 |\n| SR | 3 | AI 焦虑是议题但不是热点对峙 |\n| SAT | 2 | 共情调性，几乎无讽刺 |\n→ composite ≈ 8.00\n\n🎯 押 bucket：30-100w，中枢 ~60w\n   概率分布: <5w 5% / 5-30w 22% / **30-100w 50%** / 100-150w 18% / >150w 5%\n   confidence: 🟢 中（基于 8 个校准样本，中枢 ±25%）\n\n🔍 锚点对比：\n| 对照 | composite | 实绩 | 异同 |\n|---|---|---|---|\n| ... | ... | ... | ... |\n\n🤔 反事实：\n   如果 >100w → 验证 ER 主导假设强化\n   如果 30-100w → 基准线 ok\n   如果 <30w → 推翻 \"AI 焦虑普适\"，AB 偏乐观\n\n🎲 关键校准假设：本篇 vs [对照] 押 1.5x\n\n——————————————————————————————\n\n回 \"ok\" 我直接落盘，\n或指出哪些维度 / 判断不对（如 \"AB 给 3，太乐观\" / \"中枢应该 30w 不是 60w\"）。\n```\n\n用户三种回应：\n\n1. **\"ok\"** / \"可以\" / \"继续\" → 直接 Phase 6 落盘，header 标 `Scored By: claude`\n2. **\"X 不对，应该 Y\"** → Claude 改对应字段（不光改值，要更新 composite + 概率分布等连锁影响），重新展示 → 循环回 Phase 5.5\n3. **\"全部重做\"** → Phase 2-5 重跑（罕见，通常是 Claude 严重误判稿子调性）\n\n**用户挑刺的字段**记录到 prediction header 的 `User Override` 段（Phase 6 写入）：\n- 哪个维度 / 哪个数字被覆盖\n- AI 原值 vs 用户改后的值\n\n复盘时这个字段帮诊断：\n- 用户每次都 ok（claude 一致）→ 没有用户偏见污染\n- 用户经常覆盖某维度 → Claude 在该维度系统性偏离用户实际感觉\n- 覆盖维度被实绩验证 → 用户直觉准 → rubric 可能漏了什么\n\n**用户挑刺的纪律**：\n- 用户**只能改字段值**，不能在 review 阶段塞新理由让 Claude 重写整段——那是把 Claude 当代笔\n- 改完 composite / 概率分布 / 锚点不一致 → Claude 自动连锁更新（不是用户算）\n\n### Phase 6: 落盘\n\n#### Phase 6a: v1 模式（新建预测文件）\n\n文件名约定（[blind-prediction-protocol.md](../../shared-references/blind-prediction-protocol.md) 的\"文件名约定\"段）：\n```\npredictions/YYYY-MM-DD_<id>_<short-title>.md\n```\n- `YYYY-MM-DD`：今天日期（预测写下的日期）\n- `<id>`：12 位 hash，对稿子全文做 sha256 取前 12 位（稳定 ID，重写不变）\n- `<short-title>`：3-8 字，去标点\n\n**第一段标题写 `## 预测 v1`**（不再写裸 `## 预测`——为将来可能的 v2 留 schema 一致性。老用户的 legacy `## 预测` 文件不动，hook 都识别）。\n\n**header 必填字段**：\n- `Article ID`（与 scripts/<id>.md 同 id）\n- `Script Path`（指向 scripts/<id>.md）\n- `Script Hash`（Phase 1 算出的）\n- `Calibration Samples` + `Confidence`（从 state 派生）\n- `Prediction Basis`：`pre_shoot`（v1 默认）\n- `Scored By`：`claude` / `claude+user_override`\n- **`BlindScored By`**：`subagent-v1`（Phase 2 默认）/ `main-claude-self`（`--skip-blind` 时） / `mixed`（Phase 2.5 用户裁定 b/c）\n- **`BlindScore Disagreement`**：JSON 字段列表，每维度 `{dim, blind, self, delta, decided_as}`，**所有维度必记**（即使 delta=0）\n- `User Override`（如有覆盖）：列出哪些字段被用户改了\n- 其他见 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 组件 1\n\n留一个空的 `## 复盘` 段：\n```markdown\n## 复盘\n\n（待填——T+RETRO_WINDOW_DAYS 天后跑 /cheat-retro <对应 video folder>）\n```\n\n#### Phase 6b: v2 模式（append 到既有文件）\n\n**绝不**用 Write 覆盖文件——会被 immutability hook 拦。用 **Edit** 在 `## 复盘` 之前插入 `## 预测 v2` 段：\n\n```python\n# 伪代码\nedit_old = \"## 复盘\\n\"   # 单独一行，确保 hook awk 识别为 v1 段的边界\nedit_new = \"\"\"## 预测 v2 (replaces v1; basis=post_shoot_pre_publish)\n\n**Diff vs v1**: 改了 N 行（X→Y%），主要变化：[摘要]\n**重判触发**: cheat-shoot 检测稿子改动 ≥30%\n**Script Hash (v2)**: <新稿子 hash>\n\n[7 组件 — 与 v1 同 anatomy]\n\n---\n\n## 复盘\n\"\"\"\n```\n\nv1 段**不动**。v2 段头部明确写\"replaces v1\"——读者一眼知道哪段是有效预测。\n\ncheat-retro 复盘时按\"读最后一个 `## 预测 vN`\"逻辑，自然取到 v2 算偏差。\n\n#### 共用规则\n\n**所有阶段都用统一完整版格式**（参考 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) \"完整结构总览\"）。confidence 低不缩格式，只让 header 标 confidence 等级 + 锚点对比段写\"N/A 解释\" + 概率分布更平。\n\n写文件**前**自检 7 个组件齐全（缺锚点 / 关键校准假设 → 写\"N/A 解释段\"，不删段）。\n\n### Phase 7: 更新 state file\n\n更新 `.cheat-state.json`：\n```json\n{\n  \"in_progress_session\": {\n    \"type\": \"prediction\",\n    \"file\": \"predictions/YYYY-MM-DD_<id>_<short>.md\",\n    \"video_folder\": \"videos/YYYY-MM-DD_<id>_<short>/\",\n    \"started_at\": \"<ISO timestamp>\",\n    \"rubric_version\": \"<v0/v2/...>\"\n  },\n  \"last_prediction_self_scored\": <true 仅当 --skip-blind / Phase 2.5 选 b>,\n  \"last_self_scored_at\": <ISO 当 last_prediction_self_scored=true 时>\n}\n```\n\n`video_folder` 为 null 表示用户跑的是裸 .md 文件，没建 video folder。\n\n`in_progress_session` 在 `cheat-publish` 触发时清除。如果用户预测后从未 publish（弃稿），下次 `/cheat-init` 或 `/cheat-status` 检测到陈旧 in_progress 会询问是否清理。\n\n`last_prediction_self_scored`：\n- `true` 仅当本次预测走了 `--skip-blind` 或 Phase 2.5 用户选了 b（信主 Claude 自估）\n- 一旦 `true` → cheat-status 持续 nag：\"上次预测没走 blind sub-agent，已 N 天\"——直到下次 normal `cheat-predict`（走 sub-agent）触发后才清回 `false`\n- `last_self_scored_at` 跟随更新；下次 `cheat-predict` 走 sub-agent → 这两个字段一起被重置\n\n### Phase 8: 控制台总结\n\n**Cold-start-simple 模式**：\n\n```\n✅ 预测落盘（cold-start 简化版）：predictions/2026-05-04_a3f2c1d4e5b6_停止期待.md\n\n7 维打分：ER5 / HP5 / QL4 / NA3 / AB5 / SR2 / SAT4\n方向押注：比上一篇明显好（ER+HP 双 5）\n对比对象：N/A（这是第 1 篇）\n\n⚠️  ## 预测段已 immutable（hook 锁定）。\n⚠️  这是 cold-start 期简化版——没有 bucket 数字。前 5 篇都这样。\n   第 5 篇复盘后会自动解锁完整预测（bucket / 概率 / 锚点 / 反事实）。\n\n进度：第 N 篇 / 共 5 篇 cold-start 期\n\n下一步：\n- 发布后 → \"已发布 https://...\"\n- T+3 天 → \"复盘 predictions/2026-05-04_...\"\n```\n\n**Complete 模式**：\n\n```\n✅ 预测落盘：predictions/2026-05-04_a3f2c1d4e5b6_停止期待.md\n\nbucket 押注：30-100w（中枢 50w）\n关键校准假设：本篇 vs 谁问你了 = 1.5-2x\n\n⚠️  ## 预测 段已 immutable（hook 锁定）。\n⚠️  你不能再向我\"透露\"这条作品的播放数据，否则下次复盘的盲度声明失效。\n   如果你不小心看到了，告诉我——我会在文件里补一个 integrity warning。\n\n下一步：\n- 发布后 → \"已发布 https://...\"\n- T+3 天 → \"复盘 predictions/2026-05-04_...\"\n```\n\n## Key Rules\n\n1. **blind check 是硬门槛**。BLIND_CHECK=strict 模式下，触犯即终止，不允许\"软处理\"。lenient 仅用于演练\n2. **整数维度分**。同 cheat-score\n3. **概率分布 = 100%**。不允许 95% + 8%；要么诚实给 50% + 30% + 15% + 5%，要么承认不知道\n4. **必须有 `## 复盘` 占位空段**——否则 hook 不知道哪里是 immutable 边界\n5. **不允许\"先写文件再讨论分数\"**——文件落盘后预测段就锁了；讨论必须发生在 Phase 2 之后、Phase 6 之前\n6. **id 是稿子 hash，不是时间戳**——重写 _redo.md 时 id 不变，便于跨文件追溯\n\n## Refusals\n\n- 「我已经看过播放数据了，但你假装没看到给我做个预测」 → 拒绝。BLIND_CHECK=strict 直接终止\n- 「我把预测段先写一版，等数据出来再调」 → 拒绝。这是把 immutable 协议反着用\n- 「我改稿了想让你覆盖之前的预测，不要 v2 段」 → 拒绝。v1 是档案，v2 才是当前判断——append 不覆盖。即使你\"主观感觉 v1 完全错了\"，git history 里 v1 还能查，但工作目录里 v1 必须留\n- 「跳过反事实场景，太麻烦」 → 拒绝。反事实是复盘诊断的依据，缺它复盘退化为\"准 / 不准\"\n- 「可不可以只写 bucket，不写概率分布」 → 拒绝。概率分布是逼你诚实的工具\n- 「这次先用 lenient 模式，下次再 strict」 → 询问原因。如果是测试 / 演练 → 允许且文件明确标 reconstructed；如果是想偷懒 → 拒绝\n- 「sub-agent 太慢，你直接打就行」 → 用 `--skip-blind` flag 显式声明。**不接受**主 Claude 自作主张跳过 sub-agent。flag 触发 state.last_prediction_self_scored=true，cheat-status 持续提示直到下次 normal 调用清除\n- 「Phase 2.5 选 b 后我不想标 last_prediction_self_scored=true」 → 拒绝。选 b 等于\"我有意接受主 Claude 自评\"——必须留下污染追踪轨迹\n- 「我是 cold-start 但想跑完整版预测，给我 bucket 数字」 → 拒绝。前 5 篇 bucket 数字是 false precision，给反而误导。等第 5 篇复盘后 cheat-status 会主动提示解锁。如果用户确实想要数字（罕见，自我教育目的）→ 允许但在文件头醒目标 `**Numerical predictions in cold-start are NOT predictive — for self-education only**`\n\n## Integration\n\n- 前置：`/cheat-init` 必须完成 + `rubric_notes.md` 存在\n- 上游可选：`/cheat-score` 反复尝试不同稿子版本\n- 下游：`/cheat-publish`（发布登记）→ `/cheat-retro`（复盘）→ 累计 ≥ MIN_SAMPLES 后 `/cheat-bump`\n- hook 依赖：`hooks/prediction-immutability.sh` 必须已安装在用户 `.claude/settings.json`，否则 immutability 仅靠 SKILL.md 自律——`cheat-status` 会持续提示\n","isInternal":false,"tokens":9096,"sizeBytes":24919},{"name":"SKILL.md","path":"skills/cheat-publish/SKILL.md","rawUrl":"https://raw.githubusercontent.com/XBuilderLAB/cheat-on-content/HEAD/skills/cheat-publish/SKILL.md","title":"cheat-publish","category":"anthropic-skill","format":"markdown","content":"---\nname: cheat-publish\ndescription: 登记一篇内容已发布，把 URL/平台 ID/发布时间写入对应预测文件 header 和 state file。这是一个轻量动作——只更新元数据，**不动预测段任何字符**。触发词：\"已发布\"/\"I shipped\"/\"发布链接是 X\"/\"刚发完 [url]\"/\"publish registered\"。\nargument-hint: <prediction-file-or-url> [— platform: youtube|bilibili|douyin|...]\nallowed-tools: Bash(*), Read, Edit, Glob\n---\n\n# /cheat-publish — 发布登记\n\n把作品的发布元数据（URL、发布时间、平台）补到预测文件 header 与 state file。**禁止改预测段**——hook 会拦。\n\n## Overview\n\n```\n[用户：已发布 https://...]\n  ↓\n[Phase 0: 找到对应的预测文件]   ← 通过 in_progress_session 或匹配\n  ↓\n[Phase 1: 解析 URL → 平台/发布时间]\n  ↓\n[Phase 2: 更新 prediction 文件 header（仅 metadata 段）]\n  ↓\n[Phase 3: 更新 .cheat-state.json，清除 in_progress_session]\n```\n\n## Constants\n\n- **AUTO_DETECT_PLATFORM = true** — 从 URL 模式自动识别平台\n- **VERIFY_BLIND = true** — 提醒用户：从此刻起看到任何后续数据都会破坏盲度声明的诚信\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<prediction-file>` 或 URL | 用户参数；缺失则用 `.cheat-state.json` 的 `in_progress_session.file` |\n| `.cheat-state.json` | 用户项目根 |\n\n## Workflow\n\n### Phase 0: 找到对应的预测文件\n\n按优先级：\n1. 用户参数明确给了 prediction 文件路径 → 用它\n2. 用户参数只给了 URL → 读 `.cheat-state.json` 的 `in_progress_session.file`\n3. 都没有 → 列出 `predictions/*.md` 中 header 没填 `published_at` 的文件，让用户选\n\n**警告路径**：若 `in_progress_session.file` 与用户给的 URL 时间差超过 14 天 → 提示\"这个预测写于很久之前，确认是这篇？\"\n\n### Phase 1: 解析平台\n\n`AUTO_DETECT_PLATFORM=true` 时按 URL 模式：\n\n| URL 模式 | 平台 |\n|---|---|\n| `youtube.com/*` `youtu.be/*` | youtube |\n| `bilibili.com/*` `b23.tv/*` | bilibili |\n| `douyin.com/*` `iesdouyin.com/*` `v.douyin.com/*` | douyin |\n| `xiaohongshu.com/*` `xhslink.com/*` | xhs |\n| `mp.weixin.qq.com/*` | wechat |\n| `substack.com/*` `*.substack.com/*` | substack |\n| `medium.com/*` `*.medium.com/*` | medium |\n| `twitter.com/*` `x.com/*` | twitter |\n| 其他 | unknown — 询问用户 |\n\n发布时间获取：\n- 不强求自动抓——绝大多数平台需要登录态\n- 询问用户：\"发布时间是？（默认：现在）\" → 接受 ISO 8601 或自然语言（\"今天 14:30\" / \"20 分钟前\"）\n- 解析失败 → 用 now()\n\n### Phase 2: 更新 prediction 文件 header\n\n**绝不**触碰 `## 预测` 段及之后。只动文件最顶部的 metadata 块。\n\n读文件，定位到 metadata 块（在第一个 `##` 之前的所有行）。检查是否已有这些字段——有则警告\"已登记过\"并询问是否覆盖；无则追加：\n\n```markdown\n**Published at**: 2026-05-04T14:32:00+08:00\n**Platform**: douyin\n**URL**: https://v.douyin.com/abc123\n**Video Folder**: videos/2026-05-04_a3f2c1d4_停止期待/\n**Aweme ID**: 7234567890123456789  (douyin / 视频号 等需要的 platform-specific ID)\n```\n\n**关于 platform-specific ID**：\n- 抖音：从 URL 短链 resolve 后提取 `aweme_id`（v.douyin.com → 重定向后含 modal_id 或 item_id 参数）\n- B 站：BV 号\n- 小红书：note_id\n- YouTube：v= 参数后的 video_id\n\n如果用户给的是分享短链（无法立刻 resolve）→ 标 `Aweme ID: pending`，下次 `/cheat-retro` 时由 adapter 解析。\n\n**video folder 处理**：到 cheat-publish 这一步，对应的 `videos/<id>/` 目录**应该已经由 cheat-shoot 创建**（含 script.md）。\n\n- 如 video folder 不存在 → 警告\"你跳过了 cheat-shoot？建议先跑 cheat-shoot 把拍摄稿登记进 video folder 再发\"，**询问用户是否跳过登记直接发**：\n  - 是 → 自动建一个 video folder（fallback），但不询问稿子一致性，标 `ad_hoc_publish: true`\n  - 否 → 让用户先跑 cheat-shoot 再回来 publish\n\n用 Edit 工具（不是 Write 重写整个文件）。\n\n**hook 行为预期**：因为只动 metadata 段（在 `## 预测` 之前），immutability hook 应放行。如果 hook 误拦 → 报告 bug，**不要绕过 hook**。\n\n### Phase 3: 更新 state file\n\n```json\n{\n  \"in_progress_session\": null,\n  \"last_published_at\": \"<ISO timestamp>\",\n  \"last_published_file\": \"predictions/<filename>\",\n  \"last_published_video_folder\": \"videos/<...>/\",\n  \"last_published_platform_id\": \"<aweme_id 或 BV 号 等>\",\n  \"pending_retros\": [\n    \"predictions/<filename>\"\n  ],\n  \"shoots\": [\n    // 移除 video_folder 匹配本次发布的项；buffer -1\n  ]\n}\n```\n\n**`shoots` 队列处理**（buffer 跟踪关键）：\n1. 读 state.shoots[]\n2. 找 `video_folder == 本次发布的 video_folder` 的项 → 移除\n3. 如果没找到 → 警告\"buffer 队列里没有这条视频。是直接发布没经过 /cheat-shoot 吗？\"——不阻塞，但提示用户下次走 /cheat-shoot 让 buffer 跟踪准确\n\n`last_published_platform_id` 是 cheat-retro 调 adapter 时的输入——如 douyin-session 需要 aweme_id 直接抓数据。\n\n`pending_retros` 是待复盘列表——`cheat-status` 会基于这个列表 + RETRO_WINDOW_DAYS 显示\"今天该复盘哪些\"。\n\n### Phase 4: 提醒 + 下一步 + buffer 状态\n\n```\n✅ 登记完成：predictions/2026-05-04_a3f2c1d4e5b6_停止期待.md\n   - Published at: 2026-05-04 14:32\n   - Platform: douyin\n   - URL: https://v.douyin.com/abc123\n\n📦 Buffer：N 篇（颜色 + 含义）\n   按你的 cadence（X）= N×X 天 buffer\n   [如颜色变了，提示\"现在该去拍/暂停拍\"]\n\n⚠️  从此刻起，你看到任何关于这条作品的播放/点赞/评论数据\n    都会破坏盲度声明的诚信。如果不小心看到，告诉我——\n    我会在文件里追加一个 integrity warning。\n\n📅 计划复盘：T+3d，约 2026-05-07\n   到时间说：\"复盘 predictions/2026-05-04_...\"\n```\n\nBuffer 颜色由 [shared-references/cadence-protocol.md](../../shared-references/cadence-protocol.md) 派生。如本次发布让 buffer 跌入红色（断更风险）→ 高亮警告\"今天必须再拍 ≥1 条\"。\n\n## Key Rules\n\n1. **不动预测段**。即使是修复笔误，也不允许在 publish 时改预测段\n2. **不抓数据**。publish 是登记动作，不是数据回收（那是 cheat-retro 的活）\n3. **state 字段名固定**。`pending_retros` / `last_published_at` 是其他子 skill（特别是 cheat-status / cheat-retro）依赖的契约\n4. **平台未知不强报**。无法识别 → 询问用户，允许 `platform: other` 作为兜底\n5. **重复登记需明示**。已有 published_at → 询问\"覆盖？\"，绝不静默覆盖\n\n## Refusals\n\n- 「我顺手把预测段也改一下」 → 拒绝。请走 `_redo.md` 路径\n- 「URL 我等会儿补，先把发布时间记上」 → 允许：URL 字段可后续追加；published_at + platform 必填\n- 「跳过 metadata 更新，直接清 in_progress_session」 → 拒绝。元数据是复盘时的关键上下文（特别是 platform 决定数据回收用哪个 adapter）\n\n## Integration\n\n- 上游：`/cheat-predict`（写出 prediction 文件并设 in_progress_session）\n- 下游：T+RETRO_WINDOW_DAYS 后 → `/cheat-retro`\n- `cheat-status` 用 `pending_retros` 字段计算\"今天该复盘哪些\"\n- 平台字段被 `cheat-retro` 用来路由到对应的 perf-data adapter（manual-paste / youtube-data-api / 等）\n","isInternal":false,"tokens":2573,"sizeBytes":7521},{"name":"SKILL.md","path":"skills/cheat-recommend/SKILL.md","rawUrl":"https://raw.githubusercontent.com/XBuilderLAB/cheat-on-content/HEAD/skills/cheat-recommend/SKILL.md","title":"cheat-recommend","category":"anthropic-skill","format":"markdown","content":"---\nname: cheat-recommend\ndescription: 从 candidates.md 里按当前 rubric 排序推荐 top N 选题，每条带 composite + 一句 rationale + 锚点对比。**candidates 不存在时给引导而非报错**。触发词：\"推荐选题\"/\"next topic\"/\"下一篇做什么\"/\"recommend topics\"/\"挑一个选题\"。\nargument-hint: \"[— top: N] [— filter: tier1|all|safe|risky]\"\nallowed-tools: Read, Glob, Grep\n---\n\n# /cheat-recommend — 候选池排序推荐\n\n读 candidates.md → 按 composite 排序 → 输出 top N 推荐，每条带评分细节 + 锚点对比 + 推荐理由。\n\n## Overview\n\n```\n[用户：推荐选题]\n  ↓\n[Phase 0: 检查 candidates.md 存在性]   ← 不存在则引导，不报错\n  ↓\n[Phase 1: 解析 candidates 列表]\n  ↓\n[Phase 2: 过滤（tier / 安全性 / 已发过）]\n  ↓\n[Phase 3: 排序 by composite + 找锚点]\n  ↓\n[Phase 4: 输出 top N + 每条的 rationale + 锚点对比]\n```\n\n## Constants\n\n- **TOP_N = 5** — 默认推荐 top 5\n- **STRATEGY = stable+experimental** — 推 ≥2 时按 [cadence-protocol.md](../../shared-references/cadence-protocol.md) 的\"1 稳分 + 1 实验性\"策略；推 1 时只推 top 稳分\n- **POOL_PATH = candidates.md** — 候选池路径\n- **EXCLUDE_PUBLISHED = true** — 排除已发布的（与 `predictions/*.md` 去重）\n- **EXCLUDE_REJECTED = true** — 排除用户主动跳过的（`tier=skip`）\n- **REQUIRE_SCORED = true** — 只推荐已打分的——避免推没读过的素材\n- **DUPLICATE_CATEGORY_LOOKBACK** — 派生自 `state.target_publish_cadence_days`：max(3, cadence_days × 3) 天内已发同类目候选不推（避免审美疲劳）\n\n> 💡 调用时覆盖：`/cheat-recommend — top: 3 — filter: safe`\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `candidates.md` | 用户项目根 |\n| `predictions/*.md` | 用于去重 |\n| `.cheat-state.json` | 当前 rubric_version |\n\n## Workflow\n\n### Phase 0: 候选池存在性检查\n\n读 `candidates.md`：\n\n| 状态 | 处理 |\n|---|---|\n| 文件不存在 | **不报错**。输出引导：见下方\"无候选池引导\" |\n| 文件存在但空（< 1 个 entry） | 同上 |\n| 文件存在且非空 | 进入 Phase 1 |\n\n**无候选池引导**（核心：不让用户第一次遇到 cheat-recommend 时被劝退）：\n\n```\n你目前没有候选池（candidates.md 不存在或为空）。\n\n绝大部分人没有候选池——这很正常。四个建立方式，挑一个：\n\n1. 🌱 [推荐] 跑 /cheat-seed\n   一次性的种子动作：3 个问题（兴趣 / 调性 / 红线）→ 拉公开热点 + Claude brainstorm\n   → 输出 15 候选让你挑 5 → 默认顺带写 5 个 draft。5 分钟搞定。\n   \n   - 没发过历史的：纯 brainstorm（兴趣 × 热点）\n   - 发过历史的（init 时已 import）：brainstorm 会基于\"你过去做过什么\"给推荐\n   \n   说：\"找选题\" 或 \"seed\"\n\n2. 🔥 [日常补充] 用 /cheat-trends 抓 20 条带打分的候选\n   说：\"抓热点\" — 从 weibo-hot / zhihu-hot / b站热门 / HN / 你配的源各拉 N 条\n   适合已经跑过 /cheat-seed、想日常补充候选池的用户\n\n3. ✍️  手动建：把候选标题贴进 candidates.md，每行一条\n   我会自动给每条粗打分\n\n4. 📋 从 Notion / RSS 导入：跑 /cheat-init --mode add-pool 配置 adapter\n\n你也可以跳过候选池，直接给我具体稿子说\"启动预测\"。\n\n> /cheat-seed vs /cheat-trends 的区别：\n> - seed 是种子动作（含 brainstorm + 可选 draft），适合\"我从零开始没选题\"\n> - trends 是日常多 adapter 抓取（不 brainstorm 不写 draft），适合\"日常补充候选池\"\n```\n\n完成引导 → 退出，不继续后续 phase。\n\n### Phase 1: 解析 candidates\n\n按 [candidate-schema.md](../../shared-references/candidate-schema.md) 的\"Markdown 表示\"格式解析每个 H3 entry：\n\n```markdown\n### [tier1] 标题\n- **id**: a3f2c1d4e5b6\n- **composite (v2)**: 8.47 — ER=4 HP=4 QL=5 NA=3 AB=5 SR=3 SAT=3\n- **predicted bucket**: 5-30w\n...\n```\n\n提取每条的 `id` / `title` / `tier` / `composite` / `dimension_scores` / `note`。\n\n容错：`candidates.md` 格式被用户手改过 → 询问用户 schema，**不要静默忽略不识别的 entry**。\n\n### Phase 2: 过滤\n\n```\n1. EXCLUDE_PUBLISHED=true → 扫 predictions/*.md 的 header，提取所有 id；从候选池过滤掉\n2. EXCLUDE_REJECTED=true → 过滤 tier=skip\n3. REQUIRE_SCORED=true → 过滤 composite=null（未打分的不推荐）\n4. filter 参数：\n   - tier1: 只保留 tier=tier1\n   - all: 不过滤（tier1+2+3）\n   - safe: 排除 tier=risky\n   - risky: 仅显示 tier=risky（用于\"我今天就想发风险议题\"）\n```\n\n### Phase 2.5: Buffer 颜色覆盖（**最高优先级**）\n\n读 `state.shoots` + `state.target_publish_cadence_days` 算 buffer 颜色（[cadence-protocol.md](../../shared-references/cadence-protocol.md)）：\n\n| Buffer 颜色 | 推荐策略覆盖 |\n|---|---|\n| 🔴 红 | **只推 top 1 稳分**——不推实验性。回：\"buffer 已 0/1 篇，下个发布日断更风险高，今天必须拍 ≥1 条稳分。下面是 top 1 稳分（不推实验性）\" |\n| 🟠 橙 | 标准 1 稳 + 1 实验，但提示\"建议优先拍稳分\" |\n| 🟢 绿 | 标准 1+1（默认） |\n| 🔵 蓝 | **拒绝推荐**。回：\"你 buffer 已 N 条，cadence-protocol 规定积压时暂停拍摄。先发存货 + 复盘。手动覆盖请说 '我就要拍'\" |\n| 灵活模式 (`target_publish_cadence_days=null`) | 不应用 buffer 覆盖，标准策略 |\n\n### Phase 3: 排序 + 选 1 稳 + 1 实验（按 STRATEGY）\n\n#### 第 1 条（稳分）\n\n1. 按 `composite` 降序排\n2. 过滤掉 `tier=risky`（稳分要安全议题）\n3. 过滤掉 `category` 与最近 `DUPLICATE_CATEGORY_LOOKBACK` 天已发/已推过的重复（避免审美疲劳）\n4. 取 top 1\n\n#### 第 2 条（实验性）\n\n1. 在 candidates.md 中找：\n   - 维度组合与最近已发样本**差异最大**（增加校准信息量），或\n   - 含明确的 pattern/dimension hypothesis（如 \"MS=5 的 A/B 对照\"），或\n   - tier=risky 但用户主动愿意试（用 `--filter risky` 覆盖）\n2. composite 不一定 top——但有\"信息价值\"\n3. 如 candidates 池里没有合适的实验性候选 → 回：\"候选池里没有明显的实验性样本，给你 2 条稳分\"\n\n#### 剩余 (TOP_N - 2) 条\n\n按 composite 降序补满，标 \"（备选）\"。\n\n#### 锚点\n\n对每条找 1-2 个 composite 接近的**已发布**作品作为锚点（从 `predictions/*.md` 读）。优先**同时长**锚点（按 `state.typical_duration_seconds` ±20%）。\n\n### Phase 4: 输出\n\n```\n🎯 候选池推荐（rubric: v2 / buffer: 🟢 绿 / cadence: 隔日更）\n\n📌 第 1 条 — **稳分**（推荐立即拍）：\n  **[tier1] [👍 9.18] \"为你好\"高密体系**\n   - 维度：ER=5 HP=5 QL=4 NA=4 AB=5 SR=5 SAT=4\n   - 粗预测桶：30-100w（中枢 ~60w）\n   - rationale：ER+SR 双 5 顶配，\"高密度家庭议题\"普适且分享安全\n   - 锚点：仓鼠 (composite 9.41, 实绩 124w) — 同走\"理论框架+具象样本\"路线\n   - 风险：议题厚重，不适合连续 2 篇都打这种\n\n🧪 第 2 条 — **实验性**（验证特定假设）：\n  **[tier1] [👍 8.71] 哈哈长度**\n   - 维度：ER=3 HP=5 QL=5 NA=4 AB=5 SR=4 SAT=5\n   - 粗预测桶：30-100w（中枢 ~55w）\n   - **测试目标**：v2.1 候选维度 MS+TS 双 5 vs 谁问你了同 ER/HP/QL/SR 但 MS+TS 低 3\n   - 信息价值：拍这条能强证据/弱推翻 v2.1 升正\n   - 锚点：谁问你了 (composite 8.24, 实绩 11.7w)\n\n（备选 top 3）：\n  3. ……\n  4. ……\n  5. ……\n\n下一步：\n- 选稳分 + 实验性各拍 1 条 → 改写 script → \"启动预测\"\n- 只拍 1 条 → 选稳分（buffer 颜色越红，越应该选稳分）\n- 想抓更多候选 → 说\"抓热点\"\n- 都不满意 → 说\"过滤改 all\"看其他 tier 或 \"regen\"\n```\n\n如 buffer 颜色为 🔴：\n```\n🔴 buffer 警戒：你 buffer 已 0/1 篇，**下个发布日可能断更**。\n   按节奏协议，只推 top 1 稳分（不推实验性）：\n\n  **[tier1] [👍 9.18] \"为你好\"高密体系**\n   - ...（同上稳分格式）\n\n今天必须拍这条。挑 5 条候选 → \"抓热点\"。\n```\n\n如 buffer 颜色为 🔵：\n```\n🔵 buffer 积压：你 buffer 已 N 条，**暂停推荐**。\n   按节奏协议，先发存货 + 复盘。\n   - 已拍未发：N 条（最早一条 X 天前拍的）\n   - 待复盘：N 条\n   说 \"已发布 ...\" 出队，或 \"复盘\" 处理待复盘项。\n   如果你坚持要拍新的，回 \"我就要拍\"，我会推 top 1 稳分。\n```\n\n每条必有：维度评分（让用户能挑战打分）+ 锚点（让用户校准 composite 的可信度）+ rationale（让用户理解推荐逻辑）。**不允许只输出 composite 排序而无解释**——那是黑箱。\n\n## Key Rules\n\n1. **不报错，给引导**。candidates 缺失是默认状态，不是错误\n2. **不推未打分的**。REQUIRE_SCORED=true 是诚实门槛——推未读过的素材是占星\n3. **必带锚点**。composite 8.47 在不同账号意味不同，锚点把抽象数字 ground 到真实样本\n4. **必带 rationale**。一句话——为什么这条比第二条强？\n5. **去重 published**。已发过的不推（用户可显式覆盖）\n\n## Refusals\n\n- 「直接给我 composite 最高的，不用解释理由」 → 拒绝。展示评分 + 锚点是发现\"打错\"的唯一机会\n- 「把 candidates.md 里所有 entry 都重新打分一遍」 → 路由到 `/cheat-score` 单条做；批量重打分是 `/cheat-bump` 的一部分，不在 recommend 范围\n- 「按预测桶排，不要按 composite」 → 询问理由。bucket 是 composite 的离散化，按 composite 排即按 bucket 排，差异在桶内序——如果用户真想按\"押注期望值\"排，需要乘以平均播放，那是另一个独立 scoring 维度\n\n## Integration\n\n- 上游：`/cheat-trends` 把外部热点拉进 candidates.md → recommend 自动看到\n- 下游：用户挑一条后写稿 → `/cheat-predict`（candidate 的粗 composite 不进入 prediction，prediction 重新打）\n- 与 `/cheat-status` 协调：status 显示 \"candidates 池有 N 条 tier1 未发\"，recommend 提供具体推荐\n","frontmatter":{"name":"cheat-recommend","description":"从 candidates.md 里按当前 rubric 排序推荐 top N 选题，每条带 composite + 一句 rationale + 锚点对比。**candidates 不存在时给引导而非报错**。触发词：\"推荐选题\"/\"next topic\"/\"下一篇做什么\"/\"recommend topics\"/\"挑一个选题\"。","argument-hint":"[— top: N] [— filter: tier1|all|safe|risky]","allowed-tools":"Read, Glob, Grep","allowedTools":["Read,","Glob,","Grep"]},"allowedTools":["Read,","Glob,","Grep"],"isInternal":false,"tokens":3901,"sizeBytes":10179},{"name":"SKILL.md","path":"skills/cheat-retro/SKILL.md","rawUrl":"https://raw.githubusercontent.com/XBuilderLAB/cheat-on-content/HEAD/skills/cheat-retro/SKILL.md","title":"cheat-retro","category":"anthropic-skill","format":"markdown","content":"---\nname: cheat-retro\ndescription: T+N 天数据回收 + 复盘 + 把实绩观察写入 rubric-memo.md。这是校准循环的反馈环节——不复盘的预测等于占星。触发词：\"复盘 [path]\"/\"retro this\"/\"T+3d 数据来了\"/\"抓数据 [path]\"/\"把这篇复盘了\"。\nargument-hint: <prediction-file> [— window: 3|5|7] [— source: manual|adapter]\nallowed-tools: Bash(*), Read, Edit, Write, Glob, Grep, Skill\n---\n\n# /cheat-retro — 数据回收与复盘\n\n抓 T+N 天的实际表现 → 对比预测 → 提炼新观察 → 写入 rubric-memo.md。**只追加 `## 复盘` 段，绝不改预测段**。`rubric_notes.md` 是 blind 白名单，只能保存通用公式、维度定义和抽象规则，不能写入样本名、实绩、评论、链接或播放/阅读数。\n\n## Overview\n\n```\n[用户：复盘 predictions/2026-05-04_...]\n  ↓\n[Phase 0: 校验 immutability + 校验时间窗口]\n  ↓\n[Phase 1: 抓数据（manual paste 或 adapter）]\n  ↓\n[Phase 2: 写实绩段 + top 评论关键词]\n  ↓\n[Phase 3: 验证/推翻预测的各假设]\n  ↓\n[Phase 4: 提炼新观察]\n  ↓\n[Phase 5: 落盘（追加到 ## 复盘 段）]\n  ↓\n[Phase 6: 写入 rubric-memo.md 的\"观察记录\"段]\n  ↓\n[Phase 7: 检测是否触发 bump 候选 → 提示用户跑 /cheat-bump]\n```\n\n## Constants\n\n- **RETRO_WINDOW_DAYS = 3** — 默认 T+3d。短视频快平台可设 1，长文设 7\n- **DATA_SOURCE = manual** — manual: 用户粘数字；adapter: 调对应平台 adapter（需配置）\n- **AUTO_PROPOSE_BUMP = true** — Claude 判断是否系统性偏差时自动提议 /cheat-bump\n  - **默认参考**：连续 ≥3 次同向偏差（high/low）→ 提议\n  - **但 Claude 可以更早提议**：1 次极端偏差（如中枢 50w 实绩 5w 这种 ≥10x），即使没有\"连续\"也提议\n  - **也可以更晚**：3 次同向但每次偏差都很小（<25%），可能只是噪声不是系统性\n- **TOP_COMMENTS_N = 20** — 抓 / 粘 top N 高赞评论\n\n> 💡 调用时覆盖：`/cheat-retro <file> — window: 7 — source: adapter`\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<prediction-file>` 或 `<video-folder>` | 用户参数；缺失则从 `.cheat-state.json` 的 `pending_retros[0]` |\n| `rubric_notes.md` | 用户项目根（只读，用于当前规则上下文；不得写入实绩观察） |\n| `rubric-memo.md` | 用户项目根（写入复盘观察、实绩证据、样本名与评论信号） |\n| `.cheat-state.json` | 状态文件 |\n\n### 入参解析（同 cheat-predict 双形态接受）\n\n用户给的可能是：\n- **`predictions/2026-05-04_<id>_<short>.md`** → 直接用这个 prediction 文件\n- **`videos/2026-05-04_<id>_<short>/`** → 找对应的 prediction 文件（按 id 匹配）+ 把 report.md 写到该 video folder 里\n- 缺省 → 从 `pending_retros[0]` 取最早的\n\n## Workflow\n\n### Phase 0: 校验\n\n1. 读 `<prediction-file>`，确认存在\n2. **识别有效预测段**：扫所有 `## 预测...` 段（可能含 `## 预测`、`## 预测 v1`、`## 预测 v2` 等）：\n   - 取**最后一个**`## 预测 vN` 作为本次校准的依据（v2 存在则用 v2；只有 v1 则用 v1；legacy 单段 `## 预测` 直接用）\n   - state.shoots 对应项的 `v2_prediction_written` 应与\"是否存在 v2 段\"一致——不一致则警告（state 与文件脱节）\n3. **校验 immutability**：在内存 cache 住所有 `## 预测...` 段的内容（用于 Phase 5 后核对——**全部段不可改**，不只是有效段）\n4. 校验文件 header 有 `Published at` → 没登记的不能复盘，提示用户先 `/cheat-publish`\n5. 校验时间窗口：今天 - published_at >= RETRO_WINDOW_DAYS。不够 → 提示\"还差 X 天\"，询问用户是否仍坚持复盘（标 `early_retro: true`）\n6. 校验已有复盘段是否已填——已填则询问\"是补充还是修正？\"\n   - 补充 → 在已有复盘段下追加新子段，标日期\n   - 修正预测段（用户错觉）→ 拒绝\n\n### Phase 1: 抓数据\n\n按 `state.data_collection` 字段分两条路径——抓回数据后**写到 video folder 的 `report.md`**（如果 prediction 关联 video folder），同时解析摘要 inline 到 prediction 的复盘段。\n\n#### Path A：`DATA_SOURCE=manual`（候补方案）\n\n- 询问用户：\"粘贴这条作品的当前数据：播放 / 点赞 / 评论 / 转发 / 收藏（顺序无所谓，能识别就行）\"\n- 用户粘 → 解析提取数字\n- **强制要求 top 评论**：让用户从平台后台或直接打开评论区贴 TOP_COMMENTS_N 条到对话里（每条带赞数）\n  - 用户拒绝 / 给少于 5 条 → **拒绝继续**：\"评论才是真信号——'她不一样'这种模因爆发只能从评论看出。\n    没评论的复盘 = 看体温计判断病情。粘 top 20 给我。如果实在拿不到，告诉我原因（比如评论被关了），我帮你标 `comments_unavailable`，但这次复盘价值打折。\"\n- 把粘的原始数据写到 `videos/<...>/report.md`（如有 video folder）\n\n#### Path B：`DATA_SOURCE=adapter`\n\n按 prediction header 的 `Platform` 字段 + state 的 `enabled_perf_adapters` 决定调哪个：\n\n| Platform | Adapter | 调用方式 |\n|---|---|---|\n| `douyin` | `adapters/perf-data/douyin-session/` | `bash <adapters-dir>/douyin-session/run.sh <aweme_id> <video_folder>` |\n| `xhs` | `adapters/perf-data/xhs-explore/` | `bash <adapters-dir>/xhs-explore/run.sh <note_id> <video_folder>` |\n| `linkedin` | `adapters/perf-data/linkedin-session/` | `bash <adapters-dir>/linkedin-session/run.sh <activity_id> <video_folder>` |\n| `wechat_channels` | `adapters/perf-data/wechat-channels/` | `bash <adapters-dir>/wechat-channels/run.sh <post_id> <video_folder>` |\n| `youtube` | `adapters/perf-data/youtube-data-api/`（planned — batch 3, not yet available） | 调 YouTube Data API（需 API key） |\n| `bilibili` | `adapters/perf-data/bilibili-stat/` | `bash <adapters-dir>/bilibili-stat/run.sh <bvid> <video_folder>` |\n| 其他 | 无 adapter | 优雅降级到 Path A |\n\n> `<adapters-dir>` = 克隆源码处的 `cheat-on-content/adapters/perf-data/`（install.sh **不**复制 adapter 到 ~/.claude/skills，只复制 15 个 skill）。定位：`find ~ -path '*/cheat-on-content/adapters/perf-data' -type d | head -1`。\n\n**douyin-session 的特殊处理**：\n- 视频 URL（如 `https://v.douyin.com/abc123`）→ 短链解析 → 提取 aweme_id\n- 调用前确认 cookie 文件存在（adapter 会找 `.auth/`）；不存在则提示用户先跑 `python <adapter>/crawler.py login`\n- adapter 输出在 `<video_folder>/report.md`（adapter 的 renderer.py 已经按这个格式写）\n- cheat-retro 读这个 report.md 解析关键数据 → 摘要写入 prediction 的复盘段\n\n**xhs-explore 的特殊处理**：\n- 笔记 URL（`https://www.xiaohongshu.com/explore/<note_id>?xsec_token=...` 或 `https://xhslink.com/xxx`）→ 提取 note_id\n- 调用前确认 cookie 存在（adapter 找 `.auth-xhs/`）；不存在则提示先跑 `python <adapter>/crawler.py login`\n- 字段已校准（观看 `view_count` 等已写死）；万一接口改版导致某项为 0，看 report.md 末尾 galaxy 原始 JSON，把新 key 加进 `crawler.py` 的 `_normalize_note`\n- **评论可能抓不到**（xsec_token 缺失 / 评论关闭）→ report.md 标\"未抓到评论\" → 此时**降级要求用户 manual 粘 top 20 评论**（评论是真信号，不能省）\n\n**linkedin-session 的特殊处理**：\n- 帖子 URL（`https://www.linkedin.com/feed/update/urn:li:activity:<id>/`）或裸 activity_id → adapter 自动提取 activity_id\n- 调用前确认 cookie 存在（adapter 找 `.auth-linkedin/`）；不存在则提示先跑 `python <adapter>/crawler.py login`\n- **只能抓你本人发的帖子**（LinkedIn 单帖分析仅作者可见）；LinkedIn 界面 日/英 随机切换，`extract.py` 的 `POST_METRICS` 已存双语标签，万一某项为 None 看 `.cheat-cache/linkedin-session-debug/post_<id>.txt` 把新标签补进去\n- **评论只给数、不给正文**（分析页限制）→ report.md 标注 → **降级要求用户 manual 粘 top 评论**（评论是真信号，不能省）\n\n**wechat-channels 的特殊处理**：\n- 仅抓用户自己登录后可见的视频号助手后台数据；调用前确认内容项目存在 `.auth-wechat-channels/`\n- 不存在登录态时，提示先运行 `python <adapter>/review.py login` 并扫码；登录态只保存在内容项目本地\n- adapter 输出 `report.md`，但不落盘原始响应、请求体、完整后台 URL、后台截图、页面文本、评论者昵称或内部用户 ID\n- 找不到目标作品或 adapter 失败时，非 0 退出并降级 manual；不得把旧 report.md 当作本次成功\n\n**bilibili-stat 的特殊处理**：\n- 视频 URL（`https://www.bilibili.com/video/<BV号>` 或 b23.tv 短链）或直接给 BV 号 → adapter 自动提取 BV 号\n- **无需登录**：B站视频数据(view)与评论(reply)都是公开接口、免 wbi 签名，adapter 是纯 httpx，没有 `crawler.py login` 步骤、不碰 `.auth/`\n- 依赖 httpx：首次用 `pip install -r <adapter>/requirements.txt`\n- 评论按热度（sort=2）抓取；B站老接口主楼评论可能偏少，不足时降级 manual 粘\n\n**任何 adapter 失败**（cookie 过期 / 接口变化 / 网络）→ **优雅降级到 manual**，提示用户：\"adapter 调用失败，原因 [X]。改用 manual 模式——粘下面的数据\"。**不阻塞流程**。\n\n#### 共同输出\n\n不管 Path A 还是 B，最终：\n- `videos/<...>/report.md` 含完整原始数据（数字 + top 评论）\n- prediction 文件复盘段含**摘要**（关键比率 + 评论关键词聚类 + 验证/推翻判定）\n- report.md 是数据真相，prediction 复盘段是判断真相\n\n### Phase 2: 写实绩段 + top 评论分析\n\n**实绩数据格式**（参考 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 的复盘段格式）：\n\n```markdown\n### 实绩数据\n- 播放：71.1w（落在 `30-100w` 桶内偏高，相对中枢 50w **+42%**）\n- 点赞：2.4w（赞播比 3.38%）\n- 评论：899（评播比 0.126%）\n- 收藏：5251\n- 分享：1.8w（分播比 2.53%，强）\n```\n\n数据点之间的派生比率（赞播比、评播比、分播比）必须算出来——它们是单纯播放数无法暴露的信号。\n\n**top 评论关键词聚类**：\n- 把粘进来的 N 条评论分 3-5 类（高赞模因 / 概念引用 / 离题噪声 / 转发暴露暗示 / @朋友传播 等）\n- 每类列代表性评论（带赞数）\n- 报告比例（\"22% 是模因复用、35% 是概念引用、5% 是离题\"）\n\n### Phase 3: 验证/推翻\n\n对 prediction 文件里的每一项（推理因素表、关键校准假设、反事实场景），逐项判定：\n\n```markdown\n### 哪些预测被验证 ✅ / 推翻 ❌\n\n**验证 ✅**:\n- 关键校准假设完全成立：本篇 71.1w / 谁问你了 11.7w = 6.07x，远超我押的 1.5-2x\n- ER=5 主导情感传播力 → H1 强证据\n- HP=5 验证：分播比 2.53% 与\"金句被高频引用\"匹配\n\n**推翻 ❌**:\n- 中枢 50w 被超出 +42%\n- 反事实推理里\"必须搭配强社会议题才能破 30w\" 完全错误\n- SR 押注（\"H2 SR 应上调\"）反向被推翻：SR 在情感向场景几乎不贡献\n```\n\n**关键纪律**：\n- 每条验证 / 推翻必须引用具体数据（\"分播比 2.53%\"），不许写\"基本符合\"这种含糊措辞\n- 反事实的\"如果落在 X bucket 意味着什么\"——实际落在的那个 bucket 直接告诉你哪个 rubric 假设被测试了，明确写出来\n\n### Phase 4: 提炼新观察（**两类，分别写入两个文件**）\n\n#### 4a. Rubric 观察（写入 rubric-memo.md）\n\n打分维度 / 公式 / bucket 边界相关的观察：\n\n```markdown\n### 需要写进 rubric-memo.md 的新观察\n\n1. **ER 在情感向场景的真实权重应 ≥ ×2.0**：与谁问你了 6x 流量比是 v2 rubric 最强的反事实证据\n2. **议题分享冲动 (TS) 是隐藏维度**：joker / \"她不一样\" / 滤镜重构提供了安全的自嘲身份，转发不暴露处境，TS=5 的样本\n3. ……\n```\n\n每条观察必须可追溯到具体数据点（不写\"情感很重要\"——写\"ER5/SR2 vs ER3/SR4 同 composite 下流量差 6x\"）。\n\n#### 4b. 写作 Pattern 观察（写入 script_patterns.md）\n\nDiff `scripts/<id>.md`（pre-shoot 草稿，可能是 cheat-seed 写或用户写）vs `videos/<id>/script.md`（实际拍摄稿——cheat-shoot 时用户提供的版本），找出**改动且对流量有明显影响**的部分：\n\n| 用户做了什么 | 流量影响 | 是否提议追加 pattern |\n|---|---|---|\n| 砍掉某段 | 实绩 ≥ 中枢 → \"砍掉没伤流量\"——验证那段冗余 | 是，加到 script_patterns.md \"用户改稿历史观察\"表 |\n| 加了某句 / 互动钩子 | 实绩超中枢 → 可能是新 pattern | 是，候选 Pattern N，标 ≥1 样本待验证 |\n| 改了风格（如开头软化） | 高于同类样本 → 风格改动有效 | 是，候选 Pattern N |\n| 没动结构 / 改动与流量无关 | — | 不追加 |\n\n输出格式：\n\n```markdown\n### 需要写进 script_patterns.md 的新 pattern 候选\n\n1. **用户改稿模式**: 砍掉 [X 段] / 加了 [Y]\n   - 流量影响：实绩 [N] vs 中枢 [M]，[偏差 / 命中]\n   - 建议：追加到 script_patterns.md 的\"用户改稿历史观察\"表\n\n2. **新 pattern 候选 N**：[一句话描述]\n   - 单样本支持\n   - 触发条件：[何时该用]\n   - 建议：追加到 script_patterns.md 末尾的\"新发现的 Pattern\"段，标 ≥1 样本待验证\n```\n\n询问用户：\"要把这些追加到 script_patterns.md 吗？(yes / no / 选择哪几条)\"。**用户确认后才追加**——避免把单点观察直接写成正式 pattern。\n\n> **rubric 进化 ≠ 写作进化**——两者解耦：\n> - rubric_notes.md 学的是\"哪些维度真的预测流量\"\n> - script_patterns.md 学的是\"什么写法真的能起作用\"\n> 可能有交叉（如 MS 维度与\"互动钩子\" pattern），但记录在两个文件里是因为**作用域不同**——rubric 改了影响所有未来打分，pattern 改了影响所有未来 draft。\n\n如果 `videos/<id>/script.md` **缺失**（cheat-shoot 时用户标 `script_lost`） → 跳过 4b，没法 diff。\n如果 `script_consistency = \"consistent\"`（用户拍时没改稿）→ 4b 仍然有意义（diff 也许是空），但可以快速跳过细查。\n如果 `script_consistency = \"modified\"`（用户拍时改了）→ **4b 是核心**，重点学这次改动 → 流量影响。\n\n### Phase 5: 落盘到 ## 复盘 段\n\n用 Edit 工具，**仅追加**到现有 `## 复盘` 段（如有占位 `（待填）` 行先删除）：\n\n```markdown\n## 复盘\n\n**复盘时间**: 2026-05-07（发布 T+3d）\n**抓取时间**: 2026-05-07 09:30\n**数据来源**: manual paste\n\n### 实绩数据\n[Phase 2 内容]\n\n### Top 评论关键词\n[Phase 2 内容]\n\n### 哪些预测被验证 / 推翻\n[Phase 3 内容]\n\n### 需要写进 rubric-memo.md 的新观察\n[Phase 4 内容]\n```\n\n**写完后再次校验**：读取保存后的文件，对比**所有** `## 预测...` 段（v1 / v2 / legacy）的合并哈希应等于 Phase 0 cache 的合并哈希。**任一段被改 → 报错并回滚**。\n\n### Phase 6: 写入 rubric-memo.md + script_patterns.md\n\n#### 6a. rubric-memo.md（Phase 4a 的输出）\n\n按 [observation-lifecycle.md](../../shared-references/observation-lifecycle.md) 的 blind leak guard，追加到 `rubric-memo.md` 的 `## 观察记录` 段。这里可以包含真实样本名、实绩数据、评论关键词和链接；这些内容**绝不**写入 `rubric_notes.md`：\n\n```markdown\n### YYYY-MM-DD [标题简称] (id) — [一句话定性]\n- 预测：composite=X.XX，bucket=Y\n- 实绩：播放 / 点赞 / 评论 / 转发（带 T+Nd 标注）\n- Top 评论关键词：[简短摘录 + 赞数]\n- 判断：哪个维度被验证/推翻？为什么？\n- Rubric 调整：[如果有，写明 \"下次打 XX 类文章时改 YY\"]\n- 详见：[predictions/<file>.md]\n```\n\n**检测跨样本 pattern**：扫描 `rubric-memo.md` 已有\"观察记录\"，看新观察是否与某条已有观察形成 ≥2 样本支持。命中则在 `rubric-memo.md` 升级到\"重大跨样本观察\"段。只有在后续 `/cheat-bump` 落地时，才把已验证的规律抽象成通用语言写入 `rubric_notes.md`。\n\n#### 6b. script_patterns.md（Phase 4b 的输出，**用户确认后才写**）\n\n如 Phase 4b 用户回 \"yes\" 或选择性确认了某几条：\n- \"用户改稿模式\" → 追加到 script_patterns.md 的\"用户改稿历史观察\"表\n- \"新 pattern 候选 N\" → 追加到末尾\"新发现的 Pattern\"段，**显式标 ≥1 样本待验证**\n\n新 pattern 候选的格式（同 [script_patterns.template.md](../../templates/script_patterns.template.md) 的 Pattern 11/12 示例）：\n\n```markdown\n### Pattern N（来自 [视频简称]，单样本待验证）\n\n**现象**：[Phase 4b 描述]\n\n**原理**：[为什么有效——基于这一次观察的猜测]\n\n**触发条件**：[何时该用]\n\n**待验证**：需要 ≥2 样本支持才能升正式 pattern。\n```\n\n跨样本 pattern 升正式：扫描\"新发现的 Pattern\"段，看是否有 ≥2 样本支持同一现象 → 升到核心 pattern 库 + 删 \"待验证\" 标记。\n\n如用户在 Phase 4b 全否（\"no\"）→ 跳过 6b，rubric-memo.md 仍照写。\n\n### Phase 7: 检测 bump 触发\n\n读 `.cheat-state.json` 的 `consecutive_directional_errors` 字段，按本次复盘判定向更新：\n- 本次预测高估（实绩 < 中枢 -25%） → push `[\"high\"]` + 记录 deviation_magnitude（如 0.5x / 0.3x）\n- 本次预测低估（实绩 > 中枢 +25%） → push `[\"low\"]` + 记录 deviation_magnitude\n- 在 ±25% 内 → 不 push\n\n**Claude 判断是否提议 bump**（不是固定门槛）：\n\n```\n判断维度：\n1. 连续同向次数（参考默认：≥3）\n2. 单次偏差幅度（参考默认：>2x 或 <0.5x 算极端）\n3. 偏差是否能解释为单一维度漏判（如 ER 或 SR 一致偏离）\n4. 用户是否在复盘里反复提到同一现象\n\n任一足够强 → 提议 bump：\n- 3 次连续同向，每次都中等偏差 → 提议\n- 1 次极端偏差（如 ≥10x），即使没连续 → 提议（\"一次性强信号\"）\n- 2 次同向 + 评论区出现一致的反向证据 → 提议（\"评论 + 数据双信号\"）\n\n不提议的情况：\n- 3 次同向但每次都很小（<25%）→ 可能只是噪声\n- 偏差跨多个维度无清晰方向 → bump 不知道改什么\n```\n\n提议时输出：\n\n```\n🚨 检测到 [系统性偏差信号] / [极端单点偏差] 。\n\n[简短描述：连续 N 次 / 1 次极端 / 评论双信号 等]\n\n这可能是 rubric 系统性偏差的信号。建议：\n- 跑 /cheat-bump 看是否需要升级公式\n- 或先看 /cheat-status 详细分析\n\n注：本次提议是 [default-aligned: 满足 ≥3 同向] / [judgment-driven: 1 次 10x 强偏差]\n```\n\n更新 state file：\n```json\n{\n  \"calibration_samples\": <+1>,\n  \"pending_retros\": [<剔除本次>],\n  \"last_retro_at\": \"<ISO>\",\n  \"consecutive_directional_errors\": [...]\n}\n```\n\n## Key Rules\n\n1. **预测段 immutable**。Phase 0 cache + Phase 5 校验是双保险。任何 hash 不一致 → 报错回滚\n2. **数据来源必须标注**。`数据来源: manual paste` 或 `数据来源: adapter:douyin-session` 写进复盘段\n3. **观察可追溯**。每条新观察引用具体数据点\n4. **不在复盘里 bump**。Phase 7 只**提议** bump，实际升级走 `/cheat-bump`——避免一次操作做两件事\n5. **早复盘标记**。RETRO_WINDOW_DAYS 不到就复盘 → state file 记 `early_retro: true`，bump 时这种样本权重降级\n\n## Refusals\n\n- 「这条数据已经看过了，但你假装没看，按预测时的盲度做复盘」 → 复盘本来就是看完数据再做的；这个表述本身没有违规，但要确认用户没在 prediction 写之前透露过数据\n- 「把预测段的概率分布改一下，让复盘看起来更准」 → 拒绝。原则 #1\n- 「跳过观察提炼，直接结束」 → 拒绝。新观察是 rubric 进化的唯一燃料；缺它复盘退化为\"看一眼\"\n- 「直接 bump，不要单独走 /cheat-bump」 → 拒绝。bump 流程有完整的跨模型审 + cleanup pass，retro 是触发器不是执行器\n\n## Integration\n\n- 前置：`/cheat-publish` 已登记 + 时间窗口达到\n- 下游：累计 `consecutive_directional_errors` 满 3 → 触发 `/cheat-bump` 提议\n- 状态字段更新：`calibration_samples` +1（这是 cheat-status 显示进度的关键）\n- pending_retros：剔除本条\n- 与 [observation-lifecycle.md](../../shared-references/observation-lifecycle.md) 紧耦合：每次复盘是观察新增的入口\n","isInternal":false,"tokens":7617,"sizeBytes":20618},{"name":"SKILL.md","path":"skills/cheat-score-blind/SKILL.md","rawUrl":"https://raw.githubusercontent.com/XBuilderLAB/cheat-on-content/HEAD/skills/cheat-score-blind/SKILL.md","title":"cheat-score-blind","category":"anthropic-skill","format":"markdown","content":"---\nname: cheat-score-blind\ndescription: |\n  INTERNAL sub-agent for blind 7-dim rubric scoring. **NOT a user-facing skill — do NOT invoke from main conversation.** Called via Task tool by cheat-score / cheat-predict / cheat-bump to get a context-isolated score on a script. Receives ONLY script_path + rubric_notes_path; refuses any other input. Outputs strict JSON: 9 dimensions × {score 0-5, confidence enum, one-line reason}. **Hard refuses to Read** .cheat-state.json, predictions/*, retro 段, or anything that could leak post-publish data. This is channel B in the 3-channel calibration model (A=main, B=blind sub, C=cross-model).\nallowed-tools: Read, Glob, Grep\nargument-hint: <script-path> <rubric-notes-path>\n---\n\n# /cheat-score-blind — Channel B (blind scorer sub-agent)\n\n> ⚠️ **这是子 agent，不是用户 skill**。只能由 `cheat-score` / `cheat-predict` / `cheat-bump` 通过 Task tool spawn。用户直接 trigger 没有意义——主对话已经被污染，调用 blind sub-agent 在主 context 里跑不构成隔离。\n\n---\n\n## Why this exists（绝不可省的背景）\n\ncheat-on-content 的 7/9 维打分原本 inline 在主对话——但主 Claude 已经看过：\n- 用户对话历史（含偶然提到的播放数 / 评论 / 情绪）\n- 已发布作品的实绩数据\n- 历史 `predictions/*.md` 含复盘段（**严重污染**）\n- 用户的赞美 / 抱怨 / 期待\n\ninline 打分 = **被污染的\"盲\"预测**。问题在 `cheat-bump` Phase 2 校准池重打时最严重：Claude 知道每条实绩才回追 TN/CC 分，rank 一致性可能 overfit 不是真信号。\n\n**channel B 的角色**：用 Task tool 把打分动作丢进一个**全新 context**——这个 sub-agent 没看过主对话、没读过 state、没碰过 predictions/。它只看 script 全文 + rubric_notes.md，按 rubric 打分。\n\n输出回传主对话后，主 Claude 自己对比、做最终决策。隔离的是**打分这个动作的输入**，不是决策权。\n\n## 三 channel 模型\n\n| Channel | 输入 | 用途 | 风险 |\n|---|---|---|---|\n| **A** = 主对话 | 全部上下文 | 跟用户交互、写 retro、决策 | 被实绩 / 用户态度污染 |\n| **B** = blind sub-agent (this) | **只** script + rubric_notes.md | 给一份未受污染的打分作为 anchor | 仍是 Claude，RLHF prior 共享 |\n| **C** = 跨模型 audit (`mcp__llm-chat__chat` to qwen-max) | 校准池数据 + 新公式 | bump 终局 sanity check | RPM 限制、模型差异、单点 |\n\nA 决策时把 B 当对照看 disagreement，**不当真理**。C 只在 bump 终局调一次。\n\n---\n\n## Inputs（**唯一被允许的输入**）\n\n| 必填 | 来源 | 说明 |\n|---|---|---|\n| `<script-path>` | 主 Claude 通过 Task prompt 显式传入 | `scripts/<id>.md` 全文 |\n| `<rubric-notes-path>` | 同上 | 用户项目根 `rubric_notes.md` 当前 rubric 公式 + 维度定义 |\n\n**仅此两个文件可读**。其他一切**硬拒绝**——见下方 \"Hard refusals\" 段。\n\n## 禁止读取（hard list）\n\n下面这些路径 / 模式 sub-agent **绝不能 Read** —— 即使主 Claude 在 Task prompt 里手滑塞进来，也要拒绝并在 JSON 输出标对应 `refusal` 码：\n\n| 路径模式 | 为什么禁 | refusal_code |\n|---|---|---|\n| `.cheat-state.json` | 含 calibration_samples / pending_retros / last_published_at / shoots — 全是后视数据 | `blocked_contaminated_input` |\n| `predictions/*.md` | 含 `## 预测` 段 + `## 复盘` 段，复盘段就是实绩 | `blocked_contaminated_input` |\n| `videos/*/report.md` | T+3d 抓回的真实数据 | `blocked_contaminated_input` |\n| `videos/*/script.md` | 后改拍摄稿，复盘时被对照 | `blocked_contaminated_input` |\n| `STATUS.md` | cheat-status 渲染的看板，含过去数据 | `blocked_contaminated_input` |\n| `.cheat-cache/usage.jsonl` | 行为 log | `blocked_contaminated_input` |\n| **`rubric-memo.md`** | **cheat-bump 升级 Memo 累积档案——含真实视频名 + 实绩 + 派生证据。这是 channel B 的最大泄漏入口（PR #11 实测复现）** | **`blocked_rubric_memo`** |\n| **`audience.md`** | **cheat-persona 从复盘评论派生的受众画像——含评论证据 / 实绩信号。属 channel A creative 资产，进 blind 打分 = 实绩泄漏** | **`blocked_audience`** |\n| 任何含\"播放 / 阅读 / 点赞 / 评论数 / 转发 / w / 万 / k / M\"的文件 | 直接污染 | `blocked_contaminated_input` |\n\n**白名单只有两个**：\n- `scripts/<id>.md`（pre-shoot 草稿，传入参数）\n- `rubric_notes.md`（评分公式 + 维度定义，**应**只含通用语言；如发现实绩数字 → 标 `non_blind_warning` 并降 confidence）\n\n如果主 Claude Task prompt 漏传了某条路径，sub-agent 主动询问\"我只允许读 script + rubric_notes，缺哪个？\"——**绝不**自己去 Glob 探测项目结构补全。\n\n> ⚠️ **白名单兜底自检**：读完 `rubric_notes.md` 后必跑 `grep -E '\\\\d+\\\\s*[wWmMkK万]|播放|实绩|实际'`——命中 → 标 `self_check.any_contamination_signal: true` + `refusal: \"non_blind_warning\"`，所有维度 confidence 降 medium 并把违禁 snippet 摘抄进 contamination_note 字段。**仍输出 dimensions** 让主 Claude 知道发生了什么——拒绝输出比误判更糟，但要诚实标注。\n\n---\n\n## Workflow\n\n### Phase 0：边界自检\n\n1. 解析 Task prompt 拿 `<script-path>` 和 `<rubric-notes-path>`\n2. 校验路径符合白名单——不在 `scripts/` 下的 .md → 拒绝（除非主 Claude 显式说明\"这是临时草稿临时路径，标 `non_standard_path: true`\"）\n3. Read `<rubric-notes-path>` → 解析当前 rubric_version + 维度数量（7 或 9）+ 公式\n4. Read `<script-path>` → 拿到 script 全文 + 字数\n\n⚠️ **不要做的事**：\n- 不要为了\"看看用户做啥账号\"去 Read `benchmark.md` —— benchmark 是 Channel A 的 context，不属于本 sub-agent\n- 不要为了\"看看历史风格\"去 Glob `predictions/` —— 那是污染源\n- 不要去 Read `.cheat-state.json` 看 calibration 进度 —— 你**完全不需要知道**主 Claude 跑了多少篇\n\n### Phase 1：按 rubric 打 N 维分\n\n按 `rubric_notes.md` 当前 rubric 公式：\n\n- v0：7 维等权（ER / SR / HP / QL / NA / AB / SAT）—— 默认起步\n- v1：用户校准过的（权重不同）\n- v2 / v2.1 / ...：含 MS / TS 等新增维度（9 维）\n\n对每个维度：\n1. 给一个 **0-5 整数分**\n2. 给一个 **per-dim confidence** enum：`high | medium | low`\n   - high：稿子里有直接证据（一句话指向该维度）\n   - medium：可推断但需要解释\n   - low：稿子信号太弱，纯估\n3. 给一行 **理由** ≤ 30 字，**必须引用稿子里具体词或场景**\n\n不算 composite——composite 是公式行为，主 Claude 用回传的维度分自己算。\n\n### Phase 2：返回严格 JSON\n\n输出**只能**是一个有效 JSON。所有 markdown 解释都封禁——主 Claude 要的是结构化数据回主 context 解析。\n\n```json\n{\n  \"subagent_version\": \"v1\",\n  \"rubric_version\": \"v2\",\n  \"script_path\": \"scripts/2026-05-04_abc123_短title.md\",\n  \"script_hash\": \"<sha256:12 of script content>\",\n  \"scored_at\": \"<ISO 8601 +08:00>\",\n  \"dimensions\": {\n    \"ER\": { \"score\": 4, \"confidence\": \"high\",   \"reason\": \"PPT加油猫猫开头—具象画面，情绪反差强\" },\n    \"SR\": { \"score\": 3, \"confidence\": \"medium\", \"reason\": \"AI焦虑是议题但非热点对峙\" },\n    \"HP\": { \"score\": 5, \"confidence\": \"high\",   \"reason\": \"首句\\\"第七页大屏中央 加油猫猫\\\"具象反差\" },\n    \"QL\": { \"score\": 5, \"confidence\": \"high\",   \"reason\": \"\\\"加油猫猫救了我一命\\\"双关金句\" },\n    \"NA\": { \"score\": 4, \"confidence\": \"medium\", \"reason\": \"单线反思+收束，清晰但不复杂\" },\n    \"AB\": { \"score\": 4, \"confidence\": \"medium\", \"reason\": \"一人公司题但AI焦虑普适\" },\n    \"SAT\": { \"score\": 2, \"confidence\": \"high\",  \"reason\": \"共情调，几乎无讽刺\" }\n  },\n  \"input_status\": {\n    \"rubric_notes_read\": true,\n    \"script_read\": true,\n    \"any_other_file_read\": false\n  },\n  \"self_check\": {\n    \"saw_play_numbers\": false,\n    \"saw_comments\": false,\n    \"saw_retro_segment\": false,\n    \"any_contamination_signal\": false\n  },\n  \"refusal\": null\n}\n```\n\n`refusal != null` 的合法值：\n- `\"blocked_contaminated_input\"`：Task prompt 传了禁读路径（state / predictions / videos / 等）\n- `\"blocked_rubric_memo\"`：Task prompt 传了 `rubric-memo.md`（bump 升级档案，含实绩）\n- `\"blocked_audience\"`：Task prompt 传了 `audience.md`（受众画像，含评论派生的实绩信号）\n- `\"script_path_invalid\"`：找不到 script 文件\n- `\"rubric_unparseable\"`：rubric_notes.md 损坏\n- `\"non_blind_warning\"`：发现 contamination 苗头但勉强能打分（仍输出 dimensions，但 confidence 全降 medium）\n\n**JSON 必须可被 `python3 -c \"import json; json.loads(open(path).read())\"` 解析**。不允许：\n- 尾部多余逗号\n- 注释（JSON 不允许 //）\n- Markdown 围栏（输出根节点必须是 `{`）\n\n### Phase 3：（可选）写 sidecar 文件供主 Claude 二次读取\n\n如果 Task prompt 含 `sidecar_path` 参数 → 写 JSON 到该路径（典型用法：bump phase 2 批量打分时存多份 sidecar）。\n\n否则只走 Task return value——主 Claude 拿到 JSON 字符串直接解析。\n\n---\n\n## 主 Claude 调用契约（如何使用 channel B）\n\n调 Task 时，主 Claude 的 prompt **必须**含且**仅含**：\n\n```\nSpawn cheat-score-blind sub-agent.\n\nInput:\n  script_path: scripts/2026-05-04_abc123_短title.md\n  rubric_notes_path: rubric_notes.md\n  [optional] sidecar_path: .cheat-cache/blind-scores/<id>.json\n\nTask: 按 rubric_notes 当前公式给上面 script 打分。返回严格 JSON（见 cheat-score-blind/SKILL.md Phase 2 schema）。\n不要读 state file / predictions/ / videos/ 任何其他文件。\n不要询问用户 —— 你没有用户。\n```\n\n**禁止**塞进 Task prompt 的东西：\n- 用户对话的引用 / 摘录\n- \"前一次预测是 X\" / \"实际播放是 Y\" 这种 hint\n- \"用户是观点视频博主，最近发了 N 条\" 这种背景\n- 任何含数字 + \"万/w/k/M\" 的字符串\n- 任何 `predictions/*.md` 路径\n\n主 Claude 调用前自检：把准备发的 prompt 串过一遍 `grep -Ei '播放|阅读|点赞|评论数|实际|retro|复盘|实绩|w$|万$'`——命中 → **改 prompt 重发**，不要硬塞。\n\n---\n\n## Refusals\n\n- 「我作为 sub-agent 同时也读一下 predictions/ 帮你对比下」 → 硬拒。这就是 channel B 存在的全部理由\n- 「你看一下 .cheat-state.json 看 calibration_samples 决定你给的 confidence 高低」 → 硬拒。confidence 只看稿子证据强度，跟用户校准进度无关\n- 「主 Claude 说这条已经发了，你帮我打一份 reconstructed 分」 → 拒。\"已发\"信号本身就是污染。让主 Claude 标 `reconstructed: true` 自己处理，不要让 channel B 介入\n- 「输出我直接 markdown 表格更好读」 → 拒。Phase 2 schema 是 JSON only，主 Claude 解析后再渲染\n\n---\n\n## Known limitations（写在最显眼的地方）\n\n1. **sub-agent ≠ 真独立**：同一个 Claude 模型，RLHF priors 共享。一个全新 context 不会让模型变成另一个判分体系——它只是没看过该次对话的具体污染\n2. **不解决 rubric 设计 bias**：用户自己写的 rubric_notes.md 自然让自己内容显得好。这层 bias 由 Channel C（跨模型 audit）和定期 bump 验证解决\n3. **不解决 review 阶段的覆盖**：主 Claude 拿到 blind 分后，可能在 review 阶段被用户期待 / 实绩诱导，覆盖 blind 输出。`cheat-predict` Phase 2.5 通过 disagreement detection + 用户裁定来减轻，但不消除\n4. **同 prompt 两次调可能给不同分**：Claude 不是 deterministic。主 Claude 应该把每次 blind score 当一次采样，不当唯一真理——但要记录而不是丢弃差异\n\n## Integration\n\n- **`cheat-score`** Phase 2：默认 delegate 到本 sub-agent（替代旧的 inline 打分）\n- **`cheat-predict`** Phase 2：默认 delegate；Phase 2.5 用 disagreement detection\n- **`cheat-bump`** Phase 2：**强制** delegate，bump 时**不接受 self-scored fallback**\n- **`cheat-retro`**：不调用——retro 本来就看实绩，blind 无意义\n","frontmatter":{"name":"cheat-score-blind","description":"INTERNAL sub-agent for blind 7-dim rubric scoring. **NOT a user-facing skill — do NOT invoke from main conversation.** Called via Task tool by cheat-score / cheat-predict / cheat-bump to get a context-isolated score on a script. Receives ONLY script_path + rubric_notes_path; refuses any other input. Outputs strict JSON: 9 dimensions × {score 0-5, confidence enum, one-line reason}. **Hard refuses to Read** .cheat-state.json, predictions/*, retro 段, or anything that could leak post-publish data. This is channel B in the 3-channel calibration model (A=main, B=blind sub, C=cross-model).\n","allowed-tools":"Read, Glob, Grep","argument-hint":"<script-path> <rubric-notes-path>","allowedTools":["Read,","Glob,","Grep"]},"allowedTools":["Read,","Glob,","Grep"],"isInternal":false,"tokens":4223,"sizeBytes":12264},{"name":"SKILL.md","path":"skills/cheat-score/SKILL.md","rawUrl":"https://raw.githubusercontent.com/XBuilderLAB/cheat-on-content/HEAD/skills/cheat-score/SKILL.md","title":"cheat-score","category":"anthropic-skill","format":"markdown","content":"---\nname: cheat-score\ndescription: 给单篇稿子打 rubric 分。**只在控制台输出，不写文件，不预测**。触发词：\"打分这篇 [path]\"/\"score this [path]\"/\"给这稿子打分\"/\"先打分看看\"。是 cheat-predict 之前的轻量探索动作。\nargument-hint: <draft-path>\nallowed-tools: Read, Glob, Grep\n---\n\n# /cheat-score — 单稿打分\n\n打分但**不预测**。用户用它快速看稿子的 composite，决定是否值得进入正式预测流程。\n\n## Overview\n\n```\n[用户：打分这篇 draft.md]\n  ↓\n[读 draft.md + rubric_notes.md]\n  ↓\n[逐维度打 0-5 + 写一行理由 + 算 composite]\n  ↓\n[控制台输出：评分 + composite + 推荐下一步]\n  ↓\n[结束 — 不写任何文件]\n```\n\n## Constants\n\n- **RUBRIC_PATH = rubric_notes.md** — 当前 rubric 来源\n- **OUTPUT_DETAIL = full** — full: 含每维度理由；compact: 仅分数表\n\n> 💡 调用时覆盖：`/cheat-score draft.md — OUTPUT_DETAIL: compact`\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<draft-path>` | 用户作为参数传入；如缺失则在对话里询问 |\n| `rubric_notes.md` | 用户项目根 |\n| `.cheat-state.json` | 用户项目根（用于读当前 `rubric_version` 与 mode） |\n\n## Workflow\n\n### Step 1：前置检查\n\n1. 读 `.cheat-state.json` → 不存在则提示用户先跑 `/cheat-init`，停止\n2. 读 `<draft-path>` → 不存在或无内容 → 报错并停止\n3. 读 `rubric_notes.md` 找到当前生效的公式段（一般在\"当前评分维度\"或\"综合分公式\"位置）\n\n### Step 2：识别公式与维度\n\n从 `rubric_notes.md` 解析出：\n- 当前 rubric_version\n- 维度列表与权重（如 `ER×1.5 + SR×1.5 + HP×1.5 + QL + NA + AB + SAT`）\n- 归一化常数（如 `/ 8.5 × 2.0`）\n- 每个维度的 0-5 含义（从\"当前评分维度\"段表格读）\n\n如果 `rubric_notes.md` 格式与预期不符（用户手改过结构）→ 询问用户当前公式是哪一行，**不要自己猜**。\n\n### Step 3：**delegate 到 blind sub-agent**（不再 inline 打分）\n\n主对话已经被用户对话 / 已发数据 / 历史 retro 段污染——inline 打分等于带着后视镜判分。\n\n改成**通过 Task tool 调 `/cheat-score-blind` sub-agent**，主 Claude 只做调度 + review。详见 [skills/cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md)。\n\n**Task prompt 模板**（**只能含**下面这些）：\n\n```\nSpawn cheat-score-blind sub-agent.\n\nInput:\n  script_path: <用户给的 draft path>\n  rubric_notes_path: rubric_notes.md\n\nTask: 按 rubric_notes 当前公式给上面 script 打分。返回严格 JSON（见 cheat-score-blind SKILL.md Phase 2 schema）。\n不要读 state file / predictions/ / videos/ 任何其他文件。\n不要询问用户 —— 你没有用户。\n```\n\n**禁止**塞进 Task prompt 的东西（[cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md) 的\"主 Claude 调用契约\"段）：\n- 用户对话引用 / 摘录\n- 含播放数 / 万 / w / k 等字眼\n- \"前一次预测是 X\" / \"实际播放是 Y\" 等 hint\n- 任何 `predictions/*.md` 路径\n\n调用前 grep 自检：`echo \"<prompt>\" | grep -Ei '播放|阅读|点赞|评论数|实际|retro|复盘|实绩|w$|万$'` 命中 → 改 prompt 重发。\n\n### Step 4：解析 sub-agent 回传 JSON + review\n\nsub-agent 返回严格 JSON。主 Claude：\n\n1. 解析 dimensions 段（含 score + per-dim confidence + reason）\n2. 校验 `self_check.any_contamination_signal == false`，否则警告\n3. 按 rubric_notes 公式算 composite（公式逻辑在主，分数来自 sub-agent）\n4. **不修改 sub-agent 给的维度分**——score 只是显示。如果用户挑刺（\"AB 给 3 不是 4\"），主 Claude 记录到 `User Override` 但 sub-agent 原始分留档\n\n如果 sub-agent 返回 `refusal != null`：\n- `blocked_contaminated_input` → 报告 Task prompt 含违禁字段，让主 Claude 重发\n- `script_path_invalid` → 检查路径\n- `rubric_unparseable` → 提示用户 rubric_notes.md 损坏\n- `non_blind_warning` → 仍接受 dimensions（但 confidence 全 medium），警告\n\n### Step 5：算 composite + 输出\n\n按当前公式算综合分。控制台输出（OUTPUT_DETAIL=full）：\n\n```\n📊 [draft.md 短标题] — 打分（rubric: v2）\n\n| 维度 | 分 | 理由 |\n|---|---|---|\n| ER (情感共鸣)        | 5 | \"半夜三点翻聊天记录\" 极端具象 |\n| HP (钩子强度)        | 5 | IS 句一句锁定受众 |\n| QL (金句密度)        | 5 | MVP 句\"间歇性希望\"独立可传 |\n| NA (叙事性)          | 3 | 平铺直叙，弱弧线 |\n| AB (受众广度)        | 5 | 暗恋/前任普适 |\n| SR (社会议题共振)    | 2 | 纯个人情感，无社会托底 |\n| SAT (讽刺深度)       | 4 | 致谢段自指反讽 |\n\n公式：(ER×1.5 + SR×1.5 + HP×1.5 + QL + NA + AB + SAT) / 8.5 × 2.0\ncomposite = (5×1.5 + 2×1.5 + 5×1.5 + 5 + 3 + 5 + 4) / 8.5 × 2.0 = **8.24**\n\n📍 落在 30-100w 桶（基于 starter-rubrics 的 bucket 边界）\n\n下一步建议：\n- 如果你已写定最终稿、准备发布 → 说 \"启动预测\"\n- 如果想再改稿子 → 改完再打一次（多次打分不留痕迹）\n- 如果想看历史相近 composite 的样本 → 说 \"找 composite 8.0-8.5 的锚点\"\n```\n\nOUTPUT_DETAIL=compact 时仅输出分数表 + composite，不附理由列。\n\n### Step 6：**绝不**做的事\n\n- ❌ 写任何文件（包括 predictions/、rubric_notes.md、candidates.md）\n- ❌ 给 bucket 概率分布（那是 cheat-predict 的活）\n- ❌ 触发\"已发布\"或\"复盘\"逻辑\n- ❌ 提议 rubric 升级（即使打分时发现明显异常也只在控制台提示，不动 rubric）\n\n## Key Rules\n\n1. **打分走 sub-agent**。主 Claude 不再 inline 打分。看 [cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md) 的隔离协议\n2. **整数分**。不允许 4.5、3.7\n3. **盲打优先**。sub-agent 只看 script + rubric，天然盲——这是它存在的全部理由\n4. **理由是诊断工具**。每个维度的 1-30 字理由不是装饰——复盘时用来找出哪个维度判断错了\n5. **不写文件**。这是 score 与 predict 的核心区别。score 是探索，predict 是承诺\n6. **不算 candidate composite**。candidates.md 里的 composite 字段在 cheat-trends/cheat-recommend 里写——score 只服务\"已写好的具体稿子\"\n\n## Refusals\n\n- 「打分顺便预测一下」 → 拒绝。请改用 `/cheat-predict`。原因：predict 必须走 blind check + 写 immutable 日志，score 跳过这些\n- 「打完分把分数写进 rubric_notes.md 的观察段」 → 拒绝。observation lifecycle 规定观察必须有\"实绩 vs 预测\"对比，光有打分不构成观察\n- 「能不能直接告诉我会不会爆」 → 拒绝。给具体 composite + bucket 的判定要求走 predict 流程；score 只输出当前 rubric 下的机械计算\n- 「跳过 blind sub-agent 让主 Claude 直接打」 → cheat-score **不接受**这种 escape hatch（与 cheat-predict 不同；cheat-predict 有 `--skip-blind`）。score 是轻量探索，没理由放弃隔离。如真的 Task tool 不可用 → 提示用户配置后再试\n\n## Integration\n\n- 是 `cheat-predict` 的前置探索：用户可以反复 score 不同稿子版本，确定一份再 predict\n- score 不更新 `.cheat-state.json`——这是无副作用操作\n- 如果用户连续 score 同一稿子 ≥3 次 → 控制台温和提示\"反复打分会引入决策疲劳，差不多可以决定了\"\n","frontmatter":{"name":"cheat-score","description":"给单篇稿子打 rubric 分。**只在控制台输出，不写文件，不预测**。触发词：\"打分这篇 [path]\"/\"score this [path]\"/\"给这稿子打分\"/\"先打分看看\"。是 cheat-predict 之前的轻量探索动作。","argument-hint":"<draft-path>","allowed-tools":"Read, Glob, Grep","allowedTools":["Read,","Glob,","Grep"]},"allowedTools":["Read,","Glob,","Grep"],"isInternal":false,"tokens":2660,"sizeBytes":7422},{"name":"SKILL.md","path":"skills/cheat-seed/SKILL.md","rawUrl":"https://raw.githubusercontent.com/XBuilderLAB/cheat-on-content/HEAD/skills/cheat-seed/SKILL.md","title":"cheat-seed","category":"anthropic-skill","format":"markdown","content":"---\nname: cheat-seed\ndescription: 跟用户对话讨论选题——**默认一次一个**，用户主动给主题或经历，AI 围绕用户的输入深挖、提炼角度、写一份 draft。不是 AI 拿三个开放问题追用户，也不是一次给 5 个候选。触发词：\"找选题\"/\"我想做一条 X\"/\"最近有个想法\"/\"seed\"/\"启动种子\"。可选 batch 模式：`/cheat-seed --batch 5` 走旧的 brainstorm 5 候选 + 写 5 draft 流程。\nargument-hint: \"[— batch: N] [— sources: <comma-separated>]\"\nallowed-tools: Bash(*), Read, Write, Edit, Glob, WebFetch, Skill\n---\n\n# /cheat-seed — 选题对话（默认）/ 批量 brainstorm（可选）\n\ncheat-seed 的核心是**跟用户讨论选题**，不是机械地 brainstorm。好内容来自用户的真实经历 + 观察 + 情绪——这些是 AI 不可能凭空 brainstorm 出来的。AI 的角色是**听用户讲 → 帮提炼角度 → 写一份 draft**，不是 dump 15 候选让用户挑。\n\n**默认模式**：对话式一次一个。\n**Batch 模式**（`--batch N`）：保留旧的 brainstorm N 候选 + 写 N 份 draft 流程，给\"完全没想法 + 想批量初始化\"的用户。\n\n## 三种 Mode（自动识别）\n\n```\nMode A — 用户主动给主题（**最常见**）：\n  用户：\"/cheat-seed\" + 直接说\"我想做一条关于 X 的\"\n       或：\"/cheat-seed 我最近开会被领导...\"\n  ↓\n  AI 围绕 X / 这件事**深挖**——什么瞬间触发？最让你 [情绪 / 不爽 / 觉得有意思] 的是哪点？\n  ↓\n  收敛到一个具体角度 → 提议 → 用户认可 → 写 1 份 draft → 完成\n  ↓\n  问\"下一篇？\" 或用户说\"今天就这样\"\n\nMode B — 用户给方向但不具体：\n  用户：\"最近想做点关于 [职场 / 婚恋 / AI / ...] 的\"\n  ↓\n  AI：\"[范围] 太广。最近你接触到的具体哪件事让你想做这个方向？\"\n  ↓\n  收敛到 Mode A 的具体经历\n\nMode C — 用户完全没想法（少见）：\n  用户：\"我不知道做什么\" / \"帮我想个题\"\n  ↓\n  AI：\"好，进 brainstorm 模式——先抓热点 + 你之前的兴趣方向，给你 1 个建议\"\n  ↓\n  跑 trend-sources 抓热点 + 读 candidates.md / predictions/ 看用户历史\n  ↓\n  提议 1 个角度（不是 5 个） → 用户认可 → 写 draft\n\nBatch Mode — 用户显式要批量（`/cheat-seed --batch 5`）：\n  按旧版 brainstorm 流程：3 问题 → 15 候选 → 用户挑 → 写 5 draft。\n  给\"今天想一次性把未来 2 周的选题搞定\"的用户。\n```\n\n**关键纠正**（与旧版的区别）：\n- AI **不主动开放问**——等用户给输入再深挖\n- 一次一个选题，不是 5 个\n- 默认对话式 + 一次一个，batch 是 escape hatch\n\n## Constants\n\n- **DEFAULT_TREND_SOURCES = [\"manual-paste\"]** — 仅 Mode C / Mode A 灰色场景 / Batch 用到。用户可在 state 里加 aihot / trendradar-mcp\n- **TREND_TOOL_ROUTING** — 按 `content_form` 路由数据源，详见 [shared-references/data-source-routing.md](../../shared-references/data-source-routing.md)\n- **MODE_B_MAX_REPROBE_TURNS = 2** — Mode B \"为什么\" 反问最多 2 轮；超过则转 Mode C\n- **MAX_DEEP_DIVE_TURNS = 4** — Mode A 收敛阶段最多 4 轮反问，避免 AI 过度盘问\n- **WITH_DRAFT = yes** — 默认确认角度后立刻写 draft；用户可说 \"等下，我自己写\" 跳过\n- **DRAFT_LENGTH** — 派生自 `state.typical_duration_seconds`：30s→100-200字 / 90s→250-500字 / 240s→600-1000字 / 450s→1100-2000字 / 900s→2200+字\n- **HUMANIZE_DRAFT = on**（默认）/ off —— 写完 draft 后用 `humanizer` skill 过一遍，去掉 AI 写作 tells（em-dash 滥用 / rule of three / inflated 词汇 / 空泛归因等）。off 时直接出原始 AI draft。**只 humanize 正文，不动 header 的\"必须改写\"警告**\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `.cheat-state.json` | 读 calibration_samples / typical_duration / cadence |\n| `rubric_notes.md` | 读当前 rubric（粗打分用） |\n| `script_patterns.md` | 读已有 pattern（写 draft 时按 cheat sheet 选结构） |\n| `predictions/*.md`（如有） | 已发历史，brainstorm 时作为 context |\n| `audience.md`（如有） | 受众画像——选题 / 写稿时的\"谁在看\"镜子（由 `/cheat-persona` 派生） |\n\n## Workflow\n\n### Phase 0: 前置检查 + 加载所有 context（**核心：3 个 context 来源**）\n\n1. 读 `.cheat-state.json` → 不存在则提示先跑 `/cheat-init`\n2. 读 `rubric_notes.md` 拿当前公式（粗打分用）\n3. 读 `script_patterns.md`——写 draft 时按 cheat sheet 选结构\n4. **读已有 prediction 文件**（含 init 时 import 的 reconstructed）作为 **context 来源 A**（用户自己历史）：\n   - 0 个 → A 为空\n   - ≥1 个 → A 有内容，提取 (title / 7 维 / 实绩)\n5. **读 `benchmark.md`**（如存在）作为 **context 来源 B**（对标账号）：\n   - `state.benchmark_status = imported` → B 有内容，提取对标账号的样本主题分布、调性、Pattern\n   - `state.benchmark_status = none / pending` → B 为空\n6. **读 `audience.md`**（如存在且非空骨架）作为 **受众镜子**：\n   - Confidence 🟡 以上（有真实复盘数据派生）→ 选题 / 写 draft 时作为\"这个 persona 会在乎吗\"的检验视角\n   - Confidence 🔴/🟠（空 / 仅 benchmark seed）→ 当弱参考，不当硬约束\n   - **不进粗打分**——audience.md 是 creative lens，粗打分仍只用 rubric。persona 影响\"写什么 / 哪个角度\"，不影响分数\n7. 检查用户的入参——是否含具体 topic / 经历，决定走 Mode A/B/C/Batch\n\n**brainstorm 时的 context 优先级**（**Claude 判断**——下面是参考默认）：\n\n- **A 主导**（用户自己数据）：当 Claude 判断用户数据已能驱动方向时（参考默认：`calibration_samples ≥ 10`，但 Claude 可以更早——如 N=5 但出现 ≥3 个与 benchmark 明显不一致的强样本）\n- **B 主导**（benchmark）：用户数据少 + benchmark 有内容时\n- **B 缺席**（benchmark 为空）+ 用户数据少：纯靠用户 input + 抓热点；明确告诉用户\"没 benchmark 也没足够自己数据，建议跑 /cheat-learn-from 后再回来 brainstorm\"\n\n判断依据**不是死磕样本数**，而是看：\n- 用户最近 N 个样本的实绩**是否与 benchmark 的高表现样本类型一致**——一致说明 benchmark 仍有借鉴价值；不一致说明用户已经走自己的路\n- 用户的样本**多样性**——3 篇都是同类内容不算成熟；3 篇覆盖不同类目反而比 10 篇同类更可信\n\n### Phase 1: Mode 分流\n\n读用户输入，识别：\n\n**含具体名词 + 情绪 / 经历词**（\"我昨天开会...\" / \"我看到 X 让我...\" / \"我对 Y 觉得...\"）→ **Mode A**（深挖；如话题是时事，Phase 2A.5 询问要不要拉外部数据）\n\n**含方向词但无具体内容**（\"想做职场\" / \"AI 方向\" / \"婚恋\"）→ **Mode B**（单问\"为什么想做这个\"——用户内省窗口，**不调任何热点工具**）\n\n**显式说没想法**（\"不知道做什么\" / \"帮我想\" / \"随便给个\"）→ **Mode C**（按 [data-source-routing.md](../../shared-references/data-source-routing.md) 调热点工具 + 用户挑 + 回到内省；都不行则走\"聊经历\"三选项兜底）\n\n**显式 `--batch N`**（用户主动批量）→ **Batch Mode**\n\n**纯 `/cheat-seed` 无附加内容** → **询问入口问题**：\n\n```\n你今天想干嘛？\n\n- 有想做的主题 / 经历 → 直接告诉我（\"我想做一条 X\" / \"我最近 X...\"）\n- 知道大致方向 → 告诉我（\"想做职场\" / \"AI 方向\"）→ 我会单问你为啥想做\n- 完全没想法 → 说\"帮我想\"→ 我用 [aihot / trendradar] 拉今天的热点给你看\n- 批量搞定 → 说 \"batch <N>\"\n\n(我不会拿一堆开放问题追你——你给一句话我就开始)\n```\n\n注意这是**唯一的开放式问题**——只在用户**纯触发** `/cheat-seed` 时问。如果用户已经在触发词里给了内容（\"/cheat-seed 我想做...\" 或 \"找选题 我最近开会...\"），直接进 Mode A/B/C 不再问这一句。\n\n### Phase 2A: Mode A 深挖（用户给了具体 topic / 经历）\n\n**核心原则**：围绕用户给的内容**深挖**，**不要切到别的话题**。\n\n**反问类型（按场景挑）**：\n\n- 触发瞬间：\"你说 X 这件事，最初是哪个具体瞬间触发你想做的？\" / \"是什么让你觉得这值得讲一条视频？\"\n- 情绪锚点：\"这里面最让你 [生气 / 觉得荒唐 / 觉得有意思] 的是哪个细节？\"\n- 角度选择：\"你想说的是 [角度 a：现象批判] 还是 [角度 b：自我反思] 还是 [角度 c：泛化到普遍]？\"\n- 受众想象：\"你心里想着是说给哪种人听？她/他听完会怎么想 / 怎么转发？\"\n- 反对意见探测：\"如果有人反驳说 [反方观点 X]，你会怎么回？\"——逼用户先想清楚立场\n\n**反问纪律**：\n- 一次只问 **1 个**问题（不要塞 3 个连珠炮）\n- 最多 `MAX_DEEP_DIVE_TURNS` 轮（默认 4）——超过就主动收敛：\"OK 我感觉够了，帮你提议一个角度试试\"\n- 用户的回答如果含 emoji / 简短 / 不耐烦 → 立刻收敛，不要逼\n\n**收敛输出**：\n\n```\n我感觉这个角度能做：\n\n[一句话立意：50 字以内]\n\n走法：\n- 用 [Pattern X 结构]（来自 script_patterns.md）\n- 钩子：[具体场景 / 句子]\n- 主体：[3 个观察是什么]\n- 收尾：[MVP 句方向]\n\n粗打分（v0 等权 7 维）：ER=X HP=X QL=X NA=X AB=X SR=X SAT=X → composite ≈ X.X\nConfidence: 🔴 极低 (你才校准 0/N 篇)\n\n要不要让我先写一份 draft？(yes / 换角度 / 我自己写)\n```\n\n用户回 yes → Phase 4 写 draft。\n用户说\"换角度\" → 回 Phase 2A 深挖更多。\n用户说\"我自己写\" → 把 candidate 加进 candidates.md 标 tier1，结束。\n\n### Phase 2B: Mode B — 单问\"为什么\"，触发用户内省\n\n用户给了方向但不具体（\"想做职场\" / \"AI 方向\" / \"婚恋\"）。**这阶段不调任何热点工具**——是用户内省的窗口，外部信息会污染。\n\n只问一个问题，**单刀直入**：\n\n```\n为什么想做这个主题？\n```\n\n不要问\"a/b/c 三种例子\"——那是 dump 选项让用户挑，破坏内省。让用户自己组织语言。\n\n**根据用户回答分流**：\n\n| 用户答 | 分类 | 行为 |\n|---|---|---|\n| 含具体经历 / 个人卡点（\"我自己经常加班\" / \"我看到 X 让我...\"） | **真动机** | 转 Mode A 深挖（Phase 2A） |\n| 抽象热度归因（\"这话题最近热\" / \"别人都在做\" / \"听说能涨粉\"） | **空动机** | 反问\"那你自己对这个话题最有感觉的角度是啥？\"——逼出个人 stake；继续空 → 转 Mode C |\n| \"我也不知道\" / \"朋友说赚钱\" / 模糊推搡 | **真没想法** | 直接转 Mode C |\n\n**反问纪律**：最多 2 轮。第 2 轮还问不出真动机 → 直接转 Mode C，**不要无限挖**。\n\n> 设计意图：Mode B 是\"过滤器\"，不是\"工厂\"。用户来这里要么暴露真实动机（→ 进 Mode A）要么暴露自己其实没想法（→ 进 Mode C）。两条都比硬要在 Mode B 里产出选题更好。\n\n### Phase 2C: Mode C — 外部素材 + 用户挑 + 回到内省\n\n用户完全没想法（直接显式说 / 从 Mode B 转过来）。**这是唯一默认调热点工具的入口**。\n\n按 [shared-references/data-source-routing.md](../../shared-references/data-source-routing.md) 的路由规则：\n\n1. **拉外部素材**（按 `content_form` 选 trend source）：\n   - `tutorial-builder` / AI 类 → 调 aihot skill\n   - `opinion-video` / `long-essay` / `podcast` / `other` → 调 trendradar-mcp（如启用）\n   - `mixed` → 两个都调\n   - 都不可用 → 走 manual-paste（询问用户：\"今天看到啥可以拍的？粘几条 URL/标题\"）\n\n2. **聊经历兜底**（用户拒绝看外部素材 / 外部素材都不感兴趣）：\n\n   ```\n   外部素材你都没感觉，那回到你自己。三个开口，挑一个开始讲：\n\n   a) 你最近真实接触到的某件具体事？（\"上周我看到我同事 X...\"）\n   b) 你最近读到 / 看到的某条让你想吐槽的内容？（\"知乎上有个回答...\"）\n   c) 你长期琢磨的某个 unsolved 困惑？（\"我一直没想明白为啥 X...\"）\n\n   随便挑一个开始讲。\n   ```\n\n3. **拉到外部素材后**，用 rubric 粗筛 + 按 content_form 过滤，留 5 条最契合的：\n\n   ```\n   今天这 5 个跟你形态契合：\n   1. [标题 A]（来源: trendradar / 微博热搜 / hot_score: 8.5）\n   2. [标题 B]（来源: aihot / 模型类 / 精选）\n   3. ...\n\n   你哪个最有感觉？没感觉就回 '都没'，我换方向问。\n   ```\n\n4. **用户挑了一条 → 回到内省**：\n\n   ```\n   OK [标题 X]。你为啥对这条最有感觉？\n   是 [angle1] 还是 [angle2] 还是别的？\n   ```\n\n   → 用户答 → 转 Mode A 深挖。\n\n5. **用户回 \"都没\"** → 转回 Mode C 第 2 步的\"聊经历兜底\"。\n\n**关键**：热点不是\"塞 5 候选让用户挑\"，是\"给材料 + 强制问用户的个人 stake\"——AI 不替用户决定哪条最值得做。\n\n### Phase 2A.5: Mode A 灰色场景 — 用户讲了时事话题\n\nMode A 默认深挖用户经历。但如果**用户讲的本身是时事话题**（产品名 + 时间词 / 人名 + 事件词），按 [data-source-routing.md](../../shared-references/data-source-routing.md) 的\"时事判定\"规则，**询问**用户要不要拉外部数据作参考：\n\n```\n💡 [话题] 是时事——我可以拉一下今天的舆论风向（各平台情绪 + 主要 angles）作参考。\n\n要看吗？回 '看' 我调；回 '不用' 我直接跟你深挖你的角度。\n```\n\n用户回 \"看\" → 调对应 trend source → 把数据 inline 到深挖 context；\n用户回 \"不用\" → 标准 Mode A 深挖，不动外部数据。\n\n**永远不主动调**——用户的 angle 优先于外部数据，避免外部信息**带偏**用户视角。\n\n### Phase 2D: Batch Mode（用户显式 `--batch N`）\n\n**保留旧 brainstorm 流程**：\n\n1. 问 3 个清单问题（兴趣 / 调性 / 红线）—— Batch 模式才问这些\n2. 抓热点 + Claude brainstorm 15 候选\n3. 用户挑 N\n4. 写 N 份 draft 到 scripts/——**每份都走 Phase 4 的段落版格式 + Phase 4.5 自检**（line-format + humanizer），不因为批量就跳过\n\n详见 commit history（旧 cheat-seed 的 Phase 1-3）。这是 escape hatch，不是默认。\n\n### Phase 3: 计算 candidate ID + 落候选池\n\n不管 Mode A/B/C 哪条路径，确认角度后：\n\n1. 算 candidate id：`sha256(\"seed-\" + 立意 + 触发时间)[:12]`\n2. 写一行 entry 到 `candidates.md`（按 [candidate-schema.md](../../shared-references/candidate-schema.md) 格式）\n3. 标 `tier=tier1` + `read_status=deep_read`（已经讨论过，不是 skim）\n\n### Phase 4: 写 draft\n\n`WITH_DRAFT=yes` → 顺次写到 `scripts/<YYYY-MM-DD>_<id>_<short-title>.md`：\n\n**写 draft 前必读** `script_patterns.md` —— 按\"结构选型 cheat sheet\"对应用户的 topic 选合适结构。如果文件还在抽象骨架阶段（用户没填几个 pattern），就用 starter rubric 对应的通用框架。\n\n**字数**：按 `DRAFT_LENGTH` 派生（基于 `typical_duration_seconds`）。\n\n#### ⚠️ 正文必须是段落版，不是字幕格式（**最常见的生成跑偏**）\n\n模型的训练先验会把\"视频脚本\"默认写成提词器/字幕的短行格式。**这是错的**——cheat-seed 的 draft 是给用户**改写**的散文稿，不是拍摄字幕。字幕是剪映拍后自动断的，不是写作时的形态。\n\n生成正文时，眼睛盯住下面这个对照：\n\n```\n❌ 字幕格式（不要这样写）：\n你有没有发现\n所有审稿人都在说一样的话\n你的研究太老套了\n但你仔细看\n他们引用的全是 5 年前的反应\n\n✅ 段落版（必须这样写）：\n你有没有发现，所有审稿人都在说一样的话——你的研究太老套了。但你仔细看，他们引用的全是 5 年前的反应。AI 不是新东西，新的是这次大家集体觉醒了。\n```\n\n规则：\n- **每段 100-300 字**，逗号 / 句号 / 破折号自然连，**不在句子边界硬断行**\n- 段与段之间空一行（自然的主题切换才换段）\n- 一份 draft 正文一般 3-6 段，**不该有几十个单句行**\n\n#### 格式：\n\n```markdown\n# [立意标题]\n\n> ⚠️ **Draft by Claude — 你必须改写后再拍**\n>\n> 这是脚手架，不是成品。你的语气 / 节奏 / 个人经历无法 AI 生成。\n> 改写流程：\n> 1. **直接在本文件改写**（同 path：scripts/<...>.md）\n>    - 加你的语气、个人经历、真实金句\n>    - 砍铺垫、砍模型缩写、砍学术包装\n> 2. 改完后跑 `/cheat-predict scripts/<本文件>.md`\n> 3. 拍完跑 `/cheat-shoot scripts/<本文件>.md`\n\n**Article ID**: <12 位 hash>\n**调性**: [基于讨论得出的，不是清单 Q]\n**目标时长**: <state.typical_duration_seconds 转换> 分钟\n**目标字数**: <按时长派生>\n**结构选型**: [按 script_patterns.md 的 cheat sheet 显式标，如 \"metaphor 优先\" / \"数据反转开场\"]\n**用到的 patterns**: [编号 + 简短说明]\n**讨论种子**: [一句话回顾 deep dive 出来的核心]\n\n---\n\n[draft 正文 —— **段落版**，3-6 段，每段 100-300 字，不是单句碎行]\n```\n\n`WITH_DRAFT=no`（用户说\"我自己写\"）→ 跳过 Phase 4 + Phase 4.5。\n\n### Phase 4.5: draft 自检 pass（版式 + 去 AI 味）\n\ndraft 写完落盘后、**在展示给用户前**跑两步自检。**顺序固定：先 4.5a 修版式，再 4.5b 去 AI 味**——humanizer 处理散文，喂它字幕格式的碎行会乱。\n\n**为什么安全**（不污染校准）：cheat-seed 的 draft 不是被预测/发布的东西——用户改写后、cheat-predict 打分的是**用户最终稿**。这两步只是给用户更干净的起点。\n\n#### Phase 4.5a: line-format 自检（字幕格式 → 段落版）\n\nPhase 4 的散文指令 + ❌/✅ 对照已经在生成时压先验，但生成仍可能跑偏。这一步是**确定性兜底**：\n\n```bash\n# 只看正文段（--- 分隔线之后）\nbody=$(awk '/^---$/{f=1;next} f' scripts/<id>.md)\nline_count=$(printf '%s\\n' \"$body\" | grep -c .)        # 非空行数\nchar_count=$(printf '%s' \"$body\" | wc -m | tr -d ' ')\navg_chars_per_line=$(( char_count / (line_count > 0 ? line_count : 1) ))\n```\n\n判定：**`avg_chars_per_line < 15` 且 `line_count >= 8`** → 判定为字幕格式 → **自动重排**：\n- 把句子边界的硬断行合并回自然段落\n- 按主题切换分 3-6 段，每段 100-300 字\n- 用 Edit 替换正文段（header 不动）\n- 在 Phase 5 输出里标一行：\"📐 检测到字幕格式，已重排为段落版\"\n\n不命中 → 跳过，正文已经是段落版。\n\n#### Phase 4.5b: humanizer 去 AI 味\n\n`HUMANIZE_DRAFT=on`（默认）—— 用 `humanizer` skill 过一遍。Claude 自己写的初稿天然带 AI tells（em-dash 滥用 / rule of three / \"inflated\" 词汇 / 空泛归因 / -ing 浅层分析），这一步把它们清掉。\n\n步骤：\n\n1. 检查 `humanizer` skill 是否可用（`~/.claude/skills/humanizer/` 存在）：\n   - 不可用 → 跳过 4.5b，在 Phase 5 输出里加一行\"（humanizer 未装，draft 是原始 AI 版——`git clone https://github.com/blader/humanizer` 到 ~/.claude/skills/ 可启用自动去 AI 味）\"\n2. 可用 → 通过 Skill tool 调 `humanizer`，**只传 draft 正文**（`---` 分隔线之后、4.5a 已重排好的段落版），**绝不传 header**：\n   - header 的 `⚠️ Draft by Claude — 你必须改写后再拍` 警告是**有意的脚手架标记**，不是要 humanize 的散文\n   - **voice calibration**：如果用户有历史脚本（`videos/*/script.md`）或填过 `script_patterns.md`，把最近 1-2 份作为 humanizer 的 voice 参考样本一起传——让它往\"**这个用户的声音**\"靠，而不是\"通用人声\"\n3. humanizer 返回去 AI 味的正文 → 用 Edit 替换 draft 文件的正文段（header 不动）\n4. 记录 humanizer 报告的\"修了哪些 tell\"（如 `em-dash 滥用 ×3 / rule of three ×2 / inflated 词汇: \"深刻\" \"本质上\"`），Phase 5 输出里展示\n\n**纪律**：\n- humanizer 是**去 AI 味**，不是**替用户改写**。它让 draft 不那么像机器写的，但**仍不是用户的声音**——header 的\"必须改写\"警告依然成立，Phase 5 输出要重申\n- 如果 humanizer 把某句改得偏离了 `结构选型` / 用到的 pattern → 以 pattern 为准，那句回滚（pattern 是和用户讨论定的，humanizer 不该推翻）\n- humanizer **不负责版式**——断行问题在 4.5a 已经修完，humanizer 拿到的已是段落版\n\n### Phase 5: 输出\"下一步\" + 询问继续\n\n```\n✅ Draft 写完：scripts/2026-05-04_<id>_<short>.md\n📐 版式自检：通过（段落版）  ← 或\"检测到字幕格式，已重排为段落版\"\n🧹 humanizer 过了一遍：修了 em-dash 滥用 ×3 / rule of three ×2 / inflated 词汇 2 处\n   （draft 现在不那么\"机器味\"了——但这仍是脚手架，不是你的声音）\n\n接下来你可以：\n- 改写这份 draft（直接在原文件改）—— 加你的语气、经历、真实金句\n- 改完跑 \"打分这篇 scripts/<...>.md\" 看 7 维评分\n- 决定要拍 → \"启动预测 scripts/<...>.md\"\n\n下一篇你想做什么？\n（直接告诉我具体经历 / topic，或者说\"今天就这样\"结束）\n```\n\n> humanizer 那行只在 `HUMANIZE_DRAFT=on` 且 skill 可用时出现。未装时换成一行提示如何启用。\n\n用户说\"今天就这样\" → 结束 cheat-seed。\n用户给新 topic → 回 Phase 1 重新分流。\n\n## Key Rules\n\n1. **AI 不主动开放问**——只在用户纯触发 `/cheat-seed` 时问一次入口问题，其他时候**等用户给输入再深挖**\n2. **一次一个选题**——默认 Mode A/B/C 都给 1 个建议；用户主动要批量才走 Batch\n3. **反问纪律**：一次问 1 个，最多 4 轮，用户不耐烦立刻收敛\n4. **深挖围绕用户给的话题**，不要切到别的——你说\"开会被领导骂\"，AI 不该问\"那你最近有没有觉得 AI 让大家...\"这种平行话题\n5. **写 draft 必须读 script_patterns.md**——按用户已有 pattern 选结构\n6. **draft 是脚手架**——header 加醒目警告\"必须改写\"\n7. **humanizer 只去 AI 味，不替用户改写**——Phase 4.5b 让 draft 不那么机器味，但它仍不是用户的声音；\"必须改写\"的警告不因为过了 humanizer 就失效\n8. **正文是段落版不是字幕格式**——生成时盯 Phase 4 的 ❌/✅ 对照；Phase 4.5a 用 `avg_chars_per_line < 15 且行数 ≥ 8` 做确定性兜底，命中就重排。字幕是剪映拍后自动断的，不是写作时的形态\n\n## Refusals\n\n- 「跳过深挖，直接写 draft」 → 询问\"你想直接给主题让我写吗？OK 但 draft 质量可能差——我不知道你的角度。给我一句话立意我就写\"\n- 「AI 替我决定 topic」 → 拒绝。Mode A/B/C 路径里 AI 永远只**呈现外部素材** + **问用户角度**，不替用户拍板\"做哪条\"\n- 「Mode B 我懒得回答为什么，直接给我 5 个候选吧」 → 拒绝。Mode B 的\"为什么\"是过滤器——你答不出来就不该用 Mode B 的方向。要么进 Mode A 给具体经历，要么进 Mode C 我帮你找素材\n- 「Mode A 时直接帮我拉热点，不问我同意」 → 拒绝。Mode A 用户已经有 angle，未经允许拉外部数据会污染他的视角。详见 [data-source-routing.md](../../shared-references/data-source-routing.md)\n- 「一次写 5 个 draft」 → 不在默认流程。用户必须显式 `--batch 5`\n- 「我懒得改写，直接拍 AI draft」 → 警告\"AI 直接生成的稿子拍出来 ER 偏低，会污染你的校准数据\"，但用户坚持也允许（标 `unmodified_ai_draft: true`）\n\n## Integration\n\n- 上游：`/cheat-init` Phase 5 末尾在 `pool_status=none + calibration_samples=0` 时主动询问\"现在跑 /cheat-seed？\"\n- 上游：`/cheat-recommend` 在 candidates 空时引导文案中提及 `/cheat-seed`\n- 上游：`/cheat-status` 在 `pool_status=none + 距 init >24h` 时提示\"还没拍——跑 /cheat-seed？\"\n- 下游：用户的 candidate → candidates.md（tier1，已 deep_read）\n- 下游：（默认）draft → Phase 4.5 humanizer 去 AI 味 → scripts/<id>.md → 用户改写 → /cheat-predict\n- 可选依赖：[`humanizer`](https://github.com/blader/humanizer) skill（MIT，外部项目）。装在 `~/.claude/skills/humanizer/` 时 Phase 4.5 自动启用；未装则优雅跳过。**不打包进 cheat-on-content**——用户自己 clone\n- 与 `/cheat-trends` 区别：cheat-seed 是**讨论 + 写 draft**（重 conversation）；cheat-trends 是**多 adapter 抓 + 粗打分**（重 fetch）。两者目的不同，不互相替代。\n","frontmatter":{"name":"cheat-seed","description":"跟用户对话讨论选题——**默认一次一个**，用户主动给主题或经历，AI 围绕用户的输入深挖、提炼角度、写一份 draft。不是 AI 拿三个开放问题追用户，也不是一次给 5 个候选。触发词：\"找选题\"/\"我想做一条 X\"/\"最近有个想法\"/\"seed\"/\"启动种子\"。可选 batch 模式：`/cheat-seed --batch 5` 走旧的 brainstorm 5 候选 + 写 5 draft 流程。","argument-hint":"[— batch: N] [— sources: <comma-separated>]","allowed-tools":"Bash(*), Read, Write, Edit, Glob, WebFetch, Skill","allowedTools":["Bash(*),","Read,","Write,","Edit,","Glob,","WebFetch,","Skill"]},"allowedTools":["Bash(*),","Read,","Write,","Edit,","Glob,","WebFetch,","Skill"],"isInternal":false,"tokens":9338,"sizeBytes":24873},{"name":"SKILL.md","path":"skills/cheat-shoot/SKILL.md","rawUrl":"https://raw.githubusercontent.com/XBuilderLAB/cheat-on-content/HEAD/skills/cheat-shoot/SKILL.md","title":"cheat-shoot","category":"anthropic-skill","format":"markdown","content":"---\nname: cheat-shoot\ndescription: 登记一条视频已拍摄。**建 video folder + 询问实际拍摄稿是否与 scripts/<id>.md 一致 + buffer +1**。与 cheat-publish 配对：拍了进队列，发了出队列。触发词：\"拍了\"/\"拍了 X\"/\"shot\"/\"shot it\"/\"已拍 X\"/\"录完了\"。\nargument-hint: <scripts-path-or-id>\nallowed-tools: Bash(*), Read, Write, Edit, Glob\n---\n\n# /cheat-shoot — 登记拍摄完成 + 建 video folder + (改稿则) 触发 v2 预测\n\n把视频从\"已写预测、未拍摄\"状态推进到\"已拍摄、未发布\"状态。这一步：\n1. **建 `videos/<同 id>/`** 目录（之前没有的话）\n2. **询问用户**：\"实际拍摄时用的稿子和 `scripts/<id>.md` 一致吗？\"\n3. 算 diff——超过 V2_TRIGGER_THRESHOLD (默认 30%) → **delegate 到 `/cheat-predict — mode: v2`** 在原 prediction 文件 append `## 预测 v2` 段\n4. 把 video folder 加进 state.shoots 队列，buffer +1\n\ncheat-shoot 自己**不**写预测内容——所有预测落盘逻辑在 cheat-predict。cheat-shoot 只负责检测改稿 + 派发。\n\n为什么单独一个 skill：\n- buffer 警戒系统需要明确区分\"拍了\" vs \"发了\"。视频可以批量拍（一天拍 5 条），分散发（每天发 1 条）\n- \"实际拍摄稿\" ≠ \"pre-shoot 草稿\"是常态。这一步是把 diff 显式化、触发 v2 重判、采集\"用户改稿 pattern\"信号的入口\n- v2 预测 vs v1 预测的差异本身就是 rubric 升级证据——比如 v1 给 ER=4，v2 给 ER=5（用户改稿改高了 hook 强度），就告诉 rubric \"这个用户的 ER 阈值跟我现在公式不一致\"\n\n## Overview\n\n```\n[用户：拍了 scripts/2026-05-04_abc123_停止期待.md]\n  ↓\n[Phase 0: 解析路径 + 验证 prediction 已存在]\n  ↓\n[Phase 1: 检查是否已登记（避免重复）]\n  ↓\n[Phase 2: 建 videos/<id>/ + 询问\"实际拍摄稿一致吗？\"]\n  ↓\n[Phase 3: 写 videos/<id>/script.md]\n  ↓\n[Phase 4: append state.shoots]\n  ↓\n[Phase 5: 输出 buffer 状态]\n```\n\n## Constants\n\n- **REQUIRE_PREDICTION = true** — 拍前必须先有 v1 prediction 文件\n- **V2_TRIGGER_THRESHOLD = 0.30** — normalize 后 char-level diff 超过 30% → 默认建议 v2 重判；低于 30% 询问用户是否仍要 v2\n- **DIFF_METRIC = char_levenshtein_normalized**（**默认**）—— 通过 [`tools/diff_pct.py`](../../tools/diff_pct.py) 调用：先 normalize（去 markdown header / 分隔线 / 列表标记 / 装饰标点 / 折叠所有空白），再算 char-level Levenshtein / max(len_a, len_b)。preferred backend `rapidfuzz`，fallback `difflib.SequenceMatcher`（stdlib，永远可用）。**旧版 line-level 在口语化转录场景误报严重**（draft 长 markdown 句 vs whisper 转录的短断句，内容几乎不变但 line-level 算出 ~200% diff）—— PR #14 修复\n- **DIFF_METRIC=lines** —— legacy fallback：当 python3 完全不可用或 tools/diff_pct.py 找不到时降级到 `diff -u | grep '^[+-]' | wc -l` 算法\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<scripts-path-or-id>` | 用户参数；缺失则询问 |\n| `.cheat-state.json` | 状态文件 |\n| `scripts/*.md` | pre-shoot 草稿 |\n| `predictions/*.md` | 验证对应预测存在 |\n\n## Workflow\n\n### Phase 0：解析 + 验证\n\n1. 解析用户给的路径——支持几种形态：\n   - 完整路径 `scripts/2026-05-04_abc123_停止期待.md`\n   - 简写 `2026-05-04_abc123_停止期待`\n   - id 简写 `abc123` → glob `scripts/*_abc123_*.md` 找匹配\n2. 验证 `scripts/<id>.md` 存在：不存在 → 报错\"找不到 pre-shoot 草稿\"\n3. 验证有对应 prediction `predictions/<同名>.md`：\n   - 不存在 → **拒绝登记**，提示\"先跑 /cheat-predict 写预测，否则违反盲预测原则——你不能拍完才写预测，那等于事后看了画面写\"\n   - 存在 → 通过\n\n### Phase 1：检查重复\n\n读 `.cheat-state.json`，检查 `shoots[]` 是否已含此 id：\n- 已存在 → 警告\"已登记过（X 天前）。是要重新登记，还是要用 /cheat-publish 发布？\"\n- 不存在 → 进入 Phase 2\n\n### Phase 2：建 video folder + 询问稿子一致性\n\n1. 建目录 `videos/<id>_<short>/`（同 scripts/ + predictions/ 的命名）\n2. **询问用户**：\n\n```\n拍 「<title>」 的时候，你实际用的稿子和 scripts/<id>.md 一致吗？\n\na) 一致——按草稿拍的\nb) 改了一些——你能给我看下实际拍摄稿吗？我重新打分一次（v2 预测）\nc) 大改了，基本是另一条 → 走 _redo 流程：\n   scripts/<id>_redo.md → 重新 cheat-predict → 再 cheat-shoot（原 prediction 留档脱钩）\n```\n\n### Phase 3：写 videos/<id>/script.md + (b 路径) 触发 v2 预测\n\n**a 路径（一致）**：\n- `cp scripts/<id>.md → videos/<id>/script.md`\n- `script_consistency = consistent`\n- 不重判，进 Phase 4\n\n**b 路径（改了）**：\n1. 询问用户实际拍摄稿——粘贴文本 / 文件路径 / 转录文件\n2. 若用户提供 → 写入 `videos/<id>/script.md`\n3. 若用户没保留（即兴）→ 标 `script_lost`，写占位文件 + 警告\"v2 重判跳过——下次建议留稿（哪怕 voice memo 转录）\"，进 Phase 4\n4. 提供了的话：算 diff\n   ```bash\n   # 解析 cheat-on-content 源码根（cheat-shoot 是 symlink 装的）\n   SKILL_REAL=\"$(readlink -f ~/.claude/skills/cheat-shoot 2>/dev/null || readlink ~/.claude/skills/cheat-shoot 2>/dev/null)\"\n   if [[ -n \"$SKILL_REAL\" ]]; then\n     REPO_ROOT=\"$(cd \"$SKILL_REAL/../..\" && pwd)\"\n     DIFF_TOOL=\"$REPO_ROOT/tools/diff_pct.py\"\n   fi\n\n   if [[ -n \"${DIFF_TOOL:-}\" && -f \"$DIFF_TOOL\" ]] && command -v python3 >/dev/null 2>&1; then\n     # 默认 char-level Levenshtein on normalized text（rapidfuzz preferred, difflib fallback）\n     diff_pct=$(python3 \"$DIFF_TOOL\" \"scripts/<id>.md\" \"videos/<id>/script.md\")\n   else\n     # legacy line-level fallback——只在 python3 或 diff_pct.py 都不可用时用\n     added=$(diff -u scripts/<id>.md videos/<id>/script.md | grep -c '^+')\n     removed=$(diff -u scripts/<id>.md videos/<id>/script.md | grep -c '^-')\n     total_orig=$(wc -l < scripts/<id>.md)\n     diff_pct=$(( (added + removed) * 100 / total_orig ))\n     echo \"⚠️  fallback 到 line-level diff——口语化转录会 inflate diff_pct，可能误触发 v2\"\n   fi\n   ```\n\n   **为什么 normalize + char-level**：line-level diff 在创作者真实场景（draft 是 markdown 长句、拍摄稿是 whisper 转录的口语化短行）算出 ~200% 差异但内容几乎不变。char-level Levenshtein 在 normalize 后稳定反映**内容**差异，而非格式差异。详见 [`tools/diff_pct.py`](../../tools/diff_pct.py) + `tools/diff_pct_test.sh`（3 fixture 在两个 backend 上全过）。\n5. **判定 v2 触发**：\n   - `diff_pct >= 30` → 默认建议 v2 重判，**主动调用** `/cheat-predict — mode: v2 — prediction-file: predictions/<id>.md` 传 `videos/<id>/script.md` 作 input。cheat-predict 走 v2 模式 append `## 预测 v2`\n   - `diff_pct < 30` → 询问用户：\"只改了 N% 的内容，要重判吗？默认不（v1 预测仍有效）\"。用户说要 → 同上调用；用户说不 → 跳过 v2，继续 Phase 4\n6. cheat-predict 完成 v2 落盘后，控制权回到 cheat-shoot 进 Phase 4\n\n**c 路径（大改）**：\n- 不写 `videos/<id>/script.md`，提示走 `_redo` 流程\n- 退出 cheat-shoot（不进 Phase 4）\n\n### Phase 4：state 更新\n\n```json\n{\n  \"shoots\": [\n    ...,\n    {\n      \"video_folder\": \"videos/2026-05-04_abc123_停止期待/\",\n      \"prediction_file\": \"predictions/2026-05-04_abc123_停止期待.md\",\n      \"scripts_path\": \"scripts/2026-05-04_abc123_停止期待.md\",\n      \"shot_at\": \"<ISO timestamp>\",\n      \"script_consistency\": \"consistent\" | \"modified\" | \"lost\",\n      \"script_diff_pct\": <0-100 int 或 null>,\n      \"v2_prediction_written\": <true/false>,\n      \"script_hash_at_shoot\": \"<sha256:12 of videos/<id>/script.md>\"\n    }\n  ]\n}\n```\n\n`v2_prediction_written: true` 表示 prediction 文件里现在有 `## 预测 v2` 段，cheat-retro 应读 v2 算偏差；`false` 表示沿用 v1。\n\n### Phase 5：输出 buffer 状态\n\n读完 state 后立即算 buffer + 颜色（按 [cadence-protocol.md](../../shared-references/cadence-protocol.md) 的派生规则）：\n\n```\n✅ 已登记拍摄：videos/2026-05-04_abc123_停止期待/\n   预测文件：predictions/2026-05-04_abc123_停止期待.md\n\n📦 当前 buffer：3 篇（🟢 绿色，正常）\n   按你的 cadence（隔日更）= 6 天 buffer，节奏稳定。\n\n下一步：拍其他候选 / 等下个发布日 / 不动\n```\n\n如果 buffer 颜色变了（如从绿到蓝）→ 高亮提醒：\n```\n📦 当前 buffer：6 篇（🔵 蓝色，**积压**）\n⚠️  建议暂停拍摄，全力发布存货 + 复盘。\n   按你的 cadence（日更）= 6 天预备，已超过健康上限。\n```\n\n## Key Rules\n\n1. **不写 prediction**——拍了 ≠ 发了。预测在 /cheat-predict 锁，拍只是事件\n2. **不动 video folder 内容**——script.md / draft-v0.md 都不改\n3. **必须先有 prediction**——否则违反盲预测（拍完看了画面再写预测 = 数据泄漏到判断）\n4. **buffer 计算实时**——每次 shoot / publish 后立刻重算，state.shoots 是真值\n5. **支持批量**：用户可以一天连说 \"拍了 X / 拍了 Y / 拍了 Z\" 三次连续登记\n\n## Refusals\n\n- 「拍了 X，但我从来没跑过 cheat-predict」 → 拒绝。v1 预测**必须拍前写**——拍完才写预测会被画面诱导事后修改。请先 /cheat-predict 写 v1 再来 /cheat-shoot。（v2 重判是另一回事——v1 已存在 + 拍后改稿才允许）\n- 「我没有 video folder，我直接拍的」 → 询问用户 → 帮他建 video folder + 提示下次走完整流程；登记时标 `ad_hoc: true`\n- 「我改稿了但你直接覆盖 v1 吧，别留 v2 段」 → 拒绝。v1 是档案，v2 才是当前判断——append 不覆盖。两段一起留是 rubric 学习的关键证据\n\n## Integration\n\n- 上游：`/cheat-predict` 写完 prediction → 用户拍摄 → `/cheat-shoot` 登记\n- 下游：`/cheat-publish` 发布时把对应项从 state.shoots 移除\n- `/cheat-status` 看板的 buffer 数字直接来自 `state.shoots.length`\n- `/cheat-recommend` 看 buffer 颜色调推荐策略\n- SessionStart hook 看 buffer 颜色决定报告第一行\n\n## state.shoots 数据结构\n\n```json\n{\n  \"shoots\": [\n    {\n      \"video_folder\": \"videos/2026-05-04_abc123_停止期待/\",\n      \"prediction_file\": \"predictions/2026-05-04_abc123_停止期待.md\",\n      \"shot_at\": \"2026-05-04T18:30:00+08:00\",\n      \"ad_hoc\": false  // true if user shot without going through full flow\n    }\n  ]\n}\n```\n\n按 `shot_at` 升序——最早拍的在前面。`/cheat-status` 显示最早一项的 days-since-shoot 警告（避免有视频拍了 30 天没发）。\n","frontmatter":{"name":"cheat-shoot","description":"登记一条视频已拍摄。**建 video folder + 询问实际拍摄稿是否与 scripts/<id>.md 一致 + buffer +1**。与 cheat-publish 配对：拍了进队列，发了出队列。触发词：\"拍了\"/\"拍了 X\"/\"shot\"/\"shot it\"/\"已拍 X\"/\"录完了\"。","argument-hint":"<scripts-path-or-id>","allowed-tools":"Bash(*), Read, Write, Edit, Glob","allowedTools":["Bash(*),","Read,","Write,","Edit,","Glob"]},"allowedTools":["Bash(*),","Read,","Write,","Edit,","Glob"],"isInternal":false,"tokens":3903,"sizeBytes":10788},{"name":"SKILL.md","path":"skills/cheat-status/SKILL.md","rawUrl":"https://raw.githubusercontent.com/XBuilderLAB/cheat-on-content/HEAD/skills/cheat-status/SKILL.md","title":"cheat-status","category":"anthropic-skill","format":"markdown","content":"---\nname: cheat-status\ndescription: cheat-on-content 的状态看板。显示当前模式 / rubric 版本 / 校准进度 / 待复盘 / pool 状态 / 是否该升级 SQLite / 是否该 bump rubric。**任何时候都可调，无副作用**。触发词：\"状态\"/\"看板\"/\"status\"/\"我现在该做什么\"/\"进度怎么样\"。\nallowed-tools: Bash(*), Read, Glob, Grep\n---\n\n# /cheat-status — 状态看板\n\n读 state file + 扫描用户项目 → 汇总当前进度 → 输出\"今天该做什么\"清单。\n\n## Overview\n\n```\n[用户：状态]\n  ↓\n[Phase 1: 读 .cheat-state.json + 扫文件系统]\n  ↓\n[Phase 2: 计算派生指标]\n  ↓\n[Phase 3: 检测建议触发器（升级 / bump / 清算）]\n  ↓\n[Phase 4: 输出看板]\n```\n\n## Constants\n\n- **SQLITE_UPGRADE_THRESHOLD = 30** — calibration_samples 达到 N 时建议升 SQLite\n- **CLEANUP_LINE_THRESHOLD = 600** — rubric_notes.md 行数超 N 时建议清算\n- **STALE_PREDICTION_DAYS = 30** — in_progress prediction 超 N 天未发布提示清理\n\n## Inputs\n\n| 来源 | 用途 |\n|---|---|\n| `.cheat-state.json` | 主要状态 |\n| `predictions/*.md` | 校准样本数 / pending retros |\n| `candidates.md` | 候选池规模 |\n| `rubric_notes.md` | 行数 / 当前版本 |\n| `.cheat-cache/usage.jsonl`（如有） | meta-logging 数据，用于\"距上次 bump 多少次预测\" |\n\n## Workflow\n\n### Phase 1: 读状态\n\n```python\nstate = read_json('.cheat-state.json')\nif not state:\n    return \"你还没初始化。请先跑 /cheat-init。\"\n\npredictions = glob('predictions/*.md')\ncandidates_count = parse_candidates_md_entries()\nrubric_lines = wc -l rubric_notes.md\n```\n\n### Phase 2: 派生指标\n\n| 指标 | 算法 |\n|---|---|\n| **Buffer 数** | `len(state.shoots)` |\n| **Buffer 颜色** | 按 [cadence-protocol.md](../../shared-references/cadence-protocol.md) 派生：`buffer_days = buffer_count × target_publish_cadence_days`，`<1 红 / 1-2 橙 / 3-5 绿 / >5 蓝`。如 `target_publish_cadence_days=null` → 颜色禁用 |\n| **Confidence 等级** | 按 [state-management.md confidence 表](../../shared-references/state-management.md) 派生：从 `calibration_samples` 整数派生 emoji + 标签 |\n| **最早一拍至今天数** | `now - state.shoots[0].shot_at`，用于警告\"拍了 N 天没发\" |\n| 校准样本数 | predictions 中含完整复盘段（实绩数据非空）的文件数 |\n| 待复盘 | state.pending_retros 中已过 RETRO_WINDOW_DAYS 的 |\n| 池大小 | candidates.md 中 tier!=skip 的 entry 数 |\n| 上次 bump 至今几次预测 | predictions 中 published_at > state.last_bump_at 的数量 |\n| 同向偏差队列 | state.consecutive_directional_errors |\n| in_progress 陈旧度 | now - state.in_progress_session.started_at（如有） |\n\n### Phase 3: 检测建议触发器\n\n按优先级（高→低）逐项检查：\n\n1. **Buffer 颜色 = 🔴 红** → 第一行高优先级警戒：\"buffer 已 0/1 篇，下个发布日可能断更——今天必须拍 ≥1 条。说'推荐选题'我只推 top 1 稳分（不推实验性）\"\n2. **Buffer 颜色 = 🔵 蓝** → 高优先级提示：\"buffer 已 N 篇积压。**暂停拍摄**，先发存货 + 复盘。说'已发布 ...'我帮你出队\"\n3. **state.shoots 中最早一项 shot_at > 14 天** → \"你有视频拍了 N 天还没发——议题时效流失风险，建议尽快发或弃稿\"\n4. **in_progress 陈旧** (>= STALE_PREDICTION_DAYS) → 高优先级提示\"清理或 publish\"\n5. **待复盘 ≥ 1** → 高优先级\"今天该复盘 X 篇\"\n6. **`pool_status=none` + `calibration_samples=0` + 距 init >24h** → \"🌱 你 init 完已经 N 天但还没拍——是因为没选题吗？跑 /cheat-seed 5 分钟拿 5 个候选 + 5 个 draft\" 高优先级\n7. **Claude 判断系统性偏差信号**（**不是死磕 ≥3 同向**） → 提示\"建议跑 /cheat-bump\"\n   - **默认参考**：连续 ≥3 次同向偏差\n   - **但 Claude 可以更早**：1 次极端偏差（≥10x）或 2 次同向 + 评论区强反向证据\n   - **也可以更晚**：3 次同向但每次幅度都 <25%（可能只是噪声）\n   - 提示时显式标注：\"本次是 [default-aligned] / [judgment-driven]\"\n8. **calibration_samples 跨入新 confidence 等级**（0→1, 2→3, 5→6, 10→11, 20→21）→ 提示\"🎉 confidence 升级：<旧等级> → <新等级>。bucket 中枢精度从 ±X% 提到 ±Y%\"。**仅作通知，无任何用户必须确认的操作**——所有 skill 都已经按 calibration_samples 自动调整\n9. **calibration_samples 跨过 5** → \"你的 rubric 形态可以第一次正式 bump 了。回顾 rubric_notes.md 看观察记录段是否有 ≥3 样本支持的 pattern → 跑 /cheat-bump\"\n10. **calibration_samples 跨过 10** → \"可以跑 /cheat-bump --bucket-only --scheme percentile 让 bucket 边界改用 percentile（永远自洽）\"\n11. **calibration_samples 跨过 SQLITE_UPGRADE_THRESHOLD** 且 data_layer=markdown → \"建议跑 tools/md-to-sqlite.py\"（planned — batch 3, not yet available）\n12. **rubric_notes.md 行数 > CLEANUP_LINE_THRESHOLD** → \"建议清算观察段（手动或下次 bump 触发）\"\n13. **calibration_samples ≥ 5 + pool_status=none** → \"可以开始建立选题池了\"\n14. **calibration_samples ≥ 15 + pool_status=none** → \"强烈建议建池：/cheat-trends 或手动建 candidates.md\"\n15. **state.hooks_installed=false** → \"你的 immutability 是君子协定，建议跑 /cheat-init 装 hook\"\n16. **state.last_bump_self_audited=true** → \"上次 bump 是自审。建议配置 mcp__llm-chat__chat 后下次 bump 走外部审\"\n17. **state.rubric_form_mismatch=true** → \"你的 content_form 不是 opinion-video，用了内置观点 rubric。前几篇预测会更不准，下次 bump 时建议自行调整权重适配你的形态\"\n18. **state.benchmark_status=pending** → \"🎯 你 init 时答应等下找对标账号但还没找。跑 /cheat-learn-from 导入 ≥3 条对标视频，工具就有 anchor 了\"\n19. **state.benchmark_status=imported + Claude 判断用户数据信号已超过 benchmark** → \"📊 你的真实数据已经成为主信号，benchmark 影响淡出\"\n   - **默认参考**：calibration_samples ≥ 10\n   - **但 Claude 可以更早**：N=5 但用户的 (打分, 实绩) 配对里出现 ≥3 条与 benchmark pattern 不一致的——说明你的账号已经走出对标的路径\n   - **也可以更晚**：N=15 但用户的样本都很相似，没足够多样性 → benchmark 仍有信号价值\n   - 提示是**通知不是 gate**——benchmark.md 永远保留作 sanity check，cheat-seed 仍可读\n\n### Phase 4: 输出看板\n\n```\n🎛️ cheat-on-content 状态（更新于 2026-05-04 15:00）\n\n内容形态：opinion-video / 时长 3-5min / cadence: 隔日更\n当前 rubric：v2 (上次 bump: 2026-04-22)\n校准样本：18 篇\nConfidence: 🟢 较高 (中枢 ±15%，rubric 形态稳定)\nBaseline: 4.2w 中位数\n\n📦 Buffer：3 篇（🟢 绿色）\n   按你的 cadence (隔日更)= 6 天 buffer，节奏稳定\n\n📊 进度条\n  [█████████████░░░░░] 18 / 30 → SQLite 升级建议门槛\n  [██████████░░░░░░░░] 18 / 10 → percentile 桶可用（已超过门槛）\n\n🎬 待办（按紧急度）\n  🚨 复盘 1 篇（已过 T+3d）\n     - predictions/2026-05-01_db063817_你已不在关系里.md（T+3d 到了）\n  ⚠️  同向偏差 3 次（high, high, high）→ 建议 /cheat-bump\n  💤 in-progress prediction 已陈旧 35 天\n     - predictions/2026-04-01_xxx.md → 是已发了忘登记？还是弃稿？\n\n🔥 候选池\n  - candidates.md: 27 条（tier1: 12, tier2: 9, tier3: 6）\n  - 距上次抓热点: 4 天 — 可以再跑 /cheat-trends\n\n📈 健康度\n  - rubric_notes.md: 412 行（健康，<600 警戒线）\n  - hooks_installed: ✅\n  - external audit configured: ❌ → 建议配 mcp__llm-chat__chat\n\n下一步建议（按推荐优先级）：\n1. /cheat-retro predictions/2026-05-01_db063817_你已不在关系里.md  ← 最紧急\n2. /cheat-bump  ← 同向偏差 3 次的处理\n3. 处理陈旧 in-progress（手动或回 \"清理 in-progress\"）\n\n完整的命令清单见主 SKILL.md。\n```\n\n输出风格：**直白、具体、可点击**。每个建议附确切的命令——用户应能 copy-paste 直接执行。\n\n## Key Rules\n\n1. **无副作用**。读多写零。任何状态修改是其他 skill 的事\n2. **不假装数据可用**。state file 字段缺失 → 显式标\"未知\"，不猜\n3. **建议带优先级**。10 个建议同时显示用户会麻木——按紧急度排\n4. **每个建议附命令**。不能只说\"该 bump 了\"——要给 `/cheat-bump --propose \"...\"` 的精确入口\n\n## Refusals\n\n- 「顺便帮我自动跑一下 retro」 → 拒绝。status 是只读，retro 是另一个动作（避免一次操作做两件事）\n- 「我不想看 rubric_notes 行数，太琐碎」 → 输出仍包含但折叠到底部\"健康度\"区——状态信息的存在让用户在出问题前可见\n\n## Integration\n\n- 上游：所有其他 skill 完成时更新 .cheat-state.json，status 是这些更新的可视化\n- 下游：每个建议都路由到具体子 skill\n- meta-logging hook（如启用） → 写 usage.jsonl，status 用它算\"距上次 X 多少次\"\n","frontmatter":{"name":"cheat-status","description":"cheat-on-content 的状态看板。显示当前模式 / rubric 版本 / 校准进度 / 待复盘 / pool 状态 / 是否该升级 SQLite / 是否该 bump rubric。**任何时候都可调，无副作用**。触发词：\"状态\"/\"看板\"/\"status\"/\"我现在该做什么\"/\"进度怎么样\"。","allowed-tools":"Bash(*), Read, Glob, Grep","allowedTools":["Bash(*),","Read,","Glob,","Grep"]},"allowedTools":["Bash(*),","Read,","Glob,","Grep"],"isInternal":false,"tokens":3309,"sizeBytes":9236},{"name":"SKILL.md","path":"skills/cheat-trends/SKILL.md","rawUrl":"https://raw.githubusercontent.com/XBuilderLAB/cheat-on-content/HEAD/skills/cheat-trends/SKILL.md","title":"cheat-trends","category":"anthropic-skill","format":"markdown","content":"---\nname: cheat-trends\ndescription: 从配置的热点源（HN / Reddit / YouTube trending / B 站热门 / 等）抓今天的热门话题，去重 + 粗打分 + 写入 candidates.md。**绝大部分人没有候选池——这是让\"我没素材\"问题在 onboarding 第二步就消失的钥匙**。触发词：\"抓热点\"/\"fetch trends\"/\"今天有什么可做的\"/\"trending now\"/\"找选题\"。\nargument-hint: \"[— sources: <comma-separated>] [— max-per: 20]\"\nallowed-tools: Bash(*), Read, Write, Edit, Glob, WebFetch, Skill\n---\n\n# /cheat-trends — 热点抓取\n\n多 adapter 模式：读各 `trend-sources` adapter 的输出 → 去重 → 粗打分 → 写入 `candidates.md`。\n\n## Overview\n\n```\n[用户：抓热点]\n  ↓\n[Phase 0: 读 .cheat-state.json 拿 enabled adapters]\n  ↓\n[Phase 1: 对每个 adapter 调 fetch]\n  ↓\n[Phase 2: normalize 到 candidate-schema]\n  ↓\n[Phase 3: 去重（vs candidates / predictions / trends-history）]\n  ↓\n[Phase 4: 对每个新 item 粗打分（调 cheat-score 内联逻辑）]\n  ↓\n[Phase 5: 排序 + 询问用户哪些加入 candidates.md]\n  ↓\n[Phase 6: 写入 + 更新 trends-history.jsonl 缓存]\n```\n\n## Constants\n\n- **TREND_SOURCES = [\"manual-paste\"]** — 启用的 adapter 列表（默认仅 manual-paste，最稳）\n- **LOOKBACK_HOURS = 24** — 抓最近 N 小时的热点\n- **MAX_PER_SOURCE = 20** — 每个 adapter 最多 N 条\n- **DEDUPE = true** — 去重开关\n- **AUTO_SCORE = true** — 抓回来后自动调 cheat-score 粗打分\n- **MIN_COMPOSITE_TO_SUGGEST = 6.0** — 低于此分的不推荐用户加入候选池（仍写入 trends-history 避免下次重复推）\n\n> 💡 调用时覆盖：`/cheat-trends — sources: manual-paste,aihot,weibo-hot — max-per: 10`\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `.cheat-state.json` | 默认 sources |\n| `adapters/trend-sources/<name>.md` | 各 adapter 的实现描述 |\n| `candidates.md` | 去重对照 |\n| `predictions/*.md` | 去重对照（已发的不再推） |\n| `.cheat-cache/trends-history.jsonl` | 历史抓取去重缓存 |\n\n## Workflow\n\n### Phase 0: 读启用的 adapters\n\n```python\n# 伪代码\nstate = read('.cheat-state.json')\nenabled_adapters = args.sources or state.get('enabled_trend_sources', ['manual-paste'])\n```\n\n如 enabled_adapters 为空 → 输出引导：\n\n```\n你目前没有启用任何热点源。\n\n最快配法：\n- 临时跑：/cheat-trends — sources: manual-paste,aihot\n- 永久启用：编辑 .cheat-state.json 的 enabled_trend_sources 数组\n\n可用 adapter（详见 adapters/trend-sources/）：\n- manual-paste（默认，永远能用）\n- aihot（AI 热点聚合，无需 key）\n- weibo-hot（微博热搜，无需 key）\n- zhihu-hot（知乎热榜，无需 key）\n- trendradar-mcp（TrendRadar MCP 服务，需配置）\n```\n\n### Phase 1-2: 对每个 adapter 调 fetch + normalize\n\n对每个 adapter，读其 `adapters/trend-sources/<name>.md` 中描述的 fetch 接口（实际是 Bash 调底层 Python / shell / WebFetch）：\n\n| Adapter | 实现机制 |\n|---|---|\n| `manual-paste` | 询问用户：\"粘贴你今天的候选 URL/标题列表（每行一条）\" → 解析每行，对 URL 做 WebFetch 拓展 snippet |\n| `aihot` | 读 adapters/trend-sources/aihot.md 描述的 fetch 接口 |\n| `weibo-hot` | 读 adapters/trend-sources/weibo-hot.md 描述的 fetch 接口 |\n| `zhihu-hot` | 读 adapters/trend-sources/zhihu-hot.md 描述的 fetch 接口 |\n| `trendradar-mcp` | 读 adapters/trend-sources/trendradar-mcp.md 描述的 fetch 接口 |\n\n每个 adapter 输出符合 [candidate-schema.md](../../shared-references/candidate-schema.md) 的 items。\n\n**优雅降级**：单 adapter 失败（API key 缺失 / 端点 503 / cookie 失效）→ skip 该 adapter，**不抛异常**，在汇总里说明：\n```\n✅ aihot: 拉到 18 条\n✅ weibo-hot: 拉到 15 条\n✅ zhihu-hot: 拉到 12 条\n⚠️  trendradar-mcp: 跳过（MCP 服务未配置——配置见 adapters/trend-sources/trendradar-mcp.md）\n```\n\n### Phase 3: 去重\n\n按 [candidate-schema.md](../../shared-references/candidate-schema.md) 的\"去重协议\"：\n\n1. 对每个 item 算 id（`sha256(source_type + normalized_title + url_path)[:12]`）\n2. 检查 `candidates.md` 已含此 id → 跳过\n3. 检查 `predictions/*.md` 已含此 id → 跳过\n4. 检查 `.cheat-cache/trends-history.jsonl` 已含此 id 且 `rejected_at != null` → 跳过\n\n去重统计写到汇总报告里。\n\n### Phase 4: 粗打分\n\n`AUTO_SCORE=true` 时，对每条新 item：\n1. 用 item 的 `snapshot_text` 作为输入\n2. 按当前 rubric 给 7 维打分（**不**调 `/cheat-score` 子 skill 走 IO；inline 复用打分逻辑）\n3. 算 composite\n4. 给一句 rationale\n\n**注意**：粗打分 ≠ 正式预测。预测必须基于最终稿（用户改过的），这里的打分只是\"是否值得展开写\"的粗筛。\n\n`AUTO_SCORE=false` 时，items 写入 candidates.md 时 composite=null，需要后续手动 `/cheat-score`。\n\n### Phase 5: 排序 + 询问\n\n按 composite 降序，过滤掉 composite < `MIN_COMPOSITE_TO_SUGGEST` 的：\n\n```\n🔥 抓热点完成。各源拉取统计：\n- manual-paste: 5 条（用户输入）\n- aihot: 18 条\n- weibo-hot: 15 条\n跳过 trendradar-mcp（MCP 服务未配置）\n\n去重后剩 27 条新 item。\n粗打分后 12 条 composite ≥ 6.0：\n\n| # | 标题 | source | composite | bucket | rationale |\n|---|---|---|---|---|---|\n| 1 | 为什么我们都讨厌主动联系朋友 | aihot | 8.4 | 30-100w | ER+QL 双 5，AB 普适 |\n| 2 | \"她不一样\"的一千种变体 | weibo-hot | 8.1 | 30-100w | MS 候选维度高 |\n| 3 | ...... |\n\n哪些加入 candidates.md？\n- 全部加 → 回 \"all\"\n- 选几个 → 回 \"1, 3, 5\"\n- 都不要 → 回 \"none\"（这些会被记到 trends-history 避免下次重复推）\n```\n\n### Phase 6: 落盘\n\n用户响应后：\n1. 选中的 items → 按 [candidate-schema.md](../../shared-references/candidate-schema.md) 的\"Markdown 表示\"格式追加到 `candidates.md`\n2. 所有抓回来的 items（不管选中与否）→ append 到 `.cheat-cache/trends-history.jsonl`：\n   ```jsonl\n   {\"id\": \"...\", \"title\": \"...\", \"source\": \"...\", \"snapshot_at\": \"...\", \"rejected_at\": null|\"<ISO>\", \"fetched_at\": \"<ISO>\"}\n   ```\n\n### Phase 7: 状态更新\n\n```json\n{\n  \"last_trends_run_at\": \"<ISO>\",\n  \"last_trends_added_count\": 5\n}\n```\n\n## Key Rules\n\n1. **不抛异常**。单 adapter 失败 → skip + 报告。多 adapter 全失败 → 报错\"所有源都失败\"，附排查指引\n2. **manual-paste 永远在**。即使其他所有 adapter 都坏了，manual-paste 模式必须能跑——它是兜底\n3. **去重是硬约束**。同 id 不重复推；用户拒绝过的 6 个月内不再推\n4. **粗打分要诚实标注**。在 candidates.md 的 entry 里标 `composite (rough, snapshot-based)`，避免与 prediction 的精打分混淆\n5. **不直接进 predictions/**。trends 只产 candidates，predict 是另一个动作\n\n## Refusals\n\n- 「直接抓抖音热门 feed，不用 cookie」 → 拒绝。抖音反爬极严，无 cookie 必失败；引导到 douyin-session adapter 配置文档\n- 「跳过去重，把所有抓到的都写进去」 → 拒绝。会污染候选池，下次 recommend 时排序失效\n- 「跳过粗打分，直接写 raw 标题」 → 允许（`AUTO_SCORE=false`），但提示用户后续需要 `/cheat-score` 才能进 recommend 池\n\n## Integration\n\n- 上游：用户配置 `.cheat-state.json` 的 `enabled_trend_sources` 数组\n- 下游：`/cheat-recommend` 直接读 `candidates.md` 排序——trends 写完，recommend 立刻看到\n- 与 `/cheat-init`：onboarding Q4 选\"没有候选池\"的用户被引导到这里\n- 与 `/cheat-status`：status 看板显示\"上次抓热点：X 天前 / 待清理候选池：Y 条\"\n\n## Adapter 实现注意事项\n\n每个 `adapters/trend-sources/<name>.md` 必须文档化以下：\n1. **依赖**：API key / cookie / package\n2. **fetch 接口**：调用方式（python script path / shell command / API endpoint）\n3. **输出 schema**：必须符合 candidate-schema.md\n4. **失败模式**：常见错误 + 优雅降级行为\n5. **稳定性等级**：★ 1-5 颗星\n\n详见 [adapters/HOWTO.md](../../adapters/HOWTO.md)（待批次 3 实现）。\n","frontmatter":{"name":"cheat-trends","description":"从配置的热点源（HN / Reddit / YouTube trending / B 站热门 / 等）抓今天的热门话题，去重 + 粗打分 + 写入 candidates.md。**绝大部分人没有候选池——这是让\"我没素材\"问题在 onboarding 第二步就消失的钥匙**。触发词：\"抓热点\"/\"fetch trends\"/\"今天有什么可做的\"/\"trending now\"/\"找选题\"。","argument-hint":"[— sources: <comma-separated>] [— max-per: 20]","allowed-tools":"Bash(*), Read, Write, Edit, Glob, WebFetch, Skill","allowedTools":["Bash(*),","Read,","Write,","Edit,","Glob,","WebFetch,","Skill"]},"allowedTools":["Bash(*),","Read,","Write,","Edit,","Glob,","WebFetch,","Skill"],"isInternal":false,"tokens":2895,"sizeBytes":8193}],"systemPromptSnippet":"<yakaai_skills repo=\"XBuilderLAB/cheat-on-content\">\n<!-- File: SKILL.md (Tokens: ~5351 | Category: anthropic-skill) -->\n---\nname: cheat-on-content\ndescription: 给所有想把\"感觉\"变成可校准预测的内容创作者。**方法论通用**——打分 → 盲预测 → T+3d 复盘 → 进化 rubric 的循环适用任何能被量化（播放 / 阅读 / 收听 / 点击）的内容。**rubric 是循环的内容，不是循环本身**——当前内置一份观点视频 rubric（参考博主 25+ 视频拟合），其他形态可借这套起步并 bump 调权重。**强烈建议导入对标账号**作为初始信号源（/cheat-learn-from）。触发词：\"初始化\"/\"打分这篇\"/\"启动预测\"/\"已发布\"/\"复盘\"/\"升级 rubric\"/\"推荐选题\"/\"抓热点\"/\"状态\"/\"找对标\"/\"learn from\"。**首次使用必须先跑 /cheat-init。**\nargument-hint: \"[draft-path] [— mode: cold-start|calibration]\"\nallowed-tools: Bash(*), Read, Write, Edit, Grep, Glob, Skill, mcp__llm-chat__chat\n---\n\n# 网红作弊器 / Cheat on Content\n\n> 🎯 **方法论通用，rubric 当前内置观点视频版**\n>\n> **方法论**（5 阶段闭环）：任何能被量化的内容形态都适用——视频 / 文章 / 播客 / Newsletter / 短文 thread。\n>\n> **当前内置 rubric**：观点类视频（评论 / 时评 / 论说 / 议题讨论 / 个人观点表达），7 个维度由参考博主 25+ 已发样本拟合而来。如果你做其他形态，需要：\n> - 自己写一份 rubric（参照 [starter-rubrics/opinion-video-zero.md](starter-rubrics/opinion-video-zero.md) 的格式）\n> - 或等内置扩展（长文 / 短文 / 播客 starter 在批次 3 路线图）\n>\n> 默认假设：**用户是从零开始的新人**（一条视频都没发过）——cold-start 期的预测会**简化**，只要 7 维打分 + 一句话 bet，不强求 bucket 数字（避免 false precision）。已有 5+ 篇数据的老手走 calibration 模式解锁完整 7 组件预测。\n\n把内容创作变成可校准预测循环：**打分 → 预测 → 发布 → 复盘 → 进化 rubric**。\n\n本文件是**总协议 + 路由器**。具体每个阶段的工作流在 `skills/cheat-*/SKILL.md` 各子 skill 里。\n\n## Codex compatibility\n\nCodex 没有 Claude Code 的 slash-command harness。安装到 Codex 后，按自然语言触发同一套路由即可：\n\n- `初始化 cheat-on-content` → 读取并执行 `skills/cheat-init/SKILL.md`\n- `打分这篇 scripts/foo.md` → 读取并执行 `skills/cheat-score/SKILL.md`\n- `启动预测 scripts/foo.md` → 读取并执行 `skills/cheat-predict/SKILL.md`\n- `拍了 ...` / `已发布 ...` / `复盘 ...` / `升级 rubric` / `状态` → 分别读取对应 `skills/cheat-*/SKILL.md`\n\n执行时遵循本文件的三条原则和路由表；不要依赖 `/cheat-*` 命令是否存在。Claude Code 专用 hook（`.claude/settings.json`）仍只在 Claude Code 里自动触发；Codex 中需要用户主动说 `状态` 查看 buffer、待复盘和候选池。\n\n---\n\n## 三条不可妥协原则\n\n任何一条被违反，整个校准循环退化为\"凭直觉的自我安慰\"。如果用户要求打破其中任何一条，**拒绝执行并说明原因**。\n\n1. **盲预测（Blind prediction）**：预测必须在看到任何实际数据**之前**写完。一旦写完，`## 预测` 段是 immutable——只能往 `## 复盘` 段追加。完整规范：[shared-references/blind-prediction-protocol.md](shared-references/blind-prediction-protocol.md)。**hooks/prediction-immutability.sh 在 harness 层强制执行**。\n\n2. **升级 = 全量重打（Bump = full re-score）**：rubric 升级时，校准池所有有实绩数据的样本必须用新公式重打分；新排序与实际表现排序若在 ≥4/5 样本上不一致，升级被拒；升级必须经跨模型独立审核。完整规范：[shared-references/bump-validation-protocol.md](shared-references/bump-validation-protocol.md)。\n\n3. **rubric 是工作台，不是博物馆**：被新数据推翻或被吸收为正式维度的观察，**删掉**。绝不留\"我曾经以为 X，但其实...\"的考古层。git history 才是档案。完整规范：[shared-references/observation-lifecycle.md](shared-references/observation-lifecycle.md)。\n\n---\n\n## 路由表（触发词 → 子 skill）\n\n| 用户说 | 调用 | 前置条件 |\n|---|---|---|\n| \"初始化\" / \"init\" / \"首次使用\" | `/cheat-init` | 无（这是入口） |\n| \"找对标\" / \"学这个账号\" / \"拆这几个对标视频\" / \"learn from\" / \"导入对标账号\" | `/cheat-learn-from` | 已 init；cold-start 强烈建议；后续可随时 --append / --replace |\n| \"找选题\" / \"我不知道拍什么\" / \"seed\" / \"找前 5 个选题\" | `/cheat-seed` | 已 init（cold-start 用户专用一次性种子动作） |\n| \"打分这篇 [path]\" / \"score this [path]\" | `/cheat-score` | rubric_notes.md 存在 |\n| \"启动预测\" / \"start prediction\" / \"给这稿子打分并预测\" | `/cheat-predict` | 已 init + 有最终稿 |\n| \"拍了 X\" / \"shot it\" / \"录完了\" | `/cheat-shoot` | 对应预测已写（buffer +1） |\n| \"已发布\" / \"I shipped it\" / \"发布链接是 X\" | `/cheat-publish` | 对应预测文件存在（buffer -1） |\n| \"复盘\" / \"retro this\" / \"T+3d 数据来了\" | `/cheat-retro` | 对应预测文件存在 + 已过 RETRO_WINDOW_DAYS |\n| \"构造受众画像\" / \"更新 persona\" / \"我的观众是谁\" / \"build persona\" | `/cheat-persona` | 已 init；有复盘评论数据（或 benchmark seed） |\n| \"升级 rubric\" / \"bump rubric\" / \"更新公式\" | `/cheat-bump` | 校准池 ≥ MIN_SAMPLES_FOR_BUMP |\n| \"推荐选题\" / \"next topic\" | `/cheat-recommend` | candidates.md 存在且非空 |\n| \"抓热点\" / \"fetch trends\" / \"今天有什么可做的\" | `/cheat-trends` | trend-sources adapter 已配置（日常补充候选池） |\n| \"状态\" / \"status\" / \"看板\" | `/cheat-status` | 任意时刻可调 |\n| \"迁移\" / \"升级 state\" / \"schema 版本不对\" / \"migrate\" | `/cheat-migrate` | 已 init；用户 git pull 拉了新版后；SessionStart hook 提示 schema mismatch 后 |\n\n> 拍 vs 发分两个动作：buffer 警戒系统需要明确知道\"拍了但没发\"vs\"已发\"两种状态。详见 [shared-references/cadence-protocol.md](shared-references/cadence-protocol.md)。\n\n**Mode detection**（首次接到非 init 触发词时执行）：\n1. 检查用户当前目录是否有 `.cheat-state.json` → 没有 → 强制路由到 `/cheat-init`\n2. 检查 `predictions/` 下有几个文件含完整 `## 复盘` 段填了真实数据 → 决定 `mode: cold-start | calibration`\n3. 把判定结果写回 `.cheat-state.json` 后再路由到目标 skill\n\n---\n\n## 必须拒绝的请求\n\n下列模式会**直接破坏**三条原则之一，无论用户怎么说，都拒绝执行：\n\n- 「帮我预测一下，但我先告诉你播放量你来反推就行」 → 违反原则 #1。改用 `_redo.md` 路径记为 reconstructed\n- 「能不能从 candidates 里直接挑 composite 最高的，不用解释理由」 → 拒绝。永远展示各维度评分和至少一个锚点对比\n- 「跳过校准池重打，直接换公式」 → 违反原则 #2\n- 「跳过外部模型审核，自己说了算」 → 仅当 `CROSS_MODEL_AUDIT=false` 显式设置且 state file 标记自审时允许\n- 「删掉这份预测，我想重写」 → 违反原则 #1。预测是 immutable。如有正当理由重做，写新文件 `_redo.md`，原版必须保留\n- 「凭你的感觉给我推荐选题，不用打分」 → 拒绝。本工具不做 gut-feel forecast——那是它诞生**之前**的状态\n- 「把 rubric_notes.md 里所有历史观察都留着，加个时间戳分组就行」 → 违反原则 #3。git history 是档案，不是 markdown 文件\n- 「能不能把 THRESHOLD 从 4/5 降到 3/5 让这次 bump 过」 → 拒绝。改 THRESHOLD 本身是元层级 bump，单独走流程\n\n详细的拒绝场景在每个子 skill 的 `Refusals` 段。\n\n---\n\n## 项目目录结构（用户 repo）\n\nskill 期望用户的项目布局如下。`/cheat-init` 会创建缺失项；**绝不在没确认的情况下覆盖**。\n\n```\n<user-content-project>/\n├── .gitignore                         # cheat-init 创建；挡住 .auth*/.cheat-secrets.json 等凭证\n├── rubric_notes.md                    # 评分规则的真实来源\n├── WORKFLOW.md                        # 5 阶段流程文档（cheat-init 创建）\n├── STATUS.md                          # 看板（cheat-status 维护）\n├── .cheat-state.json                  # 状态文件，子 skill 共享上下文\n├── .cheat-cache/                      # 不入版本控制\n│   ├── usage.jsonl                    # 钩子被动记录的使用日志\n│   └── trends-history.jsonl           # cheat-trends 的去重缓存\n├── .claude/\n│   └── settings.json                  # 含 prediction-immutability hook\n├── benchmark.md                       # 对标账号信息（cheat-learn-from 维护）\n├── audience.md                        # 受众画像（cheat-persona 派生；blind 硬禁读）\n├── scripts/                           # 拍前的所有草稿（cheat-seed 写或用户写）\n│   └── YYYY-MM-DD_<id>_<short>.md\n├── predictions/                       # immutable 预测日志（hook 保护）\n│   └── YYYY-MM-DD_<id>_<short>.md     # 与 scripts/ 同 id\n├── videos/                            # 拍后才建（cheat-shoot 创建）\n│   └── YYYY-MM-DD_<id>_<short>/\n│       ├── script.md                  # 用户提供的最终拍摄稿（cheat-shoot 时询问\"和 scripts/ 一致吗\"）\n│       └── report.md                  # T+3d 抓的数据 + 评论（cheat-retro 写）\n├── samples/                           # 对标账号视频 / 转录（cheat-learn-from 创建）\n│   └── <账号名>/<video-id>/{source.mp4 (可选), transcript.md, meta.md}\n├── candidates.md                      # 选题池（可选）\n└── content.db                         # 可选 SQLite，校准池规模化后启用\n```\n\n---\n\n## 文件清单\n\n### 本 skill 包\n\n```\ncheat-on-content/\n├── SKILL.md                           # 本文件（总协议 + 路由）\n├── README.md                          # 营销门面\n├── skills/                            # 子 skill 集\n│   ├── cheat-init/SKILL.md            # ✅ 入口：onboarding 与脚手架\n│   ├── cheat-learn-from/SKILL.md      # ✅ 对标账号导入（拆 pattern + 派生 base rubric 信号）\n│   ├── cheat-seed/SKILL.md            # ✅ Cold-start 选题启动器（brainstorm + 可选 draft）\n│   ├── cheat-score/SKILL.md           # ✅ 单稿打分（不写文件）\n│   ├── cheat-predict/SKILL.md         # ✅ 盲预测 + immutable 日志\n│   ├── cheat-shoot/SKILL.md           # ✅ 登记拍摄（buffer +1）\n│   ├── cheat-publish/SKILL.md         # ✅ 发布元数据登记（buffer -1）\n│   ├── cheat-retro/SKILL.md           # ✅ 数据回收 + 复盘\n│   ├── cheat-persona/SKILL.md         # ✅ 受众画像派生（从复盘评论聚类）\n│   ├── cheat-bump/SKILL.md            # ✅ rubric 升级（含跨模型审）\n│   ├── cheat-recommend/SKILL.md       # ✅ 候选池排序推荐（按 buffer 颜色 + 1 稳 + 1 实验）\n│   ├── cheat-trends/SKILL.md          # ✅ 热点抓取（日常补充候选池，多 adapter）\n│   ├── cheat-status/SKILL.md          # ✅ 状态看板（含 buffer 警戒）\n│   ├── cheat-migrate/SKILL.md         # ✅ schema 升级（老用户 git pull 后用）\n│   └── cheat-score-blind/SKILL.md     # ✅ Channel B 隔离打分 sub-agent（仅 Task tool 调用）\n├── migrations/                        # schema 演进单一来源\n│   ├── registry.md                    # ✅ LATEST_SCHEMA + 版本链表\n│   └── <from>-to-<to>.md              # ✅ 每步迁移的 WHAT/WHY/HOW/Manual fallback\n├── shared-references/                 # 跨 skill 共享协议\n│   ├── blind-prediction-protocol.md   # ✅ 原则 #1\n│   ├── bump-validation-protocol.md    # ✅ 原则 #2\n│   ├── observation-lifecycle.md       # ✅ 原则 #3\n│   ├── prediction-anatomy.md          # ✅ 一份合格预测的 7 个组件\n│   ├── candidate-schema.md            # ✅ 候选项统一 schema\n│   ├── cadence-protocol.md            # ✅ 节奏协议（buffer 警戒 + 选题策略）\n│   ├── state-management.md            # ✅ .cheat-state.json 读写约定\n│   └── migration-protocol.md          # ✅ schema 演进哲学 + maintainer checklist\n├── starter-rubrics/                   # 各内容形态的先验 rubric\n│   ├── opinion-video.md               # ✅ 观点视频（中文，已校准 25+ 样本）\n│   ├── opinion-video-zero.md          # ✅ v0 等权占位（cold-start）\n│   ├── long-form-essay.md             # ⬜ 公众号 / Substack\n│   └── short-form-text.md             # ⬜ X thread / 微博长文\n├── templates/                         # skill 写进用户 repo 的文件骨架\n│   ├── gitignore.template             # ✅ 用户项目 .gitignore（护凭证，保留 predictions/ 入库）\n│   ├── rubric_notes.template.md       # ✅\n│   ├── prediction.template.md         # ✅ 统一版（所有阶段，含 confidence header）\n│   ├── retro.template.md              # ✅\n│   ├── candidates.template.md         # ✅\n│   ├── candidates.template.json       # ✅\n│   ├── script_patterns.template.md    # ✅ 写作 pattern 沉淀（含 benchmark 借鉴段说明）\n│   ├── benchmark.template.md          # ✅ 对标账号 reference\n│   ├── audience.template.md           # ✅ 受众画像骨架\n│   ├── workflow.template.md           # ✅\n│   ├── status.template.md             # ✅\n│   └── content.db.schema.sql          # ✅\n├── hooks/                             # harness 强制层\n│   ├── prediction-immutability.json   # ✅ 阻塞型钩子（拦预测段编辑）\n│   ├── prediction-immutability.sh     # ✅ 拦截脚本\n│   ├── session-start.json             # ✅ SessionStart 自动报告 hook\n│   ├── session-start.sh               # ✅ 状态报告渲染脚本\n│   ├── meta-logging.json              # ✅ 被动记录配置\n│   └── log-event.sh                   # ✅ meta-logging 脚本\n├── tools/                             # 独立 CLI 脚本\n│   ├── score-curve.py                 # ⬜ 预测精度收敛曲线\n│   ├── md-to-sqlite.py                # ⬜ markdown → content.db 升级（批次 3）\n│   └── validate-bump.py               # ⬜ 校准池全量重打（批次 3）\n├── adapters/                          # 数据源适配\n│   ├── perf-data/                     # 复盘数据源（含 douyin-session）\n│   ├── candidate-pool/                # 候选池数据源\n│   ├── trend-sources/                 # 热点抓取源\n│   └── script-extraction/             # 视频/音频转 script（含 whisper for cheat-learn-from）\n└── examples/\n    ├── reference-implementation/      # 视频分析脱敏快照（待）\n    └── script_patterns.example.md     # script_patterns 全填示例（参考用，不复制）\n```\n\n✅ = 当前批次（v1 骨架）已完成 / ⬜ = 后续批次\n\n---\n\n## Tone & voice\n\n写面向用户的文案（commit message / 复盘小结等）时，匹配项目的 **直白克制（reflective-irreverent）** voice：\n\n- 直接说出失败：「composite 8.47 但实际只有 16.8w——rubric 高估了 SR」\n- **不要**用模糊措辞软化：「这或许可能在某种程度上暗示...」——别这么写\n- Cluely 风格的反叛 hook 只在 README 出现——**不要写进** `rubric_notes.md` 或预测日志\n\n---\n\n## 给开发者：扩展本 skill\n\n- 新增内容形态 → 加 `starter-rubrics/<form>.md`\n- 新增热点抓取源 → 加 `adapters/trend-sources/<name>.md`，符合 [candidate-schema.md](shared-references/candidate-schema.md) 输出契约\n- 修改原则 → 改 `shared-references/<protocol>.md`，所有引用它的 skill 自动跟进\n- 修改路由 → 改本文件的\"路由表\"段\n- 子 skill 内部细节 → 直接改对应 `skills/cheat-*/SKILL.md`\n\n完整开发指南见 README.md。\n\n<!-- File: skills/cheat-bump/SKILL.md (Tokens: ~8002 | Category: anthropic-skill) -->\n---\nname: cheat-bump\ndescription: 提议并执行 rubric 或 bucket 升级。两种模式：**完整 rubric bump**（最高风险动作，5 步强制 + 跨模型审核）和 **--bucket-only 轻量重校**（只换 bucket 边界，不动 rubric 公式）。**Phase 2 强制走 cheat-score-blind sub-agent 给校准池重打分**——不接受 self-scored fallback。触发词：\"升级 rubric\"/\"bump rubric\"/\"更新公式\"/\"我想加一个维度\"/\"调整权重\"/\"重校桶\"/\"recalibrate bucket\"。\nargument-hint: --propose \"<...>\" | --bucket-only [--scheme ratio|absolute|percentile]\nallowed-tools: Bash(*), Read, Write, Edit, Glob, Grep, Skill, Task, mcp__llm-chat__chat\n---\n\n# /cheat-bump — Rubric / Bucket 升级\n\n两种模式：\n\n| 模式 | 触发 | 做什么 | 验证强度 |\n|---|---|---|---|\n| **完整 rubric bump** | `--propose \"<新公式>\"` | 改公式 / 维度 / 权重 | 5 步 + 跨模型审核（强制） |\n| **bucket-only 重校** | `--bucket-only` | 只重新派生 bucket 边界 | 数据自动派生，无审核 |\n\n完整 rubric bump 严格遵守 [shared-references/bump-validation-protocol.md](../../shared-references/bump-validation-protocol.md) 的 5 步。bucket-only 走轻量路径——见下方 Phase B。\n\n## Overview\n\n```\n入口：用户触发 /cheat-bump\n  ↓\n[Phase A0: 检测调用模式]\n  ↓\n  ├─ --bucket-only  →  [Phase B: 轻量 bucket 重校]\n  └─ --propose      →  [Phase 0~6: 完整 rubric bump]\n```\n\n## Phase A0: 调用模式分流（先做）\n\n读用户参数：\n- 含 `--bucket-only` → 走 **Phase B**（轻量重校）\n- 含 `--propose \"<...>\"` → 走 **Phase 0~8**（完整 rubric bump）\n- 都没有 → 询问用户：\"你想做什么？1) 调 rubric 公式 / 加减维度 → --propose；2) 只重新派生 bucket 边界 → --bucket-only\"\n\n如果用户说\"我觉得 ER 太低了想调\"→ 是 `--propose` 路径。\n如果用户说\"我账号长大了，bucket 不准了\"→ 是 `--bucket-only` 路径。\n**两条路径不能混调**——一次操作只做一种事。\n\n---\n\n## 完整 rubric bump 流程\n\n```\n[用户：升级 rubric --propose \"ER×1.5→2.0，砍 NA，加 MS\"]\n  ↓\n[Phase 0: 前置门槛检查]\n  ↓\n[Phase 1: 写出新公式完整方程]\n  ↓\n[Phase 2: 校准池全量重打分]\n  ↓\n[Phase 3: 计算排序一致性]\n  ↓\n[Phase 4: 跨模型独立审核（强制）]\n  ↓\n[Phase 5: 落地 + cleanup pass]\n  ↓\n[Phase 6: 更新所有校准样本的 prediction 文件底部追加 Re-scored 行]\n```\n\n## Constants\n\n- **READINESS_HEURISTIC** —\n  - **默认参考**：校准池 ≥ 5 样本 + 至少 1 个跨样本观察有 ≥3 样本支持\n  - **但 Claude 可以提议 bump**（即使样本少）如果观察信号特别强：\n    - N=3 但出现完全推翻当前 rubric 假设的强反例（composite 8.5 vs 实绩 5w 这种 ≥3x 偏差）\n    - 1 篇出现单点但极强的现象（如评论区出现 ≥2000 赞的单一模因）\n  - **Claude 也可以拒绝 bump**（即使样本足）如果证据弱：\n    - N=10 但观察都是低置信度的零碎 pattern，无清晰方向\n    - 用户复盘时大量\"随便看了下\"的非严肃判断\n  - **写在 prediction header 或 cheat-bump 输出时必说明**：本次提议是 default-aligned 还是 judgment-driven，给用户审视依据\n- **THRESHOLD = 0.8** — 新排序与实绩排序一致性阈值（4/5）。这条**写死**——bump 验证的统计刚性\n- **CROSS_MODEL_AUDIT = true** — 调外部 LLM 独立审核。false 仅用于离线\n- **REQUIRE_CONFIRM = true** — 落地前要求用户明确\"yes, bump\"\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `--propose` 文本 | 用户参数；缺失则询问 |\n| `rubric_notes.md` | 用户项目根 |\n| `predictions/*.md` 全量 | 校准池数据 |\n| `.cheat-state.json` | 状态 |\n\n## Workflow\n\n### Phase 0: 前置门槛检查\n\n按 [bump-validation-protocol.md](../../shared-references/bump-validation-protocol.md) 的\"何时禁止\"段，逐项检查：\n\n| 检查 | 失败处理 |\n|---|---|\n| 校准池总样本数 vs 观察强度 | **Claude 判断**——按 READINESS_HEURISTIC：默认 ≥5 样本但允许特例（强反例 / 强模因）。如不满足默认，Claude 必须**显式说明**为什么仍然提议 bump（\"虽然只 N=3 样本，但 X 这条出现 composite Y vs 实绩 Z，这是 W 倍偏差\"），让用户审视 |\n| 上次 bump 距今的新校准数 vs 观察成熟度 | **Claude 判断**——默认建议 ≥3 篇新样本，但如果连续 3 篇都强证据指向同一方向 → 不必再等 |\n| `in_progress_session == null` | 拒绝：\"你有 in-progress 预测未完成。先走完那条流程或清掉 state\" |\n| 触发条件成立（系统性偏差 / 跨样本新观察 / 新维度证据足） | 警告但不阻塞——询问用户为什么现在 bump |\n\n通过 → 进入 Phase 1。\n\n### Phase 1: 写出新公式完整方程\n\n**不能只接受用户的简短描述**。把它展开为完整方程：\n\n```\n当前：v2  composite = (ER×1.5 + SR×1.5 + HP×1.5 + QL + NA + AB + SAT) / 8.5 × 2.0\n提议：v2.1  composite = (ER×2.0 + HP×1.5 + MS×1.5 + QL + SR + TS + SAT) / 9.0 × 2.0\n\n变化总结：\n- ER ×1.5 → ×2.0（升）\n- SR ×1.5 → ×1.0（降）\n- 新增 MS ×1.5（Memetic Shareability）\n- 新增 TS ×1.0（Topic Shareability）\n- 删除 NA（与 HP 重叠）\n- 删除 AB（被 TS 替代）\n- 归一化常数 8.5 → 9.0\n- 公式总维度数：7 → 7（净变化 0）\n```\n\n如果用户的提议含糊（如\"ER 权重提一点\"）→ 询问具体数值，**禁止自己猜**。\n\n### Phase 2: 校准池全量重打分（**强制走 blind sub-agent**）\n\nGlob `predictions/*.md` 中所有有完整复盘段的文件 → 校准池。\n\n**bump 是工具最高风险动作——所有重打必须走 [cheat-score-blind](../cheat-score-blind/SKILL.md) sub-agent**。inline 重打 = 主 Claude 已经看过实绩，rank 一致性变成 overfit 而非真信号。\n\n#### 强制约束\n\n- **不接受 self-scored fallback**——`/cheat-predict` 有 `--skip-blind` flag，但 `/cheat-bump` **没有**。如果 Task tool 不可用 → **abort bump**，向用户报告\"先解决 Task tool 再 bump\"\n- **不接受\"我只重算 composite 不重打 dim\"** —— 即使新公式只调权重不加维度，每条 prediction 的所有 dim 都要由 sub-agent 重新审 script。理由：旧 dim 分本身可能是污染的；权重变了不能保证旧 dim 还成立\n\n#### 对每篇 prediction：\n\n1. 解析 prediction 文件拿到对应 `scripts/<id>.md` 路径（从 `Script Path` header 字段）\n2. 校验 script 文件存在 + hash 跟 header `Script Hash` 一致；不一致 → 警告（script 改过了）但仍 spawn sub-agent\n3. **通过 Task tool spawn cheat-score-blind sub-agent**：\n   ```\n   Spawn cheat-score-blind sub-agent.\n\n   Input:\n     script_path: <prediction header 的 Script Path>\n     rubric_notes_path: rubric_notes.md\n     sidecar_path: .cheat-cache/bump-rescores/<prediction-id>.json\n\n   Task: 按 rubric_notes 当前公式（已是新版 vN+1）给 script 打分。\n   返回严格 JSON。写 sidecar 文件用于 bump 主流程批量读取。\n\n   不要读 state file / predictions/ / videos/ 任何其他文件。\n   不要询问用户 —— 你没有用户。\n   不要读这份 prediction 文件本身 —— 你只看 script + rubric。\n   ```\n4. 等 sub-agent 完成 → 读 sidecar JSON → 主流程用新公式算 composite\n5. 写\"重打表\"到 `.cheat-cache/bump-rescores.json`（汇总）。**每条 entry 标 `blind: true`** —— bump phase 5 cleanup 时把这个字段连同新分数写到 prediction 文件的 `Re-scored under v<N+1>` 行\n\n#### 还污染没污染的诚实标注\n\n即使走 sub-agent，**仍有两类残余 contamination 要在 bump report 里诚实标注**：\n\n| 类型 | 来源 | 标注字段 |\n|---|---|---|\n| 模型 prior contamination | sub-agent 仍是 Claude，RLHF 共享 | `model_prior_warning: true`（默认 true，不可关） |\n| 用户自己 rubric design bias | rubric_notes.md 是用户写的，自然 fit 自己内容 | `rubric_self_designed: true`（默认 true，不可关） |\n\n这两条提示用户 channel C（跨模型 audit）的不可省。bump 报告末尾必印：\"上面的 rank 一致性是 channel A 内的一致性。**最终决策必须等 channel C audit 通过**。\"\n\n#### 失败模式\n\n| 症状 | 处理 |\n|---|---|\n| 某条 prediction 的 script 文件不见了 | sub-agent skip 该条，主流程汇总报告\"N 条因 script 缺失被排除\"。如剩余有效池 < MIN_SAMPLES → abort bump |\n| sub-agent 返回 `refusal != null` | 重发 Task 最多 3 次；仍败 → 该条标 `rescore_failed: true` 排除出校准池 |\n| Task tool 整个不可用 | abort bump，提示用户\"Task tool 是 bump 的硬依赖。如真的离线环境，跑 `/cheat-bump --bucket-only` 走轻量分支\" |\n| sub-agent 输出含 contamination_signal | 标 `suspicious: true` 但不排除——bump report 末尾列这些可疑条目让用户审 |\n\n### Phase 3: 计算排序一致性\n\n```\n每个样本：\n  new_composite_rank: 用新公式排序的 rank\n  actual_plays_rank: 用实际播放排序的 rank\n  delta: |new_rank - actual_rank|\n\n输出对照表：\n| 样本 | composite (v2) | composite (v2.1) | rank (new) | actual | rank (actual) | delta |\n|---|---|---|---|---|---|---|\n| 仓鼠 | 9.41 | 9.55 | 1 | 124.8w | 1 | 0 |\n| 停止期待 | 8.24 | 9.11 | 2 | 71.1w | 2 | 0 |\n| 老板废话 | 7.65 | 8.11 | 4 | 39.6w | 3 | 1 |\n| 求职悖论 | 8.47 | 7.56 | 5 | 16.8w | 4 | 1 |\n| 谁问你了 | 8.24 | 7.00 | 6 | 11.7w | 5 | 1 |\n\n排序一致性：4/5 在 |delta| ≤ 1\nPairwise no-regression：旧公式做对的所有 pair 在新公式下未颠倒 ✓\n```\n\n判定：\n- 排序一致性 < THRESHOLD（默认 0.8） → **本地拒绝**，转 Phase 4 之前明确报告失败\n- pairwise 出现回归 → **本地拒绝**\n\n`THRESHOLD` 写死在协议里——不允许临时调低（那本身是另一个需要 bump 的元决策）。\n\n### Phase 4: 跨模型独立审核（**强制**，除非 escape hatch）\n\n`CROSS_MODEL_AUDIT=true`（默认）：\n\n调用 `mcp__llm-chat__chat`：\n\n```\nprompt:\n你是一个独立审稿人。下面是一个内容创作者准备升级的 rubric 公式。\n请独立判定两件事：\n1. 排序一致性：新公式给样本的排序与实际表现排序，是否真的在 ≥80% 样本上一致？\n2. 解释力：新公式相比旧公式，是否更好地解释了校准池的实绩分布？\n\n数据：\n旧公式：(ER×1.5 + SR×1.5 + HP×1.5 + QL + NA + AB + SAT) / 8.5 × 2.0\n新公式：(ER×2.0 + HP×1.5 + MS×1.5 + QL + SR + TS + SAT) / 9.0 × 2.0\n\n校准池：\n[Phase 2 重打表的完整 JSON]\n\n排序对照：\n[Phase 3 表格的完整 JSON]\n\n输出格式：\n- 判定：PASS 或 REJECT\n- 理由：≥100 字\n- 关键风险：[如有，列出新公式的潜在问题]\n```\n\n收到外部 LLM 回复 → 解析判定。\n\n判定逻辑：\n- 本地 PASS + 外部 PASS → 通过，进入 Phase 5\n- 本地 PASS + 外部 REJECT → **视为 REJECT**。冲突意味着至少一方解读不稳定\n- 本地 REJECT → 已在 Phase 3 终止\n- mcp__llm-chat__chat 不可用 → 优雅降级到 `CROSS_MODEL_AUDIT=false`，state file 标 `last_bump_self_audited: true`\n\n`CROSS_MODEL_AUDIT=false`：\n- 仅依赖本地判定\n- state file 持续标记，cheat-status 持续提示用户\"这次 bump 是自审，建议配置 mcp__llm-chat__chat\"\n\n### Phase 5: 落地 + cleanup pass\n\n通过审核后，**REQUIRE_CONFIRM=true** → 询问用户：\"新公式 PASS 本地与外部审核。最后确认：执行 bump 落地？这会修改 rubric_notes.md + rubric-memo.md 并删除若干已被吸收的观察。回答 'yes, bump' 才执行。\"\n\n用户确认后：\n\n#### 5a. 更新 `rubric_notes.md`（**只放通用语言，不含视频名 / 实绩**）\n\n- 顶部 metadata 更新：\n  - `**当前版本**: vN+1`\n  - `**Last bumped at**: <ISO 8601>`\n  - `**Upgrade memos**: 见 [rubric-memo.md](rubric-memo.md)`（指针，不复制 Memo 内容）\n- 版本速查表加一行（只含版本号 + 公式签名，**不含**证据样本）\n- 更新\"当前评分维度\"段（删 NA / AB，加 MS / TS）\n- **派生证据段** 如新维度需要锚点解释 → **用通用语言**：\n  - ✅ 允许：「派生证据：高抽象密度样本 → CC=1 → 低 reach」\n  - ❌ 禁止：「派生证据：「停止期待」CC=1 → 实绩 13.7w」（视频名 + 实绩 数字）\n  - 命中违禁 pattern → 把该段抽到 rubric-memo.md 的\"派生证据\"子段，原位用通用语言替代\n\n#### 5b. 写 Memo 到 `rubric-memo.md`（**append 模式，不覆盖历史**）\n\n按 [bump-validation-protocol.md](../../shared-references/bump-validation-protocol.md) Step 5 + [templates/rubric-memo.template.md](../../templates/rubric-memo.template.md) 格式 append 一段 Memo 到文件末尾：\n\n- 触发观察（含真实观察 ID）\n- 证据数据（**校准池重打表 + 排序对照，含真实视频名 + 实绩**）\n- 派生证据（**含真实样本名 + 实绩**）\n- 诊断\n- 新公式\n- 跨模型审核结论引用（含模型名 + 判定 + 理由摘录）\n- 已知局限\n\n**绝不**覆盖 rubric-memo.md 已有内容——bump memo 按时间顺序累积。\n\n#### 5c. cleanup pass（按 [observation-lifecycle.md](../../shared-references/observation-lifecycle.md) 的\"cleanup pass 强制时机\"）\n\n在 `rubric_notes.md` 内执行（**不**动 rubric-memo.md）：\n\n- 已被吸收为新维度的观察 → 删（如观察 E 被吸收为 MS → 删观察 E）\n- 被新数据推翻的观察 → 删\n- 仍未解决的观察 → 迁移到新版本\"待验证假设\"段\n- 已被验证的\"规律\"→ 移到\"规律沉淀区\"\n\n#### 5d. 整理 + 自检\n\n- 重新读 `rubric_notes.md` 全文，确保读者能在 60 秒内理解当下规则——超出 600 行触发额外清算\n- **自检 leak guard**：对 `rubric_notes.md` 跑 `grep -E '\\\\d+\\\\s*[wWmMkK万]|播放|实绩|实际'` → 如有命中 → **abort bump + 回滚**，提示用户\"rubric_notes.md 写入了违禁内容（实绩 / 播放数）\"。这些内容应在 rubric-memo.md，不在 rubric_notes.md\n\n### Phase 6: 校准样本批量更新\n\n对每个校准样本的 prediction 文件，**底部追加**（不动预测段、不动复盘段）：\n\n```markdown\n\n---\n**Re-scored under v2.1 on 2026-05-04**: composite=8.24 → 9.11 (blind: true)\n（rubric bump 时全量重算，由 cheat-score-blind sub-agent 独立打分；详见 rubric-memo.md 的 v2 → v2.1 升级 Memo）\n```\n\n`blind: true` 字段**必填**——告诉未来读这条记录的人\"这是 channel B 隔离打分，不是主 Claude 自评\"。如果某条 prediction 在 Phase 2 因 sub-agent 失败被排除 → 不会有 Re-scored 行（保持原样）。\n\n用 Edit 工具，匹配每个文件的最末尾。\n\n### Phase 7: 更新 state file\n\n```json\n{\n  \"rubric_version\": \"v2.1\",\n  \"last_bump_at\": \"<ISO timestamp>\",\n  \"last_bump_self_audited\": false,\n  \"consecutive_directional_errors\": [],\n  \"calibration_samples_at_last_bump\": <current value>\n}\n```\n\n清空 `consecutive_directional_errors`——新 rubric 重新计数。\n\n### Phase 8: 控制台报告\n\n```\n✅ Rubric 已升级 v2 → v2.1\n\n变化：\n- ER ×1.5 → ×2.0\n- SR ×1.5 → ×1.0\n- 新增 MS / TS\n- 删除 NA / AB\n\n校准池重打：5/5 通过排序检查（4/5 一致 + 0 pairwise 回归）\n跨模型审核：✅ PASS\nCleanup pass：删除观察 D 和 E（已吸收为 QL 重定义和 MS 维度）\n\n下一篇预测起按 v2.1 公式打分。\n所有历史预测文件已追加 Re-scored 标记。\n```\n\n---\n\n## Phase B：bucket-only 重校（轻量分支）\n\n`/cheat-bump --bucket-only [--scheme ratio|absolute|percentile]`\n\n**与完整 bump 的本质区别**：bucket 边界不是规则的一部分，是数据派生量。重新派生它**不需要跨模型审核**——派生算法是确定性的，没有\"判断\"成分。\n\n### B1: 选择算法（按可用样本数自动派生，**state 不存 scheme**）\n\n| 算法 | 适用 | 边界派生方式 |\n|---|---|---|\n| `ratio`（默认 N=1-4） | 小样本 | 上一篇 / 最近 3 篇中位数 × {0.3 / 1 / 3 / 10 / 30} |\n| `absolute`（默认 N=5-9）| 中等样本 | 校准池中位数 × {0.3 / 1 / 3 / 10 / 30}，固定边界 |\n| `percentile`（默认 N≥10）| 大样本 | 校准池实绩 percentile {30 / 60 / 85 / 95 / 100} |\n\n`--scheme` 参数允许用户**显式覆盖默认**：\n- `--scheme ratio` 强制用 ratio（即使 N≥5）\n- `--scheme absolute` 强制用 absolute\n- `--scheme percentile` 强制用 percentile（要求 N≥3，否则报错）\n\n未指定 `--scheme` → 按上表自动派生。\n\n> 旧设计有 `bucket_scheme` state 字段——v1.1 删了。所有 skill 实时按 calibration_samples 派生算法，不需要持久化\"当前用哪个\"。这避免了\"切换 scheme 后忘了同步\"的状态不一致问题。\n\n### B2: 派生新边界\n\n读 `predictions/*.md` 中所有有 `actual_plays` 的样本。\n\n**ratio 模式**：\n```\nbaseline = median(最近 3 篇 actual_plays)\nbuckets = {\n  \"退步\": (-inf, baseline * 0.3),\n  \"持平\": (baseline * 0.3, baseline * 1),\n  \"命中\": (baseline * 1, baseline * 3),\n  \"小爆\": (baseline * 3, baseline * 10),\n  \"大爆\": (baseline * 10, +inf),\n}\n```\n\n**absolute 模式**：\n```\nbaseline = median(全部校准池 actual_plays)\nbuckets = {\n  \"底部\": (-inf, baseline * 0.3),\n  \"基础盘\": (baseline * 0.3, baseline * 1),\n  \"命中\": (baseline * 1, baseline * 3),\n  \"爆款\": (baseline * 3, baseline * 10),\n  \"现象级\": (baseline * 10, +inf),\n}\n```\n\n**percentile 模式**：\n```\nsorted_plays = sorted(全部校准池 actual_plays)\nbuckets = {\n  \"底部\":   ≤ p30,\n  \"基础盘\": p30 - p60,\n  \"命中\":   p60 - p85,\n  \"小爆\":   p85 - p95,\n  \"大爆\":   ≥ p95,\n}\n```\n\n### B3: 报告变化 + 用户确认\n\n```\n当前 bucket scheme: ratio\nproposed scheme: absolute\nbaseline: 4.2w 中位数（基于 5 篇校准样本）\n\n新边界：\n- 底部:   < 1.3w\n- 基础盘: 1.3w - 4.2w\n- 命中:   4.2w - 12.6w\n- 爆款:   12.6w - 42w\n- 现象级: > 42w\n\n派生说明：\n- 5 篇实绩：1.5w / 3.8w / 4.2w / 5.6w / 18w\n- 中位数 4.2w，新桶按 ×{0.3, 1, 3, 10} 派生\n\n确认应用？(yes / no)\n```\n\n### B4: 落地\n\n用户确认后：\n1. 编辑 `rubric_notes.md` 的 \"Bucket 方案\" 段，替换为新表\n2. 更新 `.cheat-state.json` 的 `baseline_plays` 字段（bucket scheme 不持久化——下次 cheat-predict 实时派生）\n3. 在 `rubric_notes.md` 的 bucket 段顶部追加一行变更记录：`v2 buckets recalibrated on YYYY-MM-DD: scheme=absolute, baseline=4.2w (基于 N=10 个样本)`\n4. **不**修改任何 prediction 文件——历史预测的 bucket 标签保持原样（在该样本写入时的方案下做出的判断）\n\n### B5: 对未来预测的影响\n\n下一次 `/cheat-predict` 起按新 bucket 派生。历史 prediction 文件里的 bucket 标签**不重算**——bucket 是预测时的语义判断，事后改写会破坏盲度。\n\n### Phase B 不做的事\n\n- 不重打 composite（公式没变）\n- 不重新审核观察段（rubric 没变）\n- 不调跨模型审核（确定性派生无需判断）\n- 不要求严格的样本数门槛（按 READINESS_HEURISTIC 由 Claude 判断；ratio 模式 N=1 就能跑）\n\n---\n\n## Key Rules\n\n1. **5 步不可跳**（仅完整 rubric bump）。任何\"先简化跑一下\"的请求都拒绝\n2. **THRESHOLD 写死**（仅完整 rubric bump）。不允许动态调整\n3. **跨模型审核是默认**（仅完整 rubric bump）。关闭审核需要在 state file 显式标记\n4. **cleanup pass 是 bump 的一部分**（仅完整 rubric bump）。不允许 bump 完不清理观察段\n5. **REQUIRE_CONFIRM**（两种模式都要）。最后落地前必须用户明确说 \"yes, bump\" 或 \"yes, recalibrate\"\n6. **bucket 重校不动历史预测**。bucket 是预测时语义，事后改写破坏盲度\n\n## Refusals\n\n- 「跳过校准池重打，直接换公式」 → 拒绝。原则 #2\n- 「跳过 cheat-score-blind sub-agent，主 Claude 直接重打就行」 → 拒绝。bump **不接受**任何 self-scored fallback——sub-agent 不可用 → abort bump，不接受\"自审\"\n- 「跳过外部 LLM 审核」 → 仅当 `CROSS_MODEL_AUDIT=false` 显式设置\n- 「这次 THRESHOLD 调到 3/5 让它过」 → 拒绝。改 THRESHOLD 是元层级 bump\n- 「保留所有旧观察作为历史」 → 违反原则 #3\n- 「先 bump，cleanup 下次再做」 → 拒绝。cleanup 是 bump 的一部分\n- 「只重算 composite 不重打 dim」 → 拒绝。新权重 × 旧 dim 仍是旧污染。每个 dim 都由 sub-agent 重审 script\n- 「把 Memo 全文写进 rubric_notes.md 顶部，方便我读」 → 拒绝。rubric_notes.md 是 blind sub-agent 白名单——含视频名 / 实绩 → 通过白名单泄漏。Memo 写 rubric-memo.md（白名单**外**），rubric_notes.md 只放公式 + 通用语言维度定义 + 指针\n- 「派生证据段保留真实视频名，让 rubric 读起来更具体」 → 拒绝。在 rubric_notes.md 必须用通用语言（\"高抽象密度样本\"）；带视频名的派生证据写 rubric-memo.md\n\n## Integration\n\n- 上游：`/cheat-retro` 检测到 ≥3 同向偏差 → 提议跑 `/cheat-bump`\n- 依赖：`mcp__llm-chat__chat`（如配置）+ Task tool（spawn cheat-score-blind）\n- 修改：\n  - `rubric_notes.md`（结构性更新，**绝不**写真实视频名 / 实绩）\n  - `rubric-memo.md`（**新**——append Memo 全文，含证据 + 派生证据）\n  - 所有 `predictions/*.md`（追加 Re-scored 行，不动预测段）\n  - `.cheat-state.json`\n- 下游：下一篇 `/cheat-predict` 自动按新 rubric_version 打分\n\n<!-- File: skills/cheat-init/SKILL.md (Tokens: ~9299 | Category: anthropic-skill) -->\n---\nname: cheat-init\ndescription: cheat-on-content 的首次 onboarding 与脚手架创建器。统一流程——所有用户都走相同 5 阶段闭环，唯一区别是\"发过视频的人\"会在 init 时多一步：抓取已有视频建立历史 context（用于后续 cheat-seed 给更贴合的选题、更准的 baseline）。触发词：\"初始化\"/\"init\"/\"首次使用\"/\"我是新用户\"/\"setup cheat-on-content\"。**必须在用户第一次会话执行；其他子 skill 在 .cheat-state.json 不存在时自动路由到此。**\nargument-hint: \"[— form: opinion-video|long-essay|short-text|podcast]\"\nallowed-tools: Bash(*), Read, Write, Edit, Glob, WebFetch, Skill\n---\n\n# /cheat-init — 首次 onboarding\n\n让用户从零到能跑第一篇预测，全程 ≤ 5 分钟（没发过历史的）或 ≤ 10 分钟（已发过、要 import 历史的）。\n\n## Overview\n\n```\n[用户首次说\"初始化\"]\n  ↓\n[Phase 0: 检测当前状态]\n  ↓\n[Phase 1: 首屏文案 — 适用性 + 期望管理]\n  ↓\n[Phase 2: 6 个问题（Q1-Q5 都问；Q2 决定是否走 user-history import）]\n  ↓\n[Phase 2.5: 对标账号 — 强烈建议（cold-start 必须问，已发用户可选）]\n  ↓\n[Phase 3: 创建脚手架（含 scripts/ + videos/ + samples/ 空目录 + 模板文件含 benchmark.md）]\n  ↓\n[Phase 3.5: user-history import 流程（仅 Q2=有发过历史 + 用户同意）]\n  ↓\n[Phase 4: 测试 hook 是否生效]\n  ↓\n[Phase 5: 给\"下一步该说什么\"清单]\n```\n\n## Constants\n\n- **DEFAULT_RETRO_WINDOW_DAYS = 3**\n- **INSTALL_HOOKS = ask** — 默认询问；用户选 `auto` 直接装；`skip` 不装\n- **TREND_DEFAULT_SOURCES = [\"manual-paste\"]**\n\n## Inputs\n\n无。所有信息从 6 个对话问题里收集。\n\n## Workflow\n\n### Phase 0: 检测当前状态\n\n1. 读用户当前工作目录（**用户的 content project，不是 cheat-on-content 自己**）\n2. 检查是否已存在 `.cheat-state.json`：\n   - 存在 → 提示\"项目似乎已初始化（state file 存在）。要重新初始化会覆盖现有配置——确认？\" 等用户明确确认才继续\n   - 不存在 → 进入 Phase 1\n3. 检查是否已存在 `rubric_notes.md` / `predictions/` 等核心文件——存在但 state file 不存在 → 是\"半初始化\"状态，提示用户并询问\"要从现有文件推断状态还是重置？\"\n\n### Phase 1: 首屏直白告知期望（含适用性验证）\n\n向用户输出（一字不漏，不要软化）：\n\n```\n🎯 Cheat on Content / 网红外挂 — 初始化\n\n你的下一条内容已经在改写 3 个月后的你。\n规律是客观存在的，区别是你**看见**还是**没看见**。\n这套让你看见。\n\n接下来 5-10 分钟我会问你 5-6 个问题搞清楚你做什么、有什么、怎么用。\n两件事先说在前面：\n\n1. **早期预测会不准**——前 5 篇精度大概 ±50%，这是数学事实。\n   工具用 🔴🟠🟡🟢🔵 标 confidence 等级，不藏数字——\n   你自己判断这次能不能信。\n\n2. **强烈建议导对标账号**——5-10 条对标视频，工具立刻有 anchor。\n   不然第一批预测基本是占星。后面 Q5 会再问一次。\n\n准备好开始吗？\n```\n\n如果用户答\"继续\"或类似肯定回应 → Phase 2。\n不再因为 content_form 拒绝继续——任何形态都允许，只是 `rubric_form_mismatch` 字段标真，cheat-status 后续会持续提示用户\"你的形态需要 bump 调权重\"。\n\n### Phase 2: 6 个问题（一问一答，**不**批量提问）\n\n**Q1: 内容形态**\n\n> \"你的内容更接近哪一种？\n> a) **观点视频**（评论 / 时评 / 论说 / 议题讨论 / 个人观点）— 直接匹配内置 rubric\n> b) **长文 essay**（公众号 / Substack / Medium）— 可借观点视频 rubric 起步，bump 时调权重\n> c) **短文 / thread**（X / 微博 / 即刻）— 同上\n> d) **播客 / 视频长内容**（YouTube 长片 / 播客）— 同上\n> e) **教程 / 工具教学 / Builder**（教别人怎么用 X 工具 / 怎么做 Y 项目）— 同上\n> f) **其他**（游戏 / 美食 / 妆教 / 新闻 / 剧情）— 工作流通用，但 rubric 维度需要调\n>      （ER / SR / HP 这套对你形态可能不太预测，需要自己拆出适合的维度）\n> g) **混合**\"\n\n记录到 `content_form` + `rubric_form_mismatch`。\n\n**Q1 → `content_form` enum 映射**（**必须存 enum 值，不是字母**）：\n\n| 用户答 | `content_form` 写入值 |\n|---|---|\n| a | `\"opinion-video\"` |\n| b | `\"long-essay\"` |\n| c | `\"short-text\"` |\n| d | `\"podcast\"` |\n| e | `\"tutorial-builder\"` |\n| f | `\"other\"` |\n| g | `\"mixed\"` |\n\n`rubric_form_mismatch` 派生：\n- 选 a → `false`\n- 选 b/c/d/e/f/g → `true`，cheat-status 持续提示\"你的形态可能需要 bump 调权重\"\n- **不再有\"严重不匹配\"档**——所有形态都能跑工作流，只是有的 rubric 需要更激进的 bump\n\n**Q1.5: 典型时长**（仅 Q1=a/d/f 时问）\n\n> \"你的视频典型时长？\n> a) 30秒-1分钟  b) 1-3分钟  c) 3-5分钟（推荐起步）\n> d) 5-10分钟   e) 10分钟以上\"\n\n记录到 `typical_duration_seconds`（30 / 90 / 240 / 450 / 900）。\n\n**Q1.6: 发布频率**\n\n> \"你打算多久发一篇？\n> a) 日更   b) 隔日   c) 每周   d) 灵活 / 不固定（关闭 buffer 监控）\"\n\n记录到 `target_publish_cadence_days`（1 / 2 / 7 / null）。\n\n**Q2: 你这个频道发过视频吗？**\n\n> \"a) 没发过 — 我会帮你从兴趣 + 热点 brainstorm 5 个候选 + 写 5 份初稿\n>  b) 发过 — 不管 1 条还是 100 条，我会帮你抓历史让后续 brainstorm 更贴合你做过什么\"\n\n如选 a → state 写 `calibration_samples: 0`，**Phase 3.5 跳过**，直接进入 Phase 4。\n如选 b → 进入 **Q2.1**。\n\n**Q2.1: 平台 + 抓取计划**（仅 Q2=b）\n\n> \"你内容主要在哪个平台？\n> a) 抖音 — 装 douyin-session adapter（Playwright + 扫码登录抖音创作者中心）\n> b) 小红书 — 装 xhs-explore adapter（Playwright + 扫码登录小红书创作者中心）\n> c) YouTube — 装 youtube-data-api adapter（需 API key）\n> d) B 站 — bilibili-stat adapter\n> e) LinkedIn — 装 linkedin-session adapter（Playwright + 登录 LinkedIn，抓单帖分析）\n> f) 微信视频号 — 装 wechat-channels adapter（Playwright + 扫码登录视频号助手，仅抓自己账号后台数据）\n> g) 其他 / 多平台 — 走 manual paste 模式\"\n\n如选 a/b/c/d/e/f → 询问 Q2.2；如选 g → 跳到 Q2.3 manual。\n\n**Q2.2: adapter 安装时机**（仅 Q2.1=a/b/c/d/e/f）\n\n> \"现在装 adapter 自动抓取，还是先手动告诉我？\n> - 现在装 — 引导你装 Playwright + 扫码 → 抓回最近 N 条数据\n> - 等下再装 — 先 manual 模式，state 标 'pending_adapter_setup'，\n>            cheat-status 持续提示装\"\n\n如选\"现在装\"→ 走 adapter install 引导（详见各 adapter README）→ 验证抓取可用 → Q2.3。\n如选\"等下\"→ 跳到 Q2.3 manual。\n\n**Q2.3: 抓取范围 / 历史规模**\n\n如 adapter 已装并验证可用：\n> \"我可以抓你最近多少条作为基础？\n> （建议 10-25 条；样本越多 baseline 越准。最多到你账号实际数量）\"\n→ 用户给数字 N，Phase 3.5 抓取 N 条\n\n如 manual 模式：\n> \"你大概发过多少条？给个范围就行（比如 '5-10 条' / '20+ 条'），\n>  这只用来标 calibration_samples 估值，不用准确。\"\n→ 用户给一个估值，Phase 3.5 跳过抓取，calibration_samples 写估值\n\n**Q3: 数据回收方式**\n\n> \"T+3 天复盘怎么拿数据？\n>\n> a) 手动粘 — 候补方案。**你必须粘 top 20+ 评论（带赞数）**，不是只粘播放数。\n>    评论才是真信号——'她不一样'这种模因爆发只能从评论看出，\n>    播放数永远告诉不了你什么内容真的击中了观众。\n> b) **[推荐默认]** adapter 自动抓 — 评论 + 数据全要。\n>    如果你现在没装 adapter，没关系，state 标 'pending_adapter_setup'，\n>    第一次 publish 之前装上就行（cheat-status 会持续提醒装）。\n>    装的指引在 adapters/perf-data/<platform>/README.md。\"\n\n**Q3 → `data_collection` enum 映射**：\n\n| 用户答 | `data_collection` 写入值 |\n|---|---|\n| a | `\"manual\"` |\n| b（默认） | `\"adapter\"` |\n\n默认推荐 b——除非用户明确说 \"a 我就要手动\"。\n\n**Q4: 候选选题**\n\n> \"你现在有候选选题列表吗？（如有外部 markdown / Notion 维护的）\n> a) 没有（默认）— 一会儿我帮你 brainstorm，或日常用 /cheat-trends 抓\n> b) 有，markdown 列表\n> c) 有，Notion / 其他\"\n\n**Q4 → `pool_status` enum 映射**：\n\n| 用户答 | `pool_status` 写入值 |\n|---|---|\n| a（默认） | `\"none\"` |\n| b | `\"markdown\"` |\n| c | `\"notion\"` |\n\n**Q5: 装几个 hook（默认装，不需要你决定）**\n\n> \"Q5：我顺便装几个 hook，回 'yes' 或 'enter' 就装：\n>\n> 1. **预测锁** — 我们一起做完预测后，文件被锁。你或我都不能改预测段。\n>    复盘只能往同一文件下半段追加，不污染上半段判断。\n>    （没这个锁，事后看到数据想\"修一下当时的预测\"几乎是必然的——你或我都会犯）\n>\n> 2. **SessionStart 自动报告** — 每次开新会话顶部显示 buffer / 待复盘 / 候选 top\n>\n> 3. **静默使用日志** — 异步记录使用频率，不阻塞，给将来诊断用\n>\n> 三个一起装。**不装也可以**（回 'no'）但你失去预测锁，校准价值会下降。\n>\n> 回 yes / no。\"\n\n**Q5 → `hooks_installed` 映射**：\n\n| 用户答 | `hooks_installed` 写入值 |\n|---|---|\n| yes / enter / 默认 | `true`（bool，**不是字符串 `\"yes\"`**） |\n| no | `false` |\n\n默认 yes——除非用户明确说 no。\n\n### Phase 2.5: 对标账号（**所有用户都问**，cold-start 强烈建议）\n\n> 工具早期最重要的信号源是**对标账号**——你 init 完没数据，rubric 等权 v0 等于占星。\n> 但如果你能找一个你想做成那样的账号，导入 5-10 条它的高 / 中 / 低样本，工具就有了 anchor。\n\n询问：\n\n```\n🎯 对标账号\n\n你能找一个对标账号吗？至少 3 条该账号的视频。\n\n  - 你**完全没发过历史**（Q2=a）→ **强烈建议**——rubric 没 anchor 全靠对标。\n    不找的话用通用 v0 等权 rubric，前 5 篇精度更差更久\n  - 你**已发历史**（Q2=b）→ **可选**——你也可以只用自己历史 calibrate；\n    但建议至少导 1 个对标做 sanity check（看你账号是否真的偏离对标方向）\n\na) 现在找 → 立刻进入 /cheat-learn-from（5-15 分钟，看你材料准备程度）\nb) 等下找 → state 标 `benchmark_status: pending`，cheat-status 持续提醒\nc) 不找 → state 标 `benchmark_status: none`，用通用 v0 起步\n\n回 a / b / c。\n```\n\n行为：\n- 选 a → Phase 3 创建脚手架完毕后，**自动 dispatch 到 /cheat-learn-from**（不让用户手动跑——已经在 init 流程里了）。完成后回 init Phase 4\n- 选 b → state 标 `benchmark_status: pending` + `benchmark_name: null`\n- 选 c → state 标 `benchmark_status: none`\n\n记录到 `benchmark_status` / `benchmark_name`（如 a 选则在 cheat-learn-from 里写入）。\n\n### Phase 3: 创建脚手架（逐项解释）\n\n按顺序创建并**解释每一项的作用**：\n\n0. **`.gitignore`（安全 — 必须第一步创建）**\n   ```\n   \"先创建 .gitignore，把账号凭证挡在版本控制外——这是第一件事。\n    .auth/ / .auth-xhs/ / .auth-linkedin/ / .auth-wechat-channels/ 存的是各平台登录态（等同账号密码），\n    .cheat-secrets.json 存 API key / cookie——一旦被 commit 或云同步就等于泄露账号。\n    注意：predictions/ videos/ scripts/ 这些**不**忽略——原则 #1/#3 依赖\n    git history 作为预测的不可变档案，必须入库。\"\n   ```\n   - 复制 `cheat-on-content/templates/gitignore.template` → `<user-repo>/.gitignore`\n   - 如 `<user-repo>/.gitignore` **已存在** → 不覆盖；逐行检查并**追加缺失行**，至少确保\n     `.auth/`、`.auth-xhs/`、`.auth-linkedin/`、`.auth-wechat-channels/`、`.cheat-secrets.json` 五行存在\n   - 即使用户项目当前**还不是 git 仓库**也照常创建——它会在用户 `git init` 的那一刻立即生效\n   - 创建后提醒一句：如果项目已经 `git init` 过且可能误加过 `.auth/`，让用户跑\n     `git rm -r --cached .auth .auth-xhs .auth-linkedin .auth-wechat-channels .cheat-secrets.json` 把已暂存的凭证移出\n\n1. **`.cheat-state.json`**\n   ```\n   \"正在创建 .cheat-state.json — 各子 skill 共享上下文的地方。\n    这次 init 收集的所有答案都会写在这里。\"\n   ```\n   写入（**所有 `<...>` 占位必须查上面 Q 的映射表换成具体 enum 值，绝不直接存字母**）：\n   ```json\n   {\n     \"schema_version\": \"1.4\",\n     \"skill_version\": \"1.0.0\",\n     \"rubric_version\": \"v0\",\n     \"content_form\": \"<查 Q1 映射表，写 enum 字符串如 \\\"opinion-video\\\">\",\n     \"typical_duration_seconds\": <Q1.5 派生：30/90/240/450/900>,\n     \"target_publish_cadence_days\": <Q1.6 派生：1/2/7/null>,\n     \"rubric_form_mismatch\": <Q1=a→false；其他→true>,\n     \"benchmark_status\": \"<Phase 2.5 派生：a→\\\"imported\\\"/b→\\\"pending\\\"/c→\\\"none\\\">\",\n     \"benchmark_name\": <imported 则字符串名，否则 null>,\n     \"benchmark_sample_count\": <imported 则数字，否则 0>,\n     \"baseline_plays\": null,\n     \"calibration_samples\": <Q2=a→0；Q2=b→Phase 3.5 import 回填或 Q2.3 估值>,\n     \"data_collection\": \"<查 Q3 映射表，写 \\\"manual\\\" 或 \\\"adapter\\\">\",\n     \"pool_status\": \"<查 Q4 映射表，写 \\\"none\\\"/\\\"markdown\\\"/\\\"notion\\\">\",\n     \"data_layer\": \"markdown\",\n     \"hooks_installed\": <查 Q5 映射表，写 bool true/false>,\n     \"enabled_trend_sources\": [\"manual-paste\"],\n     \"enabled_perf_adapters\": <Q2.1=a→[\\\"douyin-session\\\"]；b→[\\\"xhs-explore\\\"]；c→[\\\"youtube-data-api\\\"]；d→[\\\"bilibili-stat\\\"]；e→[\\\"linkedin-session\\\"]；f→[\\\"wechat-channels\\\"]；其他→[]>,\n     \"last_bump_at\": null,\n     \"last_bump_self_audited\": false,\n     \"last_published_at\": null,\n     \"last_published_file\": null,\n     \"last_retro_at\": null,\n     \"last_trends_run_at\": null,\n     \"last_trends_added_count\": 0,\n     \"last_prediction_self_scored\": false,\n     \"last_self_scored_at\": null,\n     \"consecutive_directional_errors\": [],\n     \"pending_retros\": [],\n     \"shoots\": [],\n     \"in_progress_session\": null,\n     \"initialized_at\": \"<本地 ISO 8601 含时区，如 \\\"2026-05-05T20:11:13+08:00\\\"，**不要用 UTC 的 Z 后缀**>\"\n   }\n   ```\n\n2. **`rubric_notes.md`**\n   ```\n   \"正在创建 rubric_notes.md — 你的评分维度的真实来源。\n    用的是 v0 占位 rubric——等权 7 维（每个维度同等重要）。\n    \n    为什么叫 v0：v0 是没校准前的占位。你的账号自己的真权重要从你\n    的数据反推，不是预设。跑完 5 篇有数据的内容后，会自动提议\n    升级到「校准 v1」（你的第一个真正校准过的 rubric）。\n\n    ⚠️ rubric_notes.md 是 blind sub-agent (channel B) 的白名单文件——\n    只能含通用语言（公式 / 维度定义 / bucket 边界），不能含真实视频名 / 实绩。\n    每次 bump 升级时的 Memo（含证据数据 + 派生证据）写到 rubric-memo.md（下一步创建）。\"\n   ```\n   - 复制 `cheat-on-content/starter-rubrics/<form>-zero.md`（cold-start）或 `<form>.md`（已有数据时仍可参考）\n\n2.5. **`rubric-memo.md`**（**新**——配合 cheat-score-blind 隔离协议）\n   ```\n   \"正在创建 rubric-memo.md — bump 升级 Memo 累积档案。\n    这是 cheat-bump Phase 5 写入 Memo 全文（含真实视频名 + 实绩 + 派生证据）的位置。\n\n    为什么单独一个文件：blind sub-agent 的白名单是 rubric_notes.md，\n    历史上 bump Memo 写进 rubric_notes.md 会让 blind sub-agent 通过白名单\n    拿到本该看不到的实绩数据——本文件是隔离修复，sub-agent 硬禁读本文件。\n    \n    现在是空的，等第一次 cheat-bump 升级后 append 第一段 Memo。\"\n   ```\n   - 复制 `cheat-on-content/templates/rubric-memo.template.md` → `<user-repo>/rubric-memo.md`\n\n3. **`script_patterns.md`**\n   ```\n   \"正在创建 script_patterns.md — 你的写作 pattern 沉淀（与 rubric 解耦）。\n    rubric_notes.md 教 Claude 怎么打分；\n    script_patterns.md 教 Claude 怎么写。\"\n   ```\n   - 复制 `cheat-on-content/templates/script_patterns.template.md`\n\n4. **四个目录**：`scripts/` + `predictions/` + `videos/` + `samples/`（都加 `.gitkeep`）\n   ```\n   \"正在创建四个目录：\n    \n    scripts/      — 拍前的草稿（cheat-seed 写或你写）\n    predictions/  — immutable 预测日志（hook 保护）\n    videos/       — 拍后的工作目录（cheat-shoot 创建子目录）\n    samples/      — 对标账号视频 / 转录（cheat-learn-from 创建子目录）\n    \n    前三处用同一组 <date>_<id>_<short> 命名相互关联。\n    samples/ 按对标账号名分组：samples/<账号名>/<video-id>/。\"\n   ```\n\n4.5. **`benchmark.md`**（仅 Phase 2.5 选 a/b 时）\n   ```\n   \"正在复制 benchmark.md 占位模板（实际内容由 cheat-learn-from 填）—— \n    这是你的对标账号的中央 reference。\n    前期工具的 rubric / pattern / 选题方向感大量从这里推；\n    后期 N≥10 后影响淡出，但保留作 sanity check。\"\n   ```\n   - 复制 `cheat-on-content/templates/benchmark.template.md` → `<user-repo>/benchmark.md`\n   - **Phase 2.5 选 c 不创建** → benchmark.md 不存在，state 标 `benchmark_status: none`\n\n4.7. **`audience.md`**\n   ```\n   \"正在创建 audience.md — 你账号的受众画像（'谁在看'）。\n    \n    现在是空骨架。它和 rubric_notes.md 平行——rubric 教 Claude 怎么打分，\n    audience 告诉 Claude 你的观众是谁。跑够几篇复盘后跑 /cheat-persona，\n    它会从评论数据聚类出真实画像，cheat-seed 选题写稿时就有了一面镜子。\n    \n    注意：audience.md 由评论派生 → 含实绩信号 → blind 打分 sub-agent 硬禁读它。\"\n   ```\n   - 复制 `cheat-on-content/templates/audience.template.md` → `<user-repo>/audience.md`\n   - 如 Phase 2.5 选了 a/b（有 benchmark）→ Phase 5 清单里提示\"可跑 `/cheat-persona — seed-from-benchmark` 先 seed 一份未验证画像\"\n\n5. **`WORKFLOW.md`** + **`STATUS.md`**\n   - 复制 templates/ 对应文件\n\n6. **如果 Q5=是 → 安装 hooks**\n   - 读 `.claude/settings.json`（如不存在则创建空 `{}`）\n   - merge 进 `hooks/prediction-immutability.json` 的 `hooks.PreToolUse`\n   - merge 进 `hooks/session-start.json` 的 `hooks.SessionStart`\n   - merge 进 `hooks/meta-logging.json` 的 hooks（如同时启用）\n   - 复制 `prediction-immutability.sh` + `session-start.sh` + `log-event.sh` 到 `.cheat-hooks/`，chmod +x\n   - settings.json 里的 command 路径用 `${CLAUDE_PROJECT_DIR}/.cheat-hooks/`\n\n7. **(Pool 选项 c—Notion)** 仅记录到 state file 的 `pool_status: notion`，后续 cheat-trends 调用时再处理\n\n### Phase 3.5: import 流程（仅 Q2=b 且用户同意抓取）\n\n如 Q2.2=现在装 → 走 adapter install + login（详见 [adapters/perf-data/<platform>/README.md](../../adapters/perf-data/)）。\n\n抓取成功后，对每条已发视频：\n\n1. **建 video folder**：`videos/<date>_<id>_<short>/`\n   - `<date>` = 视频实际发布日\n   - `<id>` = 12 位 hash，对 (title + 平台 ID) 做 sha256\n   - `<short>` = 标题前 3-8 字\n2. **写 report.md**：从 adapter 抓回的数据（播放 / 点赞 / 评论 / 转发 / top 评论）填入\n3. **询问用户原稿**：\"video 「{标题}」 你保留了原稿吗？\"\n   - 是 → 用户提供 → 存为 `videos/<id>/script.md`\n   - 否 → 标 `script_lost`（仍建 video folder，只是 script.md 缺失）\n4. **写 reconstructed prediction**：`predictions/<date>_<id>_<short>.md`\n   - header 标 `**Reconstructed retrospective — NOT a blind prediction**`\n   - 7 维打分基于 script + 复盘段实绩数据**反向打**——明确这是非校准用途\n   - 不计入 calibration_samples（这是导入的历史，不是校准积累）\n\nimport 完成后：\n- 派生 `baseline_plays` = 抓回视频的播放中位数 → 写入 state file\n- 派生 confidence 等级 → 后续 cheat-predict 写预测时直接用\n- 输出汇总：\"已 import N 条历史。最近一条 X w 播放，中位数 Y w，已建 N 个 video folder + reconstructed predictions\"\n\n### Phase 4: 测试 hook 是否生效（仅当 Q5=是）\n\n跑一次假的 Edit 拦截测试：\n1. 创建临时文件 `predictions/_test_hook.md`，含 `## 预测\\n[test]\\n## 复盘\\n`\n2. 尝试 Edit 这个文件的 `## 预测` 段\n3. 钩子应 exit 1 阻塞 → 报告\"✅ immutability 钩子生效\"\n4. 删除测试文件\n5. SessionStart hook 验证：直接调一次 `bash .cheat-hooks/session-start.sh` → 应输出报告（即使是空的也行）\n\n如果钩子未生效 → **不要假装成功**，明确告诉用户：\"钩子安装失败，可能是 .claude/settings.json 配置没生效。建议手动检查或重启 Claude Code。\"\n\n### Phase 4.5: 如 Phase 2.5 选 a → dispatch 到 /cheat-learn-from\n\n如果用户在 Phase 2.5 选了 a（现在导对标账号）→ **自动触发 /cheat-learn-from**：\n\n```\n✅ 脚手架 + hooks 装完。\n\n下面立刻进入 /cheat-learn-from 帮你导入对标账号——\n你 init 时选了\"现在找\"，不让你又开一个会话才跑。\n\n[invoke /cheat-learn-from]\n```\n\ncheat-learn-from 完成后回到 init 的 Phase 5。\n\n如 Phase 2.5 选 b/c → 跳过 Phase 4.5，直接 Phase 5。\n\n### Phase 5: 给\"下一步该说什么\"清单\n\n```\n✅ 初始化完成（rubric: v0，calibration_samples: <N>，confidence: <emoji 等级>）\n\n下次你可以直接说这些：\n\n📝 写完一篇稿子 → \"打分这篇 scripts/<...>.md\"\n🎯 准备发布前  → \"启动预测 scripts/<...>.md\"\n🎬 拍完了      → \"拍了 scripts/<...>.md\" → 建 video folder + buffer +1\n🚀 发布后      → \"已发布 https://...\"\n📊 T+3 天      → \"复盘 videos/<...>/\"\n📈 任何时候    → \"状态\"（看完整看板）\n\n<如果 Q4=没有候选选题:>\n🌱 现在跑 /cheat-seed 找选题？\n   - 没发过历史的：纯 brainstorm（兴趣 × 热点）\n   - 发过历史的（已 import）：brainstorm 会基于你过去做过什么给推荐\n   回 \"yes, seed\" 立刻跑，回 \"no\" 你自己想。\n\n💡 你的 confidence 是 <当前等级> —— 它会随着你跑更多复盘自动提升。\n   不要因为 confidence 低就跳过预测——预测的纪律本身就是工具的核心，\n   早期预测的\"价值\"是数据采集，不是决策。第 5 次复盘后 rubric 第一次校准，\n   confidence 会跨入 🟡 偏低；第 10 次后 🟢 中。\n```\n\n## Key Rules\n\n1. **不假装成功**：任何步骤失败 → 明确告诉用户哪一步出错。绝不写\"✅ 初始化完成\"如果实际没完成\n2. **不批量提问**：5 个问题一次问一个\n3. **不静默 mkdir**：每创建一个文件都解释它的作用\n4. **不强推 SQLite**：所有用户给 markdown，提一句\"将来到 30 篇会建议升级\"就够了\n5. **state 字段统一**：删掉 mode / prediction_complexity / bucket_scheme 等枚举字段——单一用 calibration_samples 整数 + confidence 派生\n6. **import 失败不阻塞**：Q2=b 但 adapter 装失败 / 抓取失败 → 优雅降级到\"标 calibration_samples 估值，不导入历史 video folder\"\n\n## Refusals\n\n- 「跳过 Q1-Q5，直接给我创建所有文件」 → 拒绝。问题答案直接影响默认配置（content_form、cadence、hooks）\n- 「我已经在别处初始化过了，把那个项目的配置同步过来」 → 慎重。提示用户手动 cp 现有 `.cheat-state.json` 和 `rubric_notes.md`，不自动跨项目同步\n- 「不装 hook 但保留 immutability 承诺」 → 允许，state 标 `hooks_installed: false`，cheat-status 持续提示\"你的 immutability 是君子协定\"\n\n## Integration\n\n- 写完后，主 SKILL.md 的路由就解锁了所有其他子 skill\n- `cheat-status` 读 `.cheat-state.json` 的 `calibration_samples` 字段决定显示哪个 confidence 等级\n- 如 Q2=b 走了 import → 历史 reconstructed predictions 进 `predictions/` 和 `videos/<...>/`，但**不**计入 calibration_samples（不是真校准样本）\n- `/cheat-seed` 读 `predictions/` 的所有历史 reconstructed prediction → brainstorm 时知道\"用户过去做过什么\"\n\n## State 字段写入清单\n\n| 字段 | 写入时机 | 来源 |\n|---|---|---|\n| `schema_version` | Phase 3 | 硬编码 \"1.4\" |\n| `skill_version` | Phase 3 | 硬编码 \"1.0.0\" |\n| `rubric_version` | Phase 3 | \"v0\" |\n| `content_form` | Phase 3 | Q1 → 查映射表换 enum 值（**不是字母**） |\n| `typical_duration_seconds` | Phase 3 | Q1.5 派生 |\n| `target_publish_cadence_days` | Phase 3 | Q1.6 派生 |\n| `rubric_form_mismatch` | Phase 3 | Q1≠a → true |\n| `benchmark_status` | Phase 3 / 2.5 | Q2.5 答案派生 |\n| `benchmark_name` | Phase 3 / 2.5 | Q2.5 用户提供 |\n| `benchmark_sample_count` | Phase 3 / 2.5 | cheat-learn-from import 后回填 |\n| `baseline_plays` | Phase 3.5（如 import 成功） | import 数据中位数；否则 null |\n| `calibration_samples` | Phase 3 / Phase 3.5 | Q2=a→0；Q2=b→Q2.3 估值或 import 数 |\n| `data_collection` | Phase 3 | Q3 → 查映射表换 enum 值 |\n| `pool_status` | Phase 3 | Q4 → 查映射表换 enum 值 |\n| `enabled_perf_adapters` | Phase 3 | Q2.1 派生（如 Q2=a 则 `[]`） |\n| `hooks_installed` | Phase 3-4 | Q5 → bool（不是字符串） |\n| `last_bump_at` / `last_published_at` / `last_published_file` / `last_retro_at` / `last_trends_run_at` | Phase 3 | 全部 `null` |\n| `last_bump_self_audited` | Phase 3 | `false` |\n| `last_trends_added_count` | Phase 3 | `0` |\n| `last_prediction_self_scored` | Phase 3 | `false` |\n| `last_self_scored_at` | Phase 3 | `null` |\n| `initialized_at` | Phase 3 | now() 本地 ISO 8601，含 `+08:00` 时区，**不要 UTC `Z`** |\n\n<!-- File: skills/cheat-learn-from/SKILL.md (Tokens: ~5471 | Category: anthropic-skill) -->\n---\nname: cheat-learn-from\ndescription: 从对标账号导入 script + 数据 → 拆 pattern + 派生 base rubric 信号 → 写到 benchmark.md / script_patterns.md / rubric_notes.md。**这是工具最早期信号的来源**——cold-start 用户没自己历史时全靠对标，发过历史的用户也建议至少 1 个对标做 sanity check。触发词：\"学这个账号\"/\"拆这几个对标视频\"/\"learn from\"/\"导入对标账号\"/\"找对标\"。\nargument-hint: <账号名> [— way: a (default) | b] [— append | --replace]\nallowed-tools: Bash(*), Read, Write, Edit, Glob, WebFetch, Skill\n---\n\n# /cheat-learn-from — 对标账号导入\n\n工具早期最重要的信号源是**对标账号**——你 init 完没数据，rubric 等权 v0 等于占星。但如果你能找一个你想做成那样的账号，导入 5-10 条它的高/中/低样本，工具就有了 anchor。\n\n后期当你自己 calibration_samples ≥ 10 时，benchmark 影响自然减弱——你的真实数据成为主要信号源。但 benchmark.md **不删**，仍是 cheat-seed brainstorm 的 reference frame。\n\n## Overview\n\n```\n[用户：学这个账号 / 启动 cheat-learn-from]\n  ↓\n[Phase 0: 检查 benchmark 状态]\n  ↓\n[Phase 1: 选 input 方式（Way a 默认）]\n  ↓\n[Phase 2: 收集材料]\n  Way a: 用户粘 N 条 script 文本 + 数据\n  Way b: 用 whisper 转录 samples/ 目录里的视频\n  ↓\n[Phase 3: 询问每条样本的\"印象判断\"（高/中/低 + 为啥）]\n  ↓\n[Phase 4: Claude 拆 pattern + 派生 rubric 信号]\n  ↓\n[Phase 5: 用户 review → 改 → 落盘]\n  ↓\n[Phase 6: 写 benchmark.md / script_patterns.md / rubric_notes.md]\n  ↓\n[Phase 7: 更新 state.benchmark_status]\n```\n\n## Constants\n\n- **MIN_SAMPLES = 3** — 最少 3 条样本（少于拆不出 pattern）\n- **RECOMMENDED_SAMPLES = 5-10** — 推荐区间，平衡信号量 vs 用户工作量\n- **MAX_SAMPLES_PER_RUN = 15** — 单次导入上限——再多 Claude context 不够 + 用户也累\n- **DEFAULT_WAY = a** — Way a 简单 + 准确，是 default\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<账号名>` | 用户参数；缺失则询问 |\n| `.cheat-state.json` | 状态文件 |\n| Way a: 用户粘的 script 文本 + 数据 | 对话 |\n| Way b: `samples/<账号名>/*.mp4` 等视频文件 | 用户提前下载好放进去 |\n\n## Workflow\n\n### Phase 0: 检查 benchmark 状态\n\n读 `.cheat-state.json` 的 `benchmark_status`：\n\n| 状态 | 处理 |\n|---|---|\n| `none` | 首次导入——继续 Phase 1 |\n| `pending` | 用户之前答应等下找——继续 Phase 1 |\n| `imported` 已有 benchmark | 询问\"你已有 benchmark [当前名]，N 条样本。要做什么？  a) 追加新视频到当前 benchmark  b) 替换为新 benchmark  c) 只看不改\" |\n\n参数解析：\n- `--append` → 追加到现有 benchmark\n- `--replace <new-name>` → 用新 benchmark 替换（旧的归档到 benchmark.archived/）\n- 没标志 + 已有 benchmark → 走上面询问\n\n### Phase 1: 选 input 方式（**两个独立维度**）\n\n每条样本 = **script** + **数据**。两者怎么拿是独立的——你可以混搭。\n\n#### Phase 1a: script source（怎么拿稿子）\n\n```\nscript 怎么拿？\n\na) **粘文本（最简单，推荐）**\n   - 你自己整理过 / 用工具提取过——直接粘进对话\n   - 工具推荐（按方便程度排）：\n   \n     抖音 / 小红书：\n     - 微信小程序「轻抖」—— 粘视频链接 → 自动提取文案 + 评论。最快\n     - 类似工具：\"视频解析助手\" / \"短视频文案提取\" 等小程序\n     - 通常有免费额度，重度使用收费\n     \n     B 站 / YouTube：\n     - 视频页面有\"显示字幕/文字记录\"按钮（如果 UP 主开了）\n     - 第三方：DownSub / SaveSubs / yt-dlp --write-auto-sub\n     \n     公众号 / Substack：\n     - 直接复制网页文字\n   \nb) **whisper 转录视频文件**\n   - 你下载了视频到 samples/<账号名>/<video>/source.mp4\n   - 需要装 whisper-cpp + ffmpeg（见 adapters/script-extraction/whisper/README.md）\n   - 转录可能有错别字 / 漏字 / 标点不准——准确度比 a 差\n\nc) **跳过 script，只用元数据 + 印象**\n   - 你拿不到稿子也懒得用工具\n   - 后果：pattern 拆不深（只能看标题 / 数据 / 你的印象），但 rubric 信号还行\n   - 适合\"先快速搭起来，将来补\"\n\n回 a / b / c。\n```\n\n#### Phase 1b: data source（怎么拿播放/点赞/评论）\n\n```\n数据（播放 / 点赞 / 评论 / 转发）怎么拿？\n\na) **手填数字（最简单）**\n   - 你查一下账号后台或视频页面，告诉我数字\n   - 不需要装任何工具\n\nb) **adapter 自动抓（如已配置）**\n   - 你已经装了 perf-data adapter（如 douyin-session）\n   - 给我视频 URL，工具自己抓数据 + top 评论\n   - 评论数据更全（手填只能告诉我数字，adapter 能拿到具体评论文本）\n\n回 a / b。\n```\n\n**最常见的组合**：\n- 完全零依赖路径：1a + 1b（粘文本 + 手填）—— 5 分钟搞定\n- 评论质量优先：1a + 2b（粘文本 + adapter 抓）—— 装了 adapter 就走这个\n- 拿不到稿子兜底：1b + 1b（whisper + 手填）\n\n### Phase 2: 收集材料\n\n按 Phase 1a + 1b 的组合走对应路径。\n\n**通用纪律**：每条样本最少要有 (script 或 transcript 或 N/A标记) + 数据 (4 项基础：播放/点赞/评论/转发)。\n\n#### Path A: 粘文本（Phase 1a=a）\n\n```\n好。我们一条一条来。最少 3 条，推荐 5-10 条。\n\n第 1 条 script，把整段粘下面（段落版，不要字幕格式）：\n```\n\n收到 script → 算 video_id（sha256(script_content)[:12]）→ 进 Phase 2 数据采集。\n\n#### Path B: whisper 转录（Phase 1a=b）\n\n```\n先确认 whisper 装了：\n\n[运行 `command -v whisper-cpp` 或 `command -v whisper` 检测]\n\n如果没装：\n  ❌ whisper 没装。三选一：\n  - brew install whisper-cpp（推荐——快）\n  - pip install openai-whisper（Python 慢些）\n  - 切回 Path A 自己粘文本（推荐用轻抖等小程序）\n\n装好后：把视频文件放到 samples/<账号名>/<video-name>/source.mp4\n（一个视频一个子目录）\n\n放好后告诉我\"放好了 N 条\"，我转录。\n```\n\n用户放好后：\n1. Glob `samples/<账号名>/*/source.*` 找视频文件\n2. 对每个视频跑 `bash adapters/script-extraction/whisper/run.sh <video> samples/<账号名>/<id>/`\n3. 失败项报告但继续其他\n4. 进 Phase 2 数据采集\n\n#### Path C: 跳过 script（Phase 1a=c）\n\n直接进 Phase 2 数据采集——告诉用户\"没 script 我能拆的 pattern 仅限标题级别 / 你的印象，rubric 信号正常拆\"。\n\n#### Phase 2 数据采集（Phase 1b=a 手填 / b adapter）\n\n**如 Phase 1b=a（手填）**：\n\n```\n第 1 条数据：告诉我\n- 标题\n- 播放量\n- 点赞\n- 评论数（不是评论内容，是数字）\n- 转发 / 分享数\n\n格式随意，能识别就行。例如：\n  \"标题：怎么停止期待\n   播放：71w / 点赞 2.4w / 评论 899 / 转发 1.8w\"\n\n如果你能再粘 top 5-10 条评论（带赞数）更好——pattern 拆能挖到模因层。\n```\n\n**如 Phase 1b=b（adapter）**：\n\n```\n你说装了 adapter（如 douyin-session）。给我每条视频的 URL，\n我跑 adapter 自动抓数据 + top 评论。\n\n第 1 条 URL：\n```\n\n收到 URL → 调对应 adapter → 写数据 + 评论到 samples/<账号名>/<id>/meta.md。\n\n**通用**：继续问第 2 条 / 第 3 条 / ... 用户说\"够了\"或达到 MAX_SAMPLES_PER_RUN 时进 Phase 3。\n\n### Phase 3: 询问\"印象判断\"\n\n对每条样本（不管 Way a 或 b），收完数据后**追加问印象**：\n\n```\n你看完 / 听完这条视频的印象，算这个账号的：\n  a) 高表现样本（代表作 / 你想做成这样的）\n  b) 中表现样本（普通水准 / 不上不下）\n  c) 低表现样本（不算这个账号的代表 / 你不想做成这样）\n\n为什么？（一句话——这个判断比数据更能告诉我你想做什么风格）\n```\n\n记录 (impression_label, impression_reason) 到内存。\n\n> 注意：印象**可以**和数据冲突——比如某条数据高但用户觉得\"不算代表作\"。这种冲突本身是有用信号，记录下来。\n\n### Phase 4: Claude 拆 pattern + 派生 rubric 信号\n\n阅读所有 (script, 数据, 印象) → 自己分析：\n\n#### 4a. Script patterns\n\n按 script_patterns.md 的 cheat sheet 框架拆：\n- 开头钩子：3 种类型分布（场景代入 / IS 戏仿 / 数据反转）\n- 主体结构：几段 / 怎么切\n- 句式 / 句长 / 节奏：短句还是长句、有没有标志性句式\n- emotional 标记 / 双声道\n- 致谢段 / 收尾\n- 高频词汇 / 词汇风格\n\n输出 N 个具体 pattern（每个引用具体样本作证据）。\n\n#### 4b. Rubric 信号（**仅定性，不给数值权重**）\n\n对每条样本打 7 维分（用通用维度），然后看：\n- 高表现样本（按用户印象）共有哪些维度高？\n- 低表现样本共有哪些维度低？\n- 哪些维度在高/低样本之间无差异（说明不是关键维度）？\n\n输出**定性方向**（不是数值权重）：\n- \"ER 看起来重要\"（3/3 高样本 ER ≥4）\n- \"SR 看起来不显著\"（高低样本 SR 分布无差异）\n- \"MS 高的样本评论区有明显模因爆发\"\n\n### Phase 5: 用户 review\n\n一次性展示所有结果给用户：\n\n```\n我从你给的 N 条对标视频拆出：\n\n📝 N 个 script pattern：\n  1. **[Pattern 1 名称]**：[一句话描述] → 证据：[样本 X / Y]\n  2. ...\n  \n🎯 Rubric 定性信号：\n  - 看起来重要的维度：ER / QL / MS（每个有 N 条高样本支持）\n  - 看起来不显著的维度：SR / NA\n  - 给的初始建议：你的对标账号是 [情感+金句驱动型] / [数据驱动型] / [类比讲解型] / ...\n  - **不直接给数值权重**——5-10 样本拟合容易过拟合，先用作 tier-2 信号\n  \n🎨 选题方向感：\n  - 主题分布大概：[主题 A 40% / 主题 B 30% / ...]\n  - 调性：[一句话]\n\n回 \"ok\" 我落盘，\n或指出哪些 pattern / 信号你不认同（\"Pattern X 我觉得不准\" / \"Rubric 信号 Y 错了\"）。\n```\n\n用户反馈循环：\n- \"ok\" → Phase 6 落盘\n- 用户挑刺 → Claude 改 → 重新展示 → 直至确认\n\n### Phase 6: 落盘\n\n#### 6a. benchmark.md\n\n按 [templates/benchmark.template.md] 格式写到 `<user-channel>/benchmark.md`：\n- 账号信息（账号名、URL、调性、粉丝量级——用户提供）\n- 导入的样本表\n- 基础 rubric 派生（仅定性）\n- 选题方向感\n\n如 `--append` → 在现有 benchmark.md 的样本表追加新行 + 重新拆 pattern；不重写整个文件。\n如 `--replace` → 把现有 benchmark.md 移到 `benchmark.archived/<旧账号名>_<日期>.md`，写新的。\n\n#### 6b. samples/<账号名>/\n\n为每条样本建子目录：\n```\nsamples/<账号名>/<video-id>/\n├── source.mp4 (Way b 才有，Way a 没有)\n├── transcript.md (从粘文本写 / whisper 转出来)\n└── meta.md (标题 / 数据 / 印象 / 印象理由)\n```\n\n#### 6c. script_patterns.md\n\n在 `<user-channel>/script_patterns.md` 加新段：\n\n```markdown\n## 对标 [账号名] 借鉴（imported on YYYY-MM-DD，N 条样本）\n\n> 这些 pattern 来自对标账号——**Imported, untested on my channel**。\n> 实拍验证后（≥2 次跑出 + 复盘确认有效）再去掉这个标记，升入正式 pattern。\n\n### Pattern A: [一句话名]\n**来自**: [样本 X]\n**描述**: [详细]\n\n### Pattern B: ...\n```\n\n#### 6d. rubric_notes.md\n\n在 `<user-channel>/rubric_notes.md` 加 / 更新\"benchmark-derived initial signals\"段：\n\n```markdown\n## Benchmark-derived initial signals\n\n> 来自 benchmark.md 的对标账号 [账号名]（N=N 样本，import on YYYY-MM-DD）。\n> **仅定性方向，不直接采纳为数值权重**——5-10 样本拟合容易过拟合。\n> 等你自己 N≥5 校准样本后正式 bump 时**再决定**是否调权重。\n\n- 看起来重要的维度: ER / QL / ...\n- 看起来不显著的维度: SR / NA / ...\n- Claude 给的初始建议: [一句话定性]\n```\n\n### Phase 7: 更新 state file\n\n```json\n{\n  \"benchmark_status\": \"imported\",\n  \"benchmark_name\": \"<账号名>\",\n  \"benchmark_sample_count\": <N>\n}\n```\n\n## Key Rules\n\n1. **Way a 默认**——简单 + 准确。Way b 仅给\"找不到 script 只有视频\"的兜底\n2. **必须问印象**——纯看 transcript 拆 pattern 容易抓表面，加用户印象才挖到深层\n3. **Rubric 信号仅定性**——不直接给数值权重。5-10 样本拟合过拟合\n4. **pattern 默认标 untested**——避免污染用户自己的 pattern 库\n5. **不直接抓视频**——下载是用户的事，避免 TOS + 反爬\n6. **可重复跑**——`--append` 加新视频，`--replace` 换账号\n7. **MIN_SAMPLES = 3**：少于 3 拆不出 pattern，拒绝继续\n\n## Refusals\n\n- 「跳过印象判断，直接拆」 → 拒绝。印象是关键 input\n- 「我只能给 1 条样本」 → 拒绝。最少 3 条\n- 「直接给我数值权重」 → 拒绝。Phase 4 只给定性信号\n- 「能不能不写 transcript 文件，只在内存里拆」 → 不行。transcript 持久化是后续 cheat-retro Phase 4b diff 的依据\n- 「帮我下载对标视频」 → 拒绝。引导用户用 yt-dlp / BBDown 等工具自己下\n\n## Integration\n\n- 上游：`/cheat-init` Phase 2.5 在 cold-start 用户时**强烈建议**跑 `/cheat-learn-from`；calibration 用户**可选**\n- 上游：`/cheat-status` 在 `benchmark_status=pending` + 距 init >24h 时持续提醒\n- 下游：`/cheat-seed` brainstorm 时读 benchmark.md → 知道用户对标方向\n- 下游：`script_patterns.md` 加新段，cheat-seed 写 draft 时按 pattern 选结构\n- 下游：`rubric_notes.md` 加 benchmark-derived signals 段，cheat-bump 时作为参考之一\n- N≥10 提示：`cheat-status` 在用户 calibration_samples ≥10 时提示\"你已有足够自己数据，benchmark 影响淡出（保留作 sanity check）\"\n\n## benchmark 何时淡出\n\n不是死磕样本数，是 **Claude 判断\"用户数据信号是否已超过 benchmark\"**：\n\n- **默认参考**：calibration_samples ≥ 10 → benchmark 影响淡出\n- **可以更早**：N=5 但用户的 (打分, 实绩) 配对里出现 ≥3 条与 benchmark pattern 不一致的——说明你账号已经走出对标的路径\n- **可以更晚**：N=15 但用户的样本都很相似（都做同一类内容），benchmark 仍有信号价值\n\n判断条件 + 默认值都在 cheat-status 触发器 #19 / cheat-seed Phase 0 里实现。\n\n**淡出后**：\n- cheat-seed brainstorm 仍读 benchmark.md，但**优先级低于用户自己的 predictions/**\n- rubric_notes 的 benchmark signals 段标 `**Status: superseded by user data**`，不删但不再主导\n- benchmark.md **不删**——保留作 sanity check（看你账号是否真的偏离对标方向太远）\n\n**任何时候用户主动**：跑 `/cheat-learn-from --replace none` 完全解除 benchmark 影响\n\n## 与其他 skill 的区别\n\n| Skill | 用途 |\n|---|---|\n| `/cheat-learn-from` | **从对标账号**导入 pattern / rubric 信号（一次性 / 偶尔追加） |\n| `/cheat-seed` | brainstorm 选题 + 写 draft（读 benchmark.md 作参考） |\n| `/cheat-trends` | 抓今天的热点（与 benchmark 无关） |\n| `/cheat-bump` | 升级 rubric（用户自己 N≥5 后用真实数据，不直接用 benchmark signals） |\n\n<!-- File: skills/cheat-migrate/SKILL.md (Tokens: ~3313 | Category: anthropic-skill) -->\n---\nname: cheat-migrate\ndescription: 把老用户的 .cheat-state.json 升级到当前 schema_version。读 migrations/registry.md 算迁移链，按顺序应用每一步迁移文件。幂等：跑两次结果一样。失败停在中间版本不前进。触发词：\"迁移\"/\"升级 state\"/\"migrate\"/\"我的 state 是老版本\"/\"schema 版本不对\"。\nargument-hint: \"[— from: <version>] [— to: <version>] [— dry-run]\"\nallowed-tools: Bash(*), Read, Write, Edit, Skill\n---\n\n# /cheat-migrate — Schema 版本迁移\n\n把用户 `.cheat-state.json` 从旧 `schema_version` 升级到 cheat-on-content 当前期望的 `LATEST_SCHEMA`。\n\n---\n\n## Overview\n\n```\n[用户：迁移 / 或 SessionStart 提示后用户跑]\n  ↓\n[Phase 0: 读 .cheat-state.json + migrations/registry.md → 确定迁移链]\n  ↓\n[Phase 1: dry-run（默认）展示迁移计划，等用户确认]\n  ↓\n[Phase 2: 备份 .cheat-state.json → .cheat-state.json.backup-<timestamp>]\n  ↓\n[Phase 3: 按顺序对每个 step 应用对应迁移文件的 HOW 段]\n  ↓\n[Phase 4: 验证升级后 state 文件能被解析 + schema_version 已更新]\n  ↓\n[Phase 5: 报告 + 提示如有备份需要清理]\n```\n\n---\n\n## Constants\n\n- **REGISTRY_PATH = `${SKILL_DIR}/../../migrations/registry.md`** — 版本链单一来源\n- **MIGRATIONS_DIR = `${SKILL_DIR}/../../migrations/`** — 迁移文件目录\n- **DRY_RUN_BY_DEFAULT = true** — 首次跑展示计划，不直接改文件\n- **BACKUP_BEFORE_WRITE = true** — 写之前必备份；备份文件保留至下次成功 init / 用户手动清理\n- **STOP_ON_STEP_FAILURE = true** — 任何 step 失败 → 停在中间版本，不前进，不回滚\n\n> 💡 调用时覆盖：`/cheat-migrate — dry-run: false` 直接执行 / `/cheat-migrate — to: 1.2` 仅升到指定版本\n\n---\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `.cheat-state.json` | 用户项目根 |\n| `migrations/registry.md` | LATEST_SCHEMA + 版本链表 |\n| `migrations/<from>-to-<to>.md` | 每步具体迁移指令 |\n\n---\n\n## Workflow\n\n### Phase 0: 确定迁移链\n\n1. 读 `.cheat-state.json` → 解析 `current_version = state.schema_version`\n2. 读 `migrations/registry.md` → 解析 `LATEST_SCHEMA` 字段（行：`LATEST_SCHEMA = \"X.Y\"`）\n3. 解析 `args.to` 覆盖（如有）；否则 target = LATEST_SCHEMA\n4. 解析 `args.from` 覆盖（罕见场景：用户的 state 文件 schema 字段坏了，强制指定起点）\n5. **状态判断**：\n   - `current_version == target` → 输出\"✅ state 已是 {target}，无需迁移\" → 退出\n   - `current_version > target`（比如用户跑了 dev 版又切回 release）→ 报错\"无法降级，请手动调整或重新 init\"\n   - `current_version < target` → 继续，从注册表查出迁移链\n6. 从注册表\"版本链\"表算出 `chain = [(from, to, file), ...]`，按顺序串起 current → target\n\n如果某一步在注册表里缺失（比如 `current_version` 不在表里）→ 报错并展示\"目前已知版本：[1.0, 1.1, ...]\"，让用户检查。\n\n### Phase 1: dry-run\n\n输出迁移计划：\n\n```\n📋 迁移计划\n\n当前版本: 1.0\n目标版本: 1.2\n将按顺序跑 2 步：\n\n  [1/2] 1.0 → 1.1（MINOR）\n       新增字段：typical_duration_seconds, target_publish_cadence_days, ...（共 12 字段）\n       删除字段：mode, prediction_complexity, bucket_scheme\n       详见: migrations/1.0-to-1.1.md\n\n  [2/2] 1.1 → 1.2（MINOR）\n       新增字段：[...]\n       详见: migrations/1.1-to-1.2.md\n\n⚠️ 备份位置: .cheat-state.json.backup-<timestamp>\n\n继续吗？回 yes 执行 / no 退出 / dry-run-detail 看每步具体改什么。\n```\n\n如 `args[\"dry-run\"] == false` 或用户回 yes → 进 Phase 2。\n\n### Phase 2: 备份\n\n```bash\ncp .cheat-state.json .cheat-state.json.backup-$(date +%s)\n```\n\n输出：\"📦 备份到 .cheat-state.json.backup-1714838400\"\n\n### Phase 3: 按顺序应用每步\n\n对 chain 里的每个 (from, to, file)：\n\n1. 输出 \"→ [N/M] 应用 {file}...\"\n2. 读 `migrations/<file>` → 找到 `## HOW (Claude steps for /cheat-migrate)` 段\n3. **按段内自然语言步骤逐项执行**——这是关键：迁移是 Claude 读 markdown 跑的，不是 python 脚本\n4. 每步完成后：\n   - 更新内存里的 state.schema_version = to\n   - **原子写**到磁盘（写 .tmp → rename）\n5. 如某步失败：\n   - 输出\"❌ {file} 第 N 步失败：{error}\"\n   - 不前进、不回滚（state 已停在前一步成功的中间版本）\n   - 提示：\"已停在 schema_version: {last_success_version}。修复后重跑 /cheat-migrate 会从这里继续\"\n   - 退出\n\n### Phase 4: 验证\n\n升完后：\n1. 读 `.cheat-state.json` → 解析 → 应能成功\n2. 检查 `schema_version == target`\n3. 检查所有\"必填字段\"非缺失（参照 [shared-references/state-management.md](../../shared-references/state-management.md) 完整 schema）\n4. 失败 → 报错\"迁移完成但验证失败：{detail}。state 文件可能不一致——查看备份恢复\"\n\n### Phase 5: 报告\n\n```\n✅ 迁移完成\n\n  从: 1.0\n  到: 1.2\n  应用步骤: 2\n\nstate 文件现在含 X 字段，全部通过验证。\n\n📦 备份保留：.cheat-state.json.backup-1714838400\n   （确认一切正常后可手动 rm；下次成功 /cheat-init 也会清理过期备份）\n\n下一步建议：\n  - 跑 /cheat-status 确认看板正常\n  - 如有 hooks 重装需求，跑 bash <skill_repo>/install.sh --reinstall-hooks\n```\n\n---\n\n## Key Rules\n\n1. **幂等**：在已升过的 state 上重跑应该立刻退出\"无需迁移\"，**不**重复应用步骤。靠对比 `current_version == target` 实现\n2. **不跳版**：1.0 → 1.3 必须按 1.0→1.1→1.2→1.3 顺序，每步独立可恢复。不允许\"直接升 1.0 → 1.3 的合并 migration\"\n3. **不静默兼容**：state 文件 schema_version 不识别 → 明确报错\"未知版本 X，最近已知版本 Y\"，不假装能继续\n4. **失败停在原地**：第 N 步失败时 schema_version 停在 N-1 已成功的版本，不回滚到迁移前。重跑能从断点继续\n5. **备份是硬约束**：写之前必有备份。即使用户跑 `--dry-run: false`，备份动作仍执行\n6. **不动 predictions / rubric / videos**：只改 `.cheat-state.json`。其他用户数据由各自 skill 负责，迁移 skill 不碰\n7. **MAJOR vs MINOR 透明**：dry-run 输出必标 (MAJOR) / (MINOR)。MAJOR 时额外提示\"老 skill 用旧字段读会出问题，迁移完不能回退到老 skill 版本\"\n\n---\n\n## Refusals\n\n- 「跳过 dry-run，立刻覆盖我的 state」 → **允许**（`--dry-run: false`），但备份仍强制执行\n- 「我的 state 损坏了 / schema_version 字段没了，能不能猜一个版本来跑」 → 允许指定 `--from: 1.0`，但要警告\"基于猜测的迁移可能导致字段错位\"\n- 「降级到旧版本（current > target）」 → 拒绝。schema 演进单向。要降级请手动 cp 历史 git 快照\n- 「合并多步迁移成一个 atomic」 → 拒绝。每步独立可恢复是设计核心\n- 「在跑 cheat-bump / cheat-predict 中途调 migrate」 → 拒绝。等其他 skill 完成再跑，避免 in_progress_session 状态被破坏\n\n---\n\n## Integration\n\n- 上游：SessionStart hook 检测 `state.schema_version != LATEST_SCHEMA` → 输出红色警告 + 建议跑 `/cheat-migrate`\n- 上游（手动）：用户 git pull 拉了新版后，看 CHANGELOG 标 BREAKING → 主动跑\n- 下游：跑完后所有其他 skill 读 state 都能拿到最新字段\n- 与 `cheat-init`：init 写新 state 时直接用 LATEST_SCHEMA，不需要走 migrate\n- 与 `install.sh --reinstall-hooks`：迁移**不**重装 hook 脚本（hook 脚本属于 skill 包代码，不属于用户 state）。这两件事解耦\n\n---\n\n## State 字段读写\n\n本 skill **写**：\n- `schema_version`（每步成功后更新）\n\n本 skill **读**：\n- 所有现有字段（取决于具体 migration 文件的 HOW 步骤）\n\n本 skill **绝不**写：\n- `calibration_samples` / `pending_retros` / `shoots` 等业务状态（这些是其他 skill 的职责）\n- 例外：迁移文件明确说\"派生新字段值时需要扫 predictions/ 算 baseline_plays\"，那是初始化新字段，不是改老字段\n\n---\n\n## Examples\n\n### 示例 1：用户从 v0.1.0 升到 v0.2.0（假设 0.2 引入 schema 1.2）\n\n```\n用户：迁移\nClaude: [跑 cheat-migrate]\n  Phase 0: current=1.1, target=1.2, chain=[(1.1, 1.2)]\n  Phase 1: dry-run 输出计划\n  用户: yes\n  Phase 2: 备份\n  Phase 3: 应用 1.1-to-1.2.md（MINOR：新增 platform_metrics_url 等字段）\n  Phase 4: 验证 OK\n  Phase 5: 报告 ✅\n```\n\n### 示例 2：用户跳了多版\n\n```\n用户：我从 v0.1.0 升到 v0.5.0，state 还是 1.0\nClaude: [跑 cheat-migrate]\n  Phase 0: current=1.0, target=1.4 (LATEST), chain=[(1.0, 1.1), (1.1, 1.2), (1.2, 1.3), (1.3, 1.4)]\n  Phase 1: dry-run 输出 4 步计划\n  ...\n```\n\n### 示例 3：迁移中途失败\n\n```\nPhase 3:\n  → [1/4] 应用 1.0-to-1.1.md ✓\n  → [2/4] 应用 1.1-to-1.2.md ✓\n  → [3/4] 应用 1.2-to-1.3.md... ❌ 失败：用户 baseline_plays 字段含非数字值，无法转 int\n  \nstate 已停在 schema_version: 1.2。\n修复 .cheat-state.json 后重跑 /cheat-migrate 会从 1.2 → 1.3 继续。\n```\n\n<!-- File: skills/cheat-persona/SKILL.md (Tokens: ~3811 | Category: anthropic-skill) -->\n---\nname: cheat-persona\ndescription: 从复盘评论数据派生 / 刷新账号的受众画像，写入 audience.md。这是和 rubric 平行的第二个派生物——rubric 答\"怎么打分\"，persona 答\"谁在看\"。cheat-seed 选题 / 写稿时读它。**audience.md 含实绩信号，cheat-score-blind 硬禁读**。触发词：\"构造受众画像\"/\"更新 persona\"/\"我的观众是谁\"/\"build persona\"/\"刷新受众画像\"/\"看看我的受众画像\"。\nargument-hint: \"[— seed-from-benchmark] [— rebuild]\"\nallowed-tools: Bash(*), Read, Write, Edit, Glob, Grep\n---\n\n# /cheat-persona — 受众画像派生\n\n从 `predictions/*.md` 复盘段的评论数据，聚类出账号真实受众画像，写入 `audience.md`。\n\n---\n\n## 核心定位\n\npersona 是和 rubric **平行**的第二个派生物，**不是 rubric 的一部分**：\n\n```\n复盘数据（评论 + 完播 + 转粉）\n   ├──→ rubric 进化（cheat-bump）   —— \"怎么打分\"\n   └──→ 受众画像（cheat-persona）    —— \"谁在看\"\n两者都喂给 cheat-seed，但用途不同\n```\n\n- **rubric**：这稿子会不会爆 → 喂 cheat-predict 打分\n- **persona**：谁会因为这条多看 3 秒 / 留评论 / 转发 → 喂 cheat-seed 选题 + 写稿\n\n**绝不混**：persona 不进打分公式。rubric 的 AB 维度（受众广度）可以参考 persona，但那是 cheat-bump 的事，不是这里。\n\n## ⚠️ 污染隔离（不可省）\n\n`audience.md` 从复盘评论派生 = **含已发布作品的实绩信号**。因此：\n\n- `audience.md` 在 [cheat-score-blind](../cheat-score-blind/SKILL.md) 的 hard refusal list 里，refusal_code `blocked_audience`\n- persona 影响 cheat-seed **写什么**（creative direction），不影响 cheat-predict **怎么打分**（blind sub-agent 永远不读 audience.md）\n- 这是干净的：persona 塑造的内容进了成稿，blind sub-agent 照成稿本身打分——没有 leak。leak 只会发生在 sub-agent 能读 audience.md \"因为这受众爱 X 所以加分\" 的情况，而它读不到\n\n## Overview\n\n```\n[用户：构造受众画像 / 更新 persona]\n  ↓\n[Phase 0: 收集数据 — 扫 predictions/*.md 复盘段评论 + benchmark.md]\n  ↓\n[Phase 1: 数据量判定 → 派生 Confidence 等级]\n  ↓\n[Phase 2: 评论聚类 — 自我认同 / 情绪寄存 / 反驳点 / 语言]\n  ↓\n[Phase 3: persona × rubric 交叉检验]\n  ↓\n[Phase 4: 写 audience.md（覆盖式重建，header 记 version + last_rebuilt）]\n  ↓\n[Phase 5: 控制台报告 + 跟上次画像的 diff]\n```\n\n## Constants\n\n- **AUDIENCE_PATH = audience.md** — 受众画像落盘位置\n- **MIN_RETROS_FOR_DATA_GROUNDED = 3** — 复盘数 ≥3 才算\"数据扎实\"（可基于评论质量软判断）\n- **MIN_COMMENTS_PER_TRAIT = 3** — 一条\"验证特征\"至少要 3 条评论证据，否则降到\"假设特征\"\n- **SEED_FROM_BENCHMARK = auto** — 无自己复盘数据但有 benchmark 时，seed 一份未验证画像\n\n> 💡 调用覆盖：`/cheat-persona — seed-from-benchmark`（强制用 benchmark seed）/ `— rebuild`（即使数据没变也重建）\n\n## Inputs\n\n| 来源 | 用途 |\n|---|---|\n| `predictions/*.md` 的 `## 复盘` 段 | **主数据源**——top 评论（带赞数）。persona 的金矿 |\n| `videos/*/report.md` | 完播 / 转粉率——薄信号，推\"留得住 vs 留不住\" |\n| `benchmark.md` | 冷启动 seed——\"看对标的人 ≈ 你想要的人\" |\n| `rubric_notes.md` | Phase 3 交叉检验用——persona 食欲 vs rubric 校准现实 |\n| `audience.md`（如已存在） | 上一版画像，用于 Phase 5 diff |\n\n## Workflow\n\n### Phase 0: 收集数据\n\n1. Glob `predictions/*.md`，对每个文件读 `## 复盘` 段（**只读复盘段——这是 channel A，本来就看实绩**）\n2. 抽取每篇的 top 评论（带赞数）+ 实绩 bucket\n3. 统计：有评论的复盘篇数 `N_retros`、评论总数 `N_comments`\n4. 读 `benchmark.md`（如存在）\n5. 读 `audience.md`（如已存在）→ 留作 Phase 5 diff\n\n### Phase 1: 数据量判定 + Confidence\n\n| 情况 | Confidence | 行为 |\n|---|---|---|\n| `N_retros == 0` 且无 benchmark | 🔴 无数据 | **不强行造**——告诉用户\"persona 需要复盘数据。先跑几篇 cheat-retro，或导 benchmark\"，退出 |\n| `N_retros == 0` 但有 benchmark | 🟠 benchmark-seed 未验证 | seed 一份 aspirational persona，**全文标\"未验证\"** |\n| `N_retros` 1-2 | 🟡 早期信号 | 能产出但特征多落\"假设\"段 |\n| `N_retros` 3-5 | 🟢 数据扎实 | 正常产出 |\n| `N_retros` ≥6 | 🔵 稳健 | 正常产出 + 可做更细的食欲分层 |\n\nConfidence 等级写进 audience.md header。\n\n### Phase 2: 评论聚类\n\n对收集到的所有评论，按四个维度聚类：\n\n1. **自我认同**——\"我也是…\" / \"这就是我\" / \"作为一个…\" 模式。统计哪类身份反复出现（\"大厂打工人\" / \"一人公司\" / \"考研党\" / ...）\n2. **情绪寄存**——观众来评论是为了什么情绪？被验证（\"说得太对了\"）/ 宣泄（\"我也好累\"）/ 抬杠（\"我不同意\"）/ 求助（\"那该怎么办\"）。统计占比\n3. **反驳点**——哪些观点引来稳定的反对声。这是 persona 边界\n4. **语言**——他们怎么说话。玩梗密度、真诚 vs 戏谑、有没有复制你的金句\n\n**聚类纪律**：\n- 一条\"验证特征\"至少 `MIN_COMMENTS_PER_TRAIT`（3）条评论证据。不够 → 降到\"假设特征\"段\n- 每条特征**必须**能引出具体评论 + 出处（哪篇 prediction 的复盘段）+ 条数\n- 发现\"反画像\"信号（你以为的受众 vs 实际评论的人不一样）→ 写\"反画像\"段\n\n### Phase 3: persona × rubric 交叉检验\n\n读 `rubric_notes.md` 当前 rubric + 校准历史。检查：\n\n- persona 说\"受众爱 X 类主题\" → rubric 校准池里 X 类主题真的 over-perform 吗？\n- 不一致 → 在 audience.md 的\"persona × rubric 交叉检验\"段 **flag 出来**\n\n诚实要求：两个派生物矛盾时**不要**强行调和。明确写\"persona 说 A，rubric 校准说 B，待下次复盘澄清\"——矛盾本身是信号。\n\n### Phase 4: 写 audience.md\n\n**覆盖式重建**（不是 append）——persona 是活文档，每次 rebuild 重写全文。但：\n\n- header 的 `Persona 版本` +1（v0 → v1 → v2）\n- header 记 `Last rebuilt` 日期 + `数据基础`（N 篇复盘 / M 条评论）+ `Confidence`\n- 文件底部\"版本历史\"段 **append 一行**：`vN — 基于 M 篇复盘 / K 条评论，主要变化：...`（这是唯一保留的历史；不搞 memo 累积——persona 是活文档不是公式）\n\n用 [templates/audience.template.md](../../templates/audience.template.md) 的结构。\n\n⚠️ **不走版本 memo / 不调跨模型审**——persona 不是高风险不可逆动作（写错了重跑一次就好），过度工程没必要。\n\n### Phase 5: 报告\n\n```\n✅ 受众画像已更新：audience.md（v2，🟢 数据扎实）\n\n数据基础：4 篇复盘 / 87 条评论\n核心画像：25-35 岁职场人，来找情绪共鸣不来找信息……\n\n跟 v1 的主要变化：\n- 新验证：\"深夜刷手机\" 场景共鸣强（v1 是假设，本次 17 条评论验证）\n- 新反画像：原以为\"学生党\"是受众，但评论里学生占比 <5% → 移到反画像\n- ⚠️ 交叉检验 flag：persona 说受众爱\"职场吐槽\"，但 rubric 校准显示职场类 composite 偏低——下次复盘留意\n\n下一步：\n- cheat-seed 选题 / 写稿时会自动参考这份画像\n- 再跑 3 篇复盘后建议再 /cheat-persona 刷新\n```\n\n## Key Rules\n\n1. **数据派生，不手写**——persona 必须来自评论聚类。用户想手动加特征 → 允许，但标 `user-asserted`（未经数据验证）\n2. **证据强制**——验证特征必须带评论条数 + 出处。无证据的进\"假设\"段\n3. **覆盖式重建**——每次 rebuild 重写 audience.md 全文，只在版本历史段 append 一行\n4. **不进打分**——persona 永远不喂 cheat-predict / cheat-score-blind。它是 cheat-seed 的 creative lens\n5. **矛盾不调和**——persona × rubric 冲突时如实 flag，不强行编一个故事\n6. **冷启动诚实**——没数据就说没数据，benchmark seed 全程标\"未验证\"\n\n## Refusals\n\n- 「我觉得我的受众就是 X，你直接写进 audience.md」 → 可以写，但标 `user-asserted` 放\"假设特征\"段，不放\"验证特征\"。persona 的价值在于数据 vs 你的幻想之间的 gap\n- 「persona 也给 cheat-predict 用，让打分更准」 → 拒绝。persona 是实绩派生物，进打分 = 把 channel B 的隔离打穿。persona 只服务 cheat-seed\n- 「跳过评论聚类，你凭感觉给我画一个」 → 拒绝。凭感觉画的是营销话术不是 persona。没评论数据就老实说\"先去复盘\"\n- 「把 persona 写进 rubric_notes.md，省一个文件」 → 拒绝。rubric_notes.md 是 blind 白名单，写 persona（实绩派生）进去 = 实绩泄漏漏洞重演（见 observation-lifecycle.md 的 leak guard）\n\n## Integration\n\n- 上游：`cheat-retro` 每完成一篇复盘 → flag \"已累计 N 篇复盘，可跑 /cheat-persona 刷新画像\"\n- 上游：`cheat-init` 创建空 audience.md 骨架；如导了 benchmark → 提示可 `/cheat-persona — seed-from-benchmark`\n- 下游：`cheat-seed` Mode A/B/C 读 audience.md 作为\"这个 persona 会在乎吗\"的镜子\n- 下游（phase 2 路线）：`cheat-recommend` persona-fit 排序；`cheat-status` persona 新鲜度 nag\n- **隔离**：`cheat-score-blind` 硬禁读 audience.md（refusal_code `blocked_audience`）\n\n## Known limitations\n\n1. **评论 ≠ 全部受众**——留评论的是受众里最活跃的一小撮（沉默大多数不在数据里）。persona 偏向\"会评论的人\"，不是\"所有看的人\"\n2. **平台评论可被污染**——水军 / 引战 / 跑题评论会进数据。cheat-persona 聚类时对明显异常值降权，但不能完全过滤\n3. **persona 滞后于真实受众变化**——画像基于过去 N 篇的评论。受众结构变了，要等新复盘累积才反映\n4. **不解决\"我想要的受众 ≠ 我实际的受众\"**——persona 只如实报告\"现在谁在看\"。想转向另一种受众是选题战略问题，cheat-persona 只提供\"现状 vs 目标\"的 gap，不替你做战略\n\n<!-- File: skills/cheat-predict/SKILL.md (Tokens: ~9096 | Category: anthropic-skill) -->\n---\nname: cheat-predict\ndescription: 给最终稿写一份 immutable 盲预测日志。这是 cheat-on-content 整个校准循环的核心动作——预测段一旦写完不可改，由 hook 强制。**自动检测**：如目标文件已有 `## 预测` / `## 预测 v1` 段（被 cheat-shoot 调用走 v2 模式），改成 append `## 预测 v2` 而非覆盖。**打分通过 Task tool 委派给 `cheat-score-blind` sub-agent**（context-isolated channel B），主 Claude review 后落盘。触发词：\"启动预测\"/\"start prediction\"/\"给这稿子打分并预测\"/\"写预测日志\"。\nargument-hint: <script-path> [— mode: v1|v2] [— prediction-file: <path>] [— skip-blind]\nallowed-tools: Bash(*), Read, Write, Edit, Glob, Task\n---\n\n# /cheat-predict — AI 主导的盲预测 + 用户 review\n\n**这个工具是\"作弊器\"——AI 帮你做判断**。所以 cheat-predict 的核心是：\n- **Claude 自己**读稿子 + 打 7 维分 + 给 bucket + 概率分布 + 反事实场景\n- 用户 **review** 后回 \"ok\" 接受，或指出哪个维度 / 哪个判断不对\n- 默认走快路径：用户直接 ok → 落盘\n- 慢路径：用户挑刺某个维度 → Claude 改 → 再 review → 直至确认\n\n不是用户从 7 维分到概率分布全部自己写，那 Claude 只剩\"格式化器\"——失去工具的核心价值。\n\n**严格遵守 [shared-references/blind-prediction-protocol.md](../../shared-references/blind-prediction-protocol.md)**——见过任何后续数据就不能写预测，只能记 reconstructed。\n完整组件清单见 [shared-references/prediction-anatomy.md](../../shared-references/prediction-anatomy.md)。\nConfidence 派生表见 [shared-references/state-management.md](../../shared-references/state-management.md)。\n\n## Overview\n\n```\n[用户：启动预测 scripts/<id>.md]\n  ↓\n[Phase 0: blind check 自检]                    ← 触犯就拒绝\n  ↓\n[Phase 0.7: 模式判定 — v1 (新建) 还是 v2 (append)]\n  ↓\n[Phase 1: 读 script + rubric + state + 派生 confidence]\n  ↓\n[Phase 2: **委派 cheat-score-blind sub-agent**（Task tool）拿 9 维盲打 + per-dim confidence]\n  ↓\n[Phase 2.5: 主 Claude 对 blind 输出做 review — 若任意维度 |delta| ≥ 2 vs 主估，弹给用户裁定]\n  ↓\n[Phase 3: **Claude 自己**找锚点对比]\n  ↓\n[Phase 4: **Claude 自己**给 bucket + 概率分布 + 中枢]   ← confidence 低时分布更平\n  ↓\n[Phase 5: **Claude 自己**写反事实场景 + 关键校准假设]\n  ↓\n[Phase 5.5: **用户 review**——展示完整草拟版，等用户 \"ok\" 或挑刺]\n  ↓\n   ├─ \"ok\" → Phase 6 落盘\n   └─ \"X 维度应该 Y 不是 Z\" → Claude 改 → 再 review → 循环\n  ↓\n[Phase 6: 落盘 — v1 写新文件 / v2 append 到现有文件 ## 复盘 之前]\n  ↓\n[Phase 7: 更新 state.in_progress_session]\n```\n\n## Constants\n\n- **SCRIPTS_DIR = scripts/** — 草稿源目录\n- **PREDICTION_DIR = predictions/** — 落盘目录\n- **BLIND_CHECK = strict** — strict（默认）/ lenient（仅警告，不推荐）—— 跟 [blind-prediction-protocol.md](../../shared-references/blind-prediction-protocol.md) \"见过数据\"边界相关\n- **BLIND_SCORING = on**（默认）/ off —— 是否走 [cheat-score-blind](../cheat-score-blind/SKILL.md) sub-agent。off 等价于 `--skip-blind` flag，标 `last_prediction_self_scored: true` 给 cheat-status 警告\n- **DISAGREEMENT_THRESHOLD = 2** —— blind 与主 Claude 自评的单维度差异 |Δ| ≥ 此值 → Phase 2.5 弹用户裁定\n- **BUCKET_PRESET = auto** — 自动派生：有 baseline_plays → 按 baseline × {0.3 / 1 / 3 / 10 / 30}；无 baseline → 平台通用默认\n- **MIN_ANCHORS = 2** — 锚点对比期望 2 个；不够时显式标\"锚点 N/A\"段（不删段，不省略）\n\n> 💡 调用时覆盖：`/cheat-predict scripts/<id>.md — BLIND_CHECK: lenient` / `--skip-blind`（都不推荐）\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<video-folder-path>` 或 `<script-path>` | 用户参数；缺失则询问 |\n| `rubric_notes.md` | 用户项目根 |\n| `.cheat-state.json` | 状态文件 |\n| `predictions/*.md`（可选） | 历史预测，作为锚点 |\n\n### 入参解析（Phase 0.5，在 blind check 之前）\n\n用户给的路径**应该是** `scripts/<date>_<id>_<short>.md`。如不在 scripts/ 下：\n\n| 形态 | 处理 |\n|---|---|\n| `scripts/<date>_<id>_<short>.md` | 标准路径，直接用 |\n| `<id>` 或 `<short>` 简写 | glob `scripts/*_<id>_*.md` 或 `scripts/*<short>*.md` 找匹配 |\n| 任意外部 .md 文件（如 `~/Desktop/my-draft.md`） | **警告 + 询问**：\"建议把稿子放到 scripts/<date>_<id>_<short>.md 让 cheat-on-content 管理。要我帮你 cp 过去并算 id 吗？\"用户同意 → 建标准路径再继续 |\n| `videos/<id>/` 路径（user 误以为视频文件夹存稿子）| 提示\"video folder 是拍后才建的——pre-shoot 草稿在 scripts/。你要 predict 哪份稿子？\" |\n\n如 scripts/<id>.md 不存在 → 报错并询问\"你想 predict 的稿子在哪？\"\n\n## Workflow\n\n### Phase 0: Blind check 自检（**最关键**，触犯立即终止）\n\n按 [blind-prediction-protocol.md](../../shared-references/blind-prediction-protocol.md) 的\"子 skill 必须做的检查清单\"执行：\n\n1. 询问用户该作品当前发布状态：\n   - 未发 → 通过\n   - 已发 < `RETRO_WINDOW_DAYS` 天 → 询问\"你看过任何后续数据吗（播放/点赞/评论）？\"\n     - 用户回答\"没看过\" → 通过，标记 `published_before_prediction: true` + `blind_status: confirmed_no_data_seen`\n     - 用户回答含糊 → 视为\"已看\"，按下一项处理\n   - 已发 ≥ `RETRO_WINDOW_DAYS` 天 → **立即拒绝写\"预测\"**，建议改用 `_redo.md` 路径记 reconstructed retrospective\n\n2. 自检对话历史里是否含播放/阅读/点赞/评论/转发等字眼的实际数字 → 命中则视为已见数据，按上面 strict 模式处理\n\n3. `BLIND_CHECK=lenient` 模式：仅警告 + 强制在文件头标注 `**Reconstructed retrospective — NOT a blind prediction**`，但仍允许继续\n\n通过 → 进入 Phase 0.7。\n\n### Phase 0.7: 模式判定（v1 vs v2）\n\n判定本次是新建预测（v1）还是对既有预测的 v2 追加（拍后改稿场景）。\n\n**显式参数优先**：用户/调用方传 `— mode: v2` + `— prediction-file: <path>` → 直接 v2 模式。\n\n**自动检测**（无显式参数）：\n1. 推断目标 prediction 路径：`predictions/<同 scripts/<id> 命名>.md`\n2. 读该路径：\n   - 不存在 → **v1 模式**，进入 Phase 1\n   - 存在但只有空 `## 复盘`（无任何 `## 预测...` 段）→ **v1 模式**（异常状态，覆盖警告 + 进 Phase 1）\n   - 存在且含 `## 预测` 或 `## 预测 v1` 段 → **v2 模式**\n\n**v2 模式额外动作**：\n- 比较输入 script（最终拍摄稿）与 `## 预测` 段引用的原 `Script Hash`\n- 如完全一致（hash 同）→ 警告\"稿子没改，是否真要写 v2？\"——用户确认才继续；不确认则退出\n- 如不同 → 算 diff 概要（行数 / 字数 / 结构变化）→ Phase 5.5 review 时展示给用户\n- 标记 `prediction_basis = \"post_shoot_pre_publish\"`（v1 默认 `pre_shoot`）\n\n### Phase 1: 读最终稿 + rubric + state + 派生 confidence\n\n1. 按 Phase 0.5 解析后的路径，读 `scripts/<id>.md` 全文\n2. 计算 `script_hash` = sha256(script 内容)[:12] → header 用\n3. 读 `rubric_notes.md`，识别当前公式 + 维度（同 cheat-score Phase 2）\n4. 读 `.cheat-state.json` 拿 `rubric_version`、`content_form`、`calibration_samples`、`typical_duration_seconds`、`baseline_plays`\n5. **从 `calibration_samples` 派生 confidence 等级**（按 [state-management.md confidence 表](../../shared-references/state-management.md)）→ 后续写入 prediction header\n6. 询问用户：\"这是你打算实际拍摄发布的最终稿吗？还是会再改？\"——必须是最终稿\n7. 如果稿子字数与 `typical_duration_seconds` 派生范围严重不符（差 >50%）→ 提示用户：\"这条稿子 N 字，按你设的典型时长（X 分钟）应该是 M-K 字。是临时改了时长，还是稿子需要砍/补？\"\n\n### Phase 2: 委派 cheat-score-blind sub-agent 拿盲打分\n\n**BLIND_SCORING=on**（默认）—— 主 Claude 不再 inline 打分。通过 Task tool spawn `cheat-score-blind`，让一个 context-isolated 的 sub-agent 只看 script + rubric_notes.md 给出 N 维分。\n\n详见 [cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md) 的\"主 Claude 调用契约\"段。**Task prompt 必须精简**：\n\n```\nSpawn cheat-score-blind sub-agent.\n\nInput:\n  script_path: <Phase 0.5 解析出的 scripts/<id>.md>\n  rubric_notes_path: rubric_notes.md\n\nTask: 按 rubric_notes 当前公式给上面 script 打分。返回严格 JSON（见 cheat-score-blind SKILL.md Phase 2 schema）。\n不要读 state file / predictions/ / videos/ 任何其他文件。\n不要询问用户 —— 你没有用户。\n```\n\n**调用前自检**：把 Task prompt 串过 `grep -Ei '播放|阅读|点赞|评论数|实际|retro|复盘|实绩|w$|万$'`——命中 → 改 prompt 重发。\n\n**主 Claude 自己也内心估一份**（不发 sub-agent）——纯为 Phase 2.5 disagreement 检测，**不落盘**、**不替代 sub-agent 输出**。这个估值代表\"如果我没用 sub-agent，我会打多少\"，是 contamination 的客观指标。\n\n**沙盒 escape**：`BLIND_SCORING=off` 或 `--skip-blind` —— 主 Claude 自己打 7 维。state 立刻标 `last_prediction_self_scored: true` + `last_self_scored_at: <ISO>`，cheat-status 持续提示警告。仅用于：\n- Task tool 不可用（开发环境 / 离线）\n- 用户主动 audit 主 Claude 的 inline 判分能力（极少数）\n\n按当前公式算 composite——**用 sub-agent 回传的 dim 分**，不用主 Claude 自估。\n\n### Phase 2.5: Blind 输出 review + disagreement detection\n\n拿到 sub-agent JSON 后，主 Claude 必做的事：\n\n1. **JSON validity check**：`python3 -c \"import json; json.loads(...)\"` 应能解析；不能解析 → 主 Claude 重发 Task（最多 3 次重试），仍败 → abort，向用户报告\n2. **Contamination check**：`self_check.any_contamination_signal == true` → 警告用户\"sub-agent 自报疑似 contamination\"，但仍接受打分（confidence 降一档）\n3. **Refusal check**：`refusal != null` → 按 [cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md) Phase 2 的处理表对应路径\n4. **Disagreement detection**（核心）：\n   - 主 Claude 内心估一份 N 维分（Phase 2 末尾的\"自估\"）\n   - 对每个维度算 `delta = |主估 - blind|`\n   - 任何维度 `delta >= DISAGREEMENT_THRESHOLD`（默认 2） → **弹给用户裁定**\n\n弹裁定 UX：\n\n```\n⚠️  blind sub-agent 跟主 Claude 在某些维度差异较大：\n\n| 维度 | blind (sub) | 主 Claude 自估 | delta | sub-agent 理由 |\n|---|---|---|---|---|\n| ER | 5 | 3 | 2 | \"PPT加油猫猫开头—具象画面强\" |\n| AB | 2 | 4 | 2 | \"一人公司视角，受众窄\" |\n\n谁更准？\n  a) 信 sub-agent（隔离打分，但同 Claude 模型）\n  b) 信主 Claude 自估（有更多对话上下文，可能是 contamination）\n  c) 我自己定（你直接给分）\n\n回 a / b / c <你的分数>\n```\n\n用户选：\n- a → 用 sub-agent 全套分进 Phase 3\n- b → 用主 Claude 自估全套分（视为有意接受 contamination）→ 强制标 `last_prediction_self_scored: true`\n- c → 用户给的分覆盖该维度，其他维度仍走 sub-agent → 记到 `User Override`\n\n**所有 delta** —— 即使全 < THRESHOLD —— 都记录到 prediction header 的 `BlindScore Disagreement` 字段（详见 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 组件 1）。delta=0 也要记录。\n\n### Phase 3: 锚点对比\n\n**所有阶段都跑此 phase**——锚点不够时显式标 N/A，不删段。\n\n1. Glob `predictions/*.md`，读每个文件 header（提取 composite、实绩 bucket、duration_seconds）。**注意排除 reconstructed predictions**（标记 \"Reconstructed\" 的不算锚点）\n2. **优先**找同时长样本（`Target Duration (s)` 与本次差 ±20% 内）\n3. 在同时长（或全部）池里，找 2-4 个 composite 与本次预测 ±0.5 范围内的样本\n4. **如果池子太小**（同时长 < 2 个 + 全部 < 2 个）→ 输出\"锚点对比 N/A 段\"（参考 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 组件 5）—— 仍写这段，告诉读者锚点为何缺\n5. 列对照表；如跨时长，每行额外列\"时长 vs 本次\"列\n6. **关键诊断**：如果某个锚点的 composite 几乎相同但实绩差异 ≥3x → 说明 rubric 没捕获关键维度。**在文件里明确标注**作为新观察的种子\n\n> 为什么按时长筛锚点：4 分钟视频 5w 播放 vs 1 分钟视频 5w 播放完全不是一回事——长视频每秒扛了更多注意力损失。跨时长锚点容易得出虚假结论。\n\n### Phase 4: Bucket + 概率分布 + 中枢\n\n**所有阶段都写**——confidence 低时分布**更平**，不是省略。\n\n1. 从 `starter-rubrics/<content_form>.md` 读默认 bucket 边界（除非用户在 rubric_notes.md 自定义了）\n2. 选择最可能的 bucket（headline call）\n3. **必须**给出所有 bucket 的概率分布——加起来 100%\n4. **必须**给出该 bucket 内的\"中枢\"点估计\n\n**反诚实陷阱**：如果你给一个 bucket 95% 概率，下次预测错了你没法说\"我其实不太确定\"。**真实的概率分布通常在 headline bucket 是 40-65%**，剩下 ≥35% 散布在邻近 buckets。\n\n### Phase 5: 反事实场景 + 关键校准假设\n\n**所有阶段都写**——校准池小时关键校准假设可能没有合适对照样本，那就写\"无可对照样本——仍写下我对这次的核心赌注\"+ 1-2 条这次想测的事。\n\n**反事实场景**（4 段，每段对应一个可能的 bucket，写\"如果落在这里，意味着什么 rubric 假设被验证 / 推翻\"）：参考 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 组件 6。\n\n**关键校准假设**（强烈推荐）：\n- 找一个对照样本（最好是上一篇预测）\n- 明确写\"我押本篇 vs 对照 = X 倍\"\n- 写\"如果反过来 / 差距 < N → 哪个 rubric 假设被推翻\"\n\n如 `REQUIRE_HYPOTHESIS=required` → 缺失则不允许落盘。\n\n### Phase 5.5: 用户 review（**核心 — 决定写什么进文件**）\n\nPhase 2-5 全部在内存里做完后，**一次性展示完整草拟版**给用户：\n\n```\n我的预测草稿（写文件前 review）：\n\n📊 7 维分（v0 / v2 / 等当前 rubric）：\n| 维度 | 分 | 理由 |\n|---|---|---|\n| ER | 5 | \"PPT 加油猫猫 + 老板看到 + 大脑空白\"——情感重 |\n| HP | 5 | 开头\"PPT 第七页大屏中央 一只加油猫猫\"具体反差强 |\n| QL | 5 | \"加油猫猫救了我一命\"双金句 |\n| NA | 4 | 单一时间线 + 反思，清晰但不复杂 |\n| AB | 4 | 一人公司题但 AI 焦虑普适 |\n| SR | 3 | AI 焦虑是议题但不是热点对峙 |\n| SAT | 2 | 共情调性，几乎无讽刺 |\n→ composite ≈ 8.00\n\n🎯 押 bucket：30-100w，中枢 ~60w\n   概率分布: <5w 5% / 5-30w 22% / **30-100w 50%** / 100-150w 18% / >150w 5%\n   confidence: 🟢 中（基于 8 个校准样本，中枢 ±25%）\n\n🔍 锚点对比：\n| 对照 | composite | 实绩 | 异同 |\n|---|---|---|---|\n| ... | ... | ... | ... |\n\n🤔 反事实：\n   如果 >100w → 验证 ER 主导假设强化\n   如果 30-100w → 基准线 ok\n   如果 <30w → 推翻 \"AI 焦虑普适\"，AB 偏乐观\n\n🎲 关键校准假设：本篇 vs [对照] 押 1.5x\n\n——————————————————————————————\n\n回 \"ok\" 我直接落盘，\n或指出哪些维度 / 判断不对（如 \"AB 给 3，太乐观\" / \"中枢应该 30w 不是 60w\"）。\n```\n\n用户三种回应：\n\n1. **\"ok\"** / \"可以\" / \"继续\" → 直接 Phase 6 落盘，header 标 `Scored By: claude`\n2. **\"X 不对，应该 Y\"** → Claude 改对应字段（不光改值，要更新 composite + 概率分布等连锁影响），重新展示 → 循环回 Phase 5.5\n3. **\"全部重做\"** → Phase 2-5 重跑（罕见，通常是 Claude 严重误判稿子调性）\n\n**用户挑刺的字段**记录到 prediction header 的 `User Override` 段（Phase 6 写入）：\n- 哪个维度 / 哪个数字被覆盖\n- AI 原值 vs 用户改后的值\n\n复盘时这个字段帮诊断：\n- 用户每次都 ok（claude 一致）→ 没有用户偏见污染\n- 用户经常覆盖某维度 → Claude 在该维度系统性偏离用户实际感觉\n- 覆盖维度被实绩验证 → 用户直觉准 → rubric 可能漏了什么\n\n**用户挑刺的纪律**：\n- 用户**只能改字段值**，不能在 review 阶段塞新理由让 Claude 重写整段——那是把 Claude 当代笔\n- 改完 composite / 概率分布 / 锚点不一致 → Claude 自动连锁更新（不是用户算）\n\n### Phase 6: 落盘\n\n#### Phase 6a: v1 模式（新建预测文件）\n\n文件名约定（[blind-prediction-protocol.md](../../shared-references/blind-prediction-protocol.md) 的\"文件名约定\"段）：\n```\npredictions/YYYY-MM-DD_<id>_<short-title>.md\n```\n- `YYYY-MM-DD`：今天日期（预测写下的日期）\n- `<id>`：12 位 hash，对稿子全文做 sha256 取前 12 位（稳定 ID，重写不变）\n- `<short-title>`：3-8 字，去标点\n\n**第一段标题写 `## 预测 v1`**（不再写裸 `## 预测`——为将来可能的 v2 留 schema 一致性。老用户的 legacy `## 预测` 文件不动，hook 都识别）。\n\n**header 必填字段**：\n- `Article ID`（与 scripts/<id>.md 同 id）\n- `Script Path`（指向 scripts/<id>.md）\n- `Script Hash`（Phase 1 算出的）\n- `Calibration Samples` + `Confidence`（从 state 派生）\n- `Prediction Basis`：`pre_shoot`（v1 默认）\n- `Scored By`：`claude` / `claude+user_override`\n- **`BlindScored By`**：`subagent-v1`（Phase 2 默认）/ `main-claude-self`（`--skip-blind` 时） / `mixed`（Phase 2.5 用户裁定 b/c）\n- **`BlindScore Disagreement`**：JSON 字段列表，每维度 `{dim, blind, self, delta, decided_as}`，**所有维度必记**（即使 delta=0）\n- `User Override`（如有覆盖）：列出哪些字段被用户改了\n- 其他见 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 组件 1\n\n留一个空的 `## 复盘` 段：\n```markdown\n## 复盘\n\n（待填——T+RETRO_WINDOW_DAYS 天后跑 /cheat-retro <对应 video folder>）\n```\n\n#### Phase 6b: v2 模式（append 到既有文件）\n\n**绝不**用 Write 覆盖文件——会被 immutability hook 拦。用 **Edit** 在 `## 复盘` 之前插入 `## 预测 v2` 段：\n\n```python\n# 伪代码\nedit_old = \"## 复盘\\n\"   # 单独一行，确保 hook awk 识别为 v1 段的边界\nedit_new = \"\"\"## 预测 v2 (replaces v1; basis=post_shoot_pre_publish)\n\n**Diff vs v1**: 改了 N 行（X→Y%），主要变化：[摘要]\n**重判触发**: cheat-shoot 检测稿子改动 ≥30%\n**Script Hash (v2)**: <新稿子 hash>\n\n[7 组件 — 与 v1 同 anatomy]\n\n---\n\n## 复盘\n\"\"\"\n```\n\nv1 段**不动**。v2 段头部明确写\"replaces v1\"——读者一眼知道哪段是有效预测。\n\ncheat-retro 复盘时按\"读最后一个 `## 预测 vN`\"逻辑，自然取到 v2 算偏差。\n\n#### 共用规则\n\n**所有阶段都用统一完整版格式**（参考 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) \"完整结构总览\"）。confidence 低不缩格式，只让 header 标 confidence 等级 + 锚点对比段写\"N/A 解释\" + 概率分布更平。\n\n写文件**前**自检 7 个组件齐全（缺锚点 / 关键校准假设 → 写\"N/A 解释段\"，不删段）。\n\n### Phase 7: 更新 state file\n\n更新 `.cheat-state.json`：\n```json\n{\n  \"in_progress_session\": {\n    \"type\": \"prediction\",\n    \"file\": \"predictions/YYYY-MM-DD_<id>_<short>.md\",\n    \"video_folder\": \"videos/YYYY-MM-DD_<id>_<short>/\",\n    \"started_at\": \"<ISO timestamp>\",\n    \"rubric_version\": \"<v0/v2/...>\"\n  },\n  \"last_prediction_self_scored\": <true 仅当 --skip-blind / Phase 2.5 选 b>,\n  \"last_self_scored_at\": <ISO 当 last_prediction_self_scored=true 时>\n}\n```\n\n`video_folder` 为 null 表示用户跑的是裸 .md 文件，没建 video folder。\n\n`in_progress_session` 在 `cheat-publish` 触发时清除。如果用户预测后从未 publish（弃稿），下次 `/cheat-init` 或 `/cheat-status` 检测到陈旧 in_progress 会询问是否清理。\n\n`last_prediction_self_scored`：\n- `true` 仅当本次预测走了 `--skip-blind` 或 Phase 2.5 用户选了 b（信主 Claude 自估）\n- 一旦 `true` → cheat-status 持续 nag：\"上次预测没走 blind sub-agent，已 N 天\"——直到下次 normal `cheat-predict`（走 sub-agent）触发后才清回 `false`\n- `last_self_scored_at` 跟随更新；下次 `cheat-predict` 走 sub-agent → 这两个字段一起被重置\n\n### Phase 8: 控制台总结\n\n**Cold-start-simple 模式**：\n\n```\n✅ 预测落盘（cold-start 简化版）：predictions/2026-05-04_a3f2c1d4e5b6_停止期待.md\n\n7 维打分：ER5 / HP5 / QL4 / NA3 / AB5 / SR2 / SAT4\n方向押注：比上一篇明显好（ER+HP 双 5）\n对比对象：N/A（这是第 1 篇）\n\n⚠️  ## 预测段已 immutable（hook 锁定）。\n⚠️  这是 cold-start 期简化版——没有 bucket 数字。前 5 篇都这样。\n   第 5 篇复盘后会自动解锁完整预测（bucket / 概率 / 锚点 / 反事实）。\n\n进度：第 N 篇 / 共 5 篇 cold-start 期\n\n下一步：\n- 发布后 → \"已发布 https://...\"\n- T+3 天 → \"复盘 predictions/2026-05-04_...\"\n```\n\n**Complete 模式**：\n\n```\n✅ 预测落盘：predictions/2026-05-04_a3f2c1d4e5b6_停止期待.md\n\nbucket 押注：30-100w（中枢 50w）\n关键校准假设：本篇 vs 谁问你了 = 1.5-2x\n\n⚠️  ## 预测 段已 immutable（hook 锁定）。\n⚠️  你不能再向我\"透露\"这条作品的播放数据，否则下次复盘的盲度声明失效。\n   如果你不小心看到了，告诉我——我会在文件里补一个 integrity warning。\n\n下一步：\n- 发布后 → \"已发布 https://...\"\n- T+3 天 → \"复盘 predictions/2026-05-04_...\"\n```\n\n## Key Rules\n\n1. **blind check 是硬门槛**。BLIND_CHECK=strict 模式下，触犯即终止，不允许\"软处理\"。lenient 仅用于演练\n2. **整数维度分**。同 cheat-score\n3. **概率分布 = 100%**。不允许 95% + 8%；要么诚实给 50% + 30% + 15% + 5%，要么承认不知道\n4. **必须有 `## 复盘` 占位空段**——否则 hook 不知道哪里是 immutable 边界\n5. **不允许\"先写文件再讨论分数\"**——文件落盘后预测段就锁了；讨论必须发生在 Phase 2 之后、Phase 6 之前\n6. **id 是稿子 hash，不是时间戳**——重写 _redo.md 时 id 不变，便于跨文件追溯\n\n## Refusals\n\n- 「我已经看过播放数据了，但你假装没看到给我做个预测」 → 拒绝。BLIND_CHECK=strict 直接终止\n- 「我把预测段先写一版，等数据出来再调」 → 拒绝。这是把 immutable 协议反着用\n- 「我改稿了想让你覆盖之前的预测，不要 v2 段」 → 拒绝。v1 是档案，v2 才是当前判断——append 不覆盖。即使你\"主观感觉 v1 完全错了\"，git history 里 v1 还能查，但工作目录里 v1 必须留\n- 「跳过反事实场景，太麻烦」 → 拒绝。反事实是复盘诊断的依据，缺它复盘退化为\"准 / 不准\"\n- 「可不可以只写 bucket，不写概率分布」 → 拒绝。概率分布是逼你诚实的工具\n- 「这次先用 lenient 模式，下次再 strict」 → 询问原因。如果是测试 / 演练 → 允许且文件明确标 reconstructed；如果是想偷懒 → 拒绝\n- 「sub-agent 太慢，你直接打就行」 → 用 `--skip-blind` flag 显式声明。**不接受**主 Claude 自作主张跳过 sub-agent。flag 触发 state.last_prediction_self_scored=true，cheat-status 持续提示直到下次 normal 调用清除\n- 「Phase 2.5 选 b 后我不想标 last_prediction_self_scored=true」 → 拒绝。选 b 等于\"我有意接受主 Claude 自评\"——必须留下污染追踪轨迹\n- 「我是 cold-start 但想跑完整版预测，给我 bucket 数字」 → 拒绝。前 5 篇 bucket 数字是 false precision，给反而误导。等第 5 篇复盘后 cheat-status 会主动提示解锁。如果用户确实想要数字（罕见，自我教育目的）→ 允许但在文件头醒目标 `**Numerical predictions in cold-start are NOT predictive — for self-education only**`\n\n## Integration\n\n- 前置：`/cheat-init` 必须完成 + `rubric_notes.md` 存在\n- 上游可选：`/cheat-score` 反复尝试不同稿子版本\n- 下游：`/cheat-publish`（发布登记）→ `/cheat-retro`（复盘）→ 累计 ≥ MIN_SAMPLES 后 `/cheat-bump`\n- hook 依赖：`hooks/prediction-immutability.sh` 必须已安装在用户 `.claude/settings.json`，否则 immutability 仅靠 SKILL.md 自律——`cheat-status` 会持续提示\n\n<!-- File: skills/cheat-publish/SKILL.md (Tokens: ~2573 | Category: anthropic-skill) -->\n---\nname: cheat-publish\ndescription: 登记一篇内容已发布，把 URL/平台 ID/发布时间写入对应预测文件 header 和 state file。这是一个轻量动作——只更新元数据，**不动预测段任何字符**。触发词：\"已发布\"/\"I shipped\"/\"发布链接是 X\"/\"刚发完 [url]\"/\"publish registered\"。\nargument-hint: <prediction-file-or-url> [— platform: youtube|bilibili|douyin|...]\nallowed-tools: Bash(*), Read, Edit, Glob\n---\n\n# /cheat-publish — 发布登记\n\n把作品的发布元数据（URL、发布时间、平台）补到预测文件 header 与 state file。**禁止改预测段**——hook 会拦。\n\n## Overview\n\n```\n[用户：已发布 https://...]\n  ↓\n[Phase 0: 找到对应的预测文件]   ← 通过 in_progress_session 或匹配\n  ↓\n[Phase 1: 解析 URL → 平台/发布时间]\n  ↓\n[Phase 2: 更新 prediction 文件 header（仅 metadata 段）]\n  ↓\n[Phase 3: 更新 .cheat-state.json，清除 in_progress_session]\n```\n\n## Constants\n\n- **AUTO_DETECT_PLATFORM = true** — 从 URL 模式自动识别平台\n- **VERIFY_BLIND = true** — 提醒用户：从此刻起看到任何后续数据都会破坏盲度声明的诚信\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<prediction-file>` 或 URL | 用户参数；缺失则用 `.cheat-state.json` 的 `in_progress_session.file` |\n| `.cheat-state.json` | 用户项目根 |\n\n## Workflow\n\n### Phase 0: 找到对应的预测文件\n\n按优先级：\n1. 用户参数明确给了 prediction 文件路径 → 用它\n2. 用户参数只给了 URL → 读 `.cheat-state.json` 的 `in_progress_session.file`\n3. 都没有 → 列出 `predictions/*.md` 中 header 没填 `published_at` 的文件，让用户选\n\n**警告路径**：若 `in_progress_session.file` 与用户给的 URL 时间差超过 14 天 → 提示\"这个预测写于很久之前，确认是这篇？\"\n\n### Phase 1: 解析平台\n\n`AUTO_DETECT_PLATFORM=true` 时按 URL 模式：\n\n| URL 模式 | 平台 |\n|---|---|\n| `youtube.com/*` `youtu.be/*` | youtube |\n| `bilibili.com/*` `b23.tv/*` | bilibili |\n| `douyin.com/*` `iesdouyin.com/*` `v.douyin.com/*` | douyin |\n| `xiaohongshu.com/*` `xhslink.com/*` | xhs |\n| `mp.weixin.qq.com/*` | wechat |\n| `substack.com/*` `*.substack.com/*` | substack |\n| `medium.com/*` `*.medium.com/*` | medium |\n| `twitter.com/*` `x.com/*` | twitter |\n| 其他 | unknown — 询问用户 |\n\n发布时间获取：\n- 不强求自动抓——绝大多数平台需要登录态\n- 询问用户：\"发布时间是？（默认：现在）\" → 接受 ISO 8601 或自然语言（\"今天 14:30\" / \"20 分钟前\"）\n- 解析失败 → 用 now()\n\n### Phase 2: 更新 prediction 文件 header\n\n**绝不**触碰 `## 预测` 段及之后。只动文件最顶部的 metadata 块。\n\n读文件，定位到 metadata 块（在第一个 `##` 之前的所有行）。检查是否已有这些字段——有则警告\"已登记过\"并询问是否覆盖；无则追加：\n\n```markdown\n**Published at**: 2026-05-04T14:32:00+08:00\n**Platform**: douyin\n**URL**: https://v.douyin.com/abc123\n**Video Folder**: videos/2026-05-04_a3f2c1d4_停止期待/\n**Aweme ID**: 7234567890123456789  (douyin / 视频号 等需要的 platform-specific ID)\n```\n\n**关于 platform-specific ID**：\n- 抖音：从 URL 短链 resolve 后提取 `aweme_id`（v.douyin.com → 重定向后含 modal_id 或 item_id 参数）\n- B 站：BV 号\n- 小红书：note_id\n- YouTube：v= 参数后的 video_id\n\n如果用户给的是分享短链（无法立刻 resolve）→ 标 `Aweme ID: pending`，下次 `/cheat-retro` 时由 adapter 解析。\n\n**video folder 处理**：到 cheat-publish 这一步，对应的 `videos/<id>/` 目录**应该已经由 cheat-shoot 创建**（含 script.md）。\n\n- 如 video folder 不存在 → 警告\"你跳过了 cheat-shoot？建议先跑 cheat-shoot 把拍摄稿登记进 video folder 再发\"，**询问用户是否跳过登记直接发**：\n  - 是 → 自动建一个 video folder（fallback），但不询问稿子一致性，标 `ad_hoc_publish: true`\n  - 否 → 让用户先跑 cheat-shoot 再回来 publish\n\n用 Edit 工具（不是 Write 重写整个文件）。\n\n**hook 行为预期**：因为只动 metadata 段（在 `## 预测` 之前），immutability hook 应放行。如果 hook 误拦 → 报告 bug，**不要绕过 hook**。\n\n### Phase 3: 更新 state file\n\n```json\n{\n  \"in_progress_session\": null,\n  \"last_published_at\": \"<ISO timestamp>\",\n  \"last_published_file\": \"predictions/<filename>\",\n  \"last_published_video_folder\": \"videos/<...>/\",\n  \"last_published_platform_id\": \"<aweme_id 或 BV 号 等>\",\n  \"pending_retros\": [\n    \"predictions/<filename>\"\n  ],\n  \"shoots\": [\n    // 移除 video_folder 匹配本次发布的项；buffer -1\n  ]\n}\n```\n\n**`shoots` 队列处理**（buffer 跟踪关键）：\n1. 读 state.shoots[]\n2. 找 `video_folder == 本次发布的 video_folder` 的项 → 移除\n3. 如果没找到 → 警告\"buffer 队列里没有这条视频。是直接发布没经过 /cheat-shoot 吗？\"——不阻塞，但提示用户下次走 /cheat-shoot 让 buffer 跟踪准确\n\n`last_published_platform_id` 是 cheat-retro 调 adapter 时的输入——如 douyin-session 需要 aweme_id 直接抓数据。\n\n`pending_retros` 是待复盘列表——`cheat-status` 会基于这个列表 + RETRO_WINDOW_DAYS 显示\"今天该复盘哪些\"。\n\n### Phase 4: 提醒 + 下一步 + buffer 状态\n\n```\n✅ 登记完成：predictions/2026-05-04_a3f2c1d4e5b6_停止期待.md\n   - Published at: 2026-05-04 14:32\n   - Platform: douyin\n   - URL: https://v.douyin.com/abc123\n\n📦 Buffer：N 篇（颜色 + 含义）\n   按你的 cadence（X）= N×X 天 buffer\n   [如颜色变了，提示\"现在该去拍/暂停拍\"]\n\n⚠️  从此刻起，你看到任何关于这条作品的播放/点赞/评论数据\n    都会破坏盲度声明的诚信。如果不小心看到，告诉我——\n    我会在文件里追加一个 integrity warning。\n\n📅 计划复盘：T+3d，约 2026-05-07\n   到时间说：\"复盘 predictions/2026-05-04_...\"\n```\n\nBuffer 颜色由 [shared-references/cadence-protocol.md](../../shared-references/cadence-protocol.md) 派生。如本次发布让 buffer 跌入红色（断更风险）→ 高亮警告\"今天必须再拍 ≥1 条\"。\n\n## Key Rules\n\n1. **不动预测段**。即使是修复笔误，也不允许在 publish 时改预测段\n2. **不抓数据**。publish 是登记动作，不是数据回收（那是 cheat-retro 的活）\n3. **state 字段名固定**。`pending_retros` / `last_published_at` 是其他子 skill（特别是 cheat-status / cheat-retro）依赖的契约\n4. **平台未知不强报**。无法识别 → 询问用户，允许 `platform: other` 作为兜底\n5. **重复登记需明示**。已有 published_at → 询问\"覆盖？\"，绝不静默覆盖\n\n## Refusals\n\n- 「我顺手把预测段也改一下」 → 拒绝。请走 `_redo.md` 路径\n- 「URL 我等会儿补，先把发布时间记上」 → 允许：URL 字段可后续追加；published_at + platform 必填\n- 「跳过 metadata 更新，直接清 in_progress_session」 → 拒绝。元数据是复盘时的关键上下文（特别是 platform 决定数据回收用哪个 adapter）\n\n## Integration\n\n- 上游：`/cheat-predict`（写出 prediction 文件并设 in_progress_session）\n- 下游：T+RETRO_WINDOW_DAYS 后 → `/cheat-retro`\n- `cheat-status` 用 `pending_retros` 字段计算\"今天该复盘哪些\"\n- 平台字段被 `cheat-retro` 用来路由到对应的 perf-data adapter（manual-paste / youtube-data-api / 等）\n\n<!-- File: skills/cheat-recommend/SKILL.md (Tokens: ~3901 | Category: anthropic-skill) -->\n---\nname: cheat-recommend\ndescription: 从 candidates.md 里按当前 rubric 排序推荐 top N 选题，每条带 composite + 一句 rationale + 锚点对比。**candidates 不存在时给引导而非报错**。触发词：\"推荐选题\"/\"next topic\"/\"下一篇做什么\"/\"recommend topics\"/\"挑一个选题\"。\nargument-hint: \"[— top: N] [— filter: tier1|all|safe|risky]\"\nallowed-tools: Read, Glob, Grep\n---\n\n# /cheat-recommend — 候选池排序推荐\n\n读 candidates.md → 按 composite 排序 → 输出 top N 推荐，每条带评分细节 + 锚点对比 + 推荐理由。\n\n## Overview\n\n```\n[用户：推荐选题]\n  ↓\n[Phase 0: 检查 candidates.md 存在性]   ← 不存在则引导，不报错\n  ↓\n[Phase 1: 解析 candidates 列表]\n  ↓\n[Phase 2: 过滤（tier / 安全性 / 已发过）]\n  ↓\n[Phase 3: 排序 by composite + 找锚点]\n  ↓\n[Phase 4: 输出 top N + 每条的 rationale + 锚点对比]\n```\n\n## Constants\n\n- **TOP_N = 5** — 默认推荐 top 5\n- **STRATEGY = stable+experimental** — 推 ≥2 时按 [cadence-protocol.md](../../shared-references/cadence-protocol.md) 的\"1 稳分 + 1 实验性\"策略；推 1 时只推 top 稳分\n- **POOL_PATH = candidates.md** — 候选池路径\n- **EXCLUDE_PUBLISHED = true** — 排除已发布的（与 `predictions/*.md` 去重）\n- **EXCLUDE_REJECTED = true** — 排除用户主动跳过的（`tier=skip`）\n- **REQUIRE_SCORED = true** — 只推荐已打分的——避免推没读过的素材\n- **DUPLICATE_CATEGORY_LOOKBACK** — 派生自 `state.target_publish_cadence_days`：max(3, cadence_days × 3) 天内已发同类目候选不推（避免审美疲劳）\n\n> 💡 调用时覆盖：`/cheat-recommend — top: 3 — filter: safe`\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `candidates.md` | 用户项目根 |\n| `predictions/*.md` | 用于去重 |\n| `.cheat-state.json` | 当前 rubric_version |\n\n## Workflow\n\n### Phase 0: 候选池存在性检查\n\n读 `candidates.md`：\n\n| 状态 | 处理 |\n|---|---|\n| 文件不存在 | **不报错**。输出引导：见下方\"无候选池引导\" |\n| 文件存在但空（< 1 个 entry） | 同上 |\n| 文件存在且非空 | 进入 Phase 1 |\n\n**无候选池引导**（核心：不让用户第一次遇到 cheat-recommend 时被劝退）：\n\n```\n你目前没有候选池（candidates.md 不存在或为空）。\n\n绝大部分人没有候选池——这很正常。四个建立方式，挑一个：\n\n1. 🌱 [推荐] 跑 /cheat-seed\n   一次性的种子动作：3 个问题（兴趣 / 调性 / 红线）→ 拉公开热点 + Claude brainstorm\n   → 输出 15 候选让你挑 5 → 默认顺带写 5 个 draft。5 分钟搞定。\n   \n   - 没发过历史的：纯 brainstorm（兴趣 × 热点）\n   - 发过历史的（init 时已 import）：brainstorm 会基于\"你过去做过什么\"给推荐\n   \n   说：\"找选题\" 或 \"seed\"\n\n2. 🔥 [日常补充] 用 /cheat-trends 抓 20 条带打分的候选\n   说：\"抓热点\" — 从 weibo-hot / zhihu-hot / b站热门 / HN / 你配的源各拉 N 条\n   适合已经跑过 /cheat-seed、想日常补充候选池的用户\n\n3. ✍️  手动建：把候选标题贴进 candidates.md，每行一条\n   我会自动给每条粗打分\n\n4. 📋 从 Notion / RSS 导入：跑 /cheat-init --mode add-pool 配置 adapter\n\n你也可以跳过候选池，直接给我具体稿子说\"启动预测\"。\n\n> /cheat-seed vs /cheat-trends 的区别：\n> - seed 是种子动作（含 brainstorm + 可选 draft），适合\"我从零开始没选题\"\n> - trends 是日常多 adapter 抓取（不 brainstorm 不写 draft），适合\"日常补充候选池\"\n```\n\n完成引导 → 退出，不继续后续 phase。\n\n### Phase 1: 解析 candidates\n\n按 [candidate-schema.md](../../shared-references/candidate-schema.md) 的\"Markdown 表示\"格式解析每个 H3 entry：\n\n```markdown\n### [tier1] 标题\n- **id**: a3f2c1d4e5b6\n- **composite (v2)**: 8.47 — ER=4 HP=4 QL=5 NA=3 AB=5 SR=3 SAT=3\n- **predicted bucket**: 5-30w\n...\n```\n\n提取每条的 `id` / `title` / `tier` / `composite` / `dimension_scores` / `note`。\n\n容错：`candidates.md` 格式被用户手改过 → 询问用户 schema，**不要静默忽略不识别的 entry**。\n\n### Phase 2: 过滤\n\n```\n1. EXCLUDE_PUBLISHED=true → 扫 predictions/*.md 的 header，提取所有 id；从候选池过滤掉\n2. EXCLUDE_REJECTED=true → 过滤 tier=skip\n3. REQUIRE_SCORED=true → 过滤 composite=null（未打分的不推荐）\n4. filter 参数：\n   - tier1: 只保留 tier=tier1\n   - all: 不过滤（tier1+2+3）\n   - safe: 排除 tier=risky\n   - risky: 仅显示 tier=risky（用于\"我今天就想发风险议题\"）\n```\n\n### Phase 2.5: Buffer 颜色覆盖（**最高优先级**）\n\n读 `state.shoots` + `state.target_publish_cadence_days` 算 buffer 颜色（[cadence-protocol.md](../../shared-references/cadence-protocol.md)）：\n\n| Buffer 颜色 | 推荐策略覆盖 |\n|---|---|\n| 🔴 红 | **只推 top 1 稳分**——不推实验性。回：\"buffer 已 0/1 篇，下个发布日断更风险高，今天必须拍 ≥1 条稳分。下面是 top 1 稳分（不推实验性）\" |\n| 🟠 橙 | 标准 1 稳 + 1 实验，但提示\"建议优先拍稳分\" |\n| 🟢 绿 | 标准 1+1（默认） |\n| 🔵 蓝 | **拒绝推荐**。回：\"你 buffer 已 N 条，cadence-protocol 规定积压时暂停拍摄。先发存货 + 复盘。手动覆盖请说 '我就要拍'\" |\n| 灵活模式 (`target_publish_cadence_days=null`) | 不应用 buffer 覆盖，标准策略 |\n\n### Phase 3: 排序 + 选 1 稳 + 1 实验（按 STRATEGY）\n\n#### 第 1 条（稳分）\n\n1. 按 `composite` 降序排\n2. 过滤掉 `tier=risky`（稳分要安全议题）\n3. 过滤掉 `category` 与最近 `DUPLICATE_CATEGORY_LOOKBACK` 天已发/已推过的重复（避免审美疲劳）\n4. 取 top 1\n\n#### 第 2 条（实验性）\n\n1. 在 candidates.md 中找：\n   - 维度组合与最近已发样本**差异最大**（增加校准信息量），或\n   - 含明确的 pattern/dimension hypothesis（如 \"MS=5 的 A/B 对照\"），或\n   - tier=risky 但用户主动愿意试（用 `--filter risky` 覆盖）\n2. composite 不一定 top——但有\"信息价值\"\n3. 如 candidates 池里没有合适的实验性候选 → 回：\"候选池里没有明显的实验性样本，给你 2 条稳分\"\n\n#### 剩余 (TOP_N - 2) 条\n\n按 composite 降序补满，标 \"（备选）\"。\n\n#### 锚点\n\n对每条找 1-2 个 composite 接近的**已发布**作品作为锚点（从 `predictions/*.md` 读）。优先**同时长**锚点（按 `state.typical_duration_seconds` ±20%）。\n\n### Phase 4: 输出\n\n```\n🎯 候选池推荐（rubric: v2 / buffer: 🟢 绿 / cadence: 隔日更）\n\n📌 第 1 条 — **稳分**（推荐立即拍）：\n  **[tier1] [👍 9.18] \"为你好\"高密体系**\n   - 维度：ER=5 HP=5 QL=4 NA=4 AB=5 SR=5 SAT=4\n   - 粗预测桶：30-100w（中枢 ~60w）\n   - rationale：ER+SR 双 5 顶配，\"高密度家庭议题\"普适且分享安全\n   - 锚点：仓鼠 (composite 9.41, 实绩 124w) — 同走\"理论框架+具象样本\"路线\n   - 风险：议题厚重，不适合连续 2 篇都打这种\n\n🧪 第 2 条 — **实验性**（验证特定假设）：\n  **[tier1] [👍 8.71] 哈哈长度**\n   - 维度：ER=3 HP=5 QL=5 NA=4 AB=5 SR=4 SAT=5\n   - 粗预测桶：30-100w（中枢 ~55w）\n   - **测试目标**：v2.1 候选维度 MS+TS 双 5 vs 谁问你了同 ER/HP/QL/SR 但 MS+TS 低 3\n   - 信息价值：拍这条能强证据/弱推翻 v2.1 升正\n   - 锚点：谁问你了 (composite 8.24, 实绩 11.7w)\n\n（备选 top 3）：\n  3. ……\n  4. ……\n  5. ……\n\n下一步：\n- 选稳分 + 实验性各拍 1 条 → 改写 script → \"启动预测\"\n- 只拍 1 条 → 选稳分（buffer 颜色越红，越应该选稳分）\n- 想抓更多候选 → 说\"抓热点\"\n- 都不满意 → 说\"过滤改 all\"看其他 tier 或 \"regen\"\n```\n\n如 buffer 颜色为 🔴：\n```\n🔴 buffer 警戒：你 buffer 已 0/1 篇，**下个发布日可能断更**。\n   按节奏协议，只推 top 1 稳分（不推实验性）：\n\n  **[tier1] [👍 9.18] \"为你好\"高密体系**\n   - ...（同上稳分格式）\n\n今天必须拍这条。挑 5 条候选 → \"抓热点\"。\n```\n\n如 buffer 颜色为 🔵：\n```\n🔵 buffer 积压：你 buffer 已 N 条，**暂停推荐**。\n   按节奏协议，先发存货 + 复盘。\n   - 已拍未发：N 条（最早一条 X 天前拍的）\n   - 待复盘：N 条\n   说 \"已发布 ...\" 出队，或 \"复盘\" 处理待复盘项。\n   如果你坚持要拍新的，回 \"我就要拍\"，我会推 top 1 稳分。\n```\n\n每条必有：维度评分（让用户能挑战打分）+ 锚点（让用户校准 composite 的可信度）+ rationale（让用户理解推荐逻辑）。**不允许只输出 composite 排序而无解释**——那是黑箱。\n\n## Key Rules\n\n1. **不报错，给引导**。candidates 缺失是默认状态，不是错误\n2. **不推未打分的**。REQUIRE_SCORED=true 是诚实门槛——推未读过的素材是占星\n3. **必带锚点**。composite 8.47 在不同账号意味不同，锚点把抽象数字 ground 到真实样本\n4. **必带 rationale**。一句话——为什么这条比第二条强？\n5. **去重 published**。已发过的不推（用户可显式覆盖）\n\n## Refusals\n\n- 「直接给我 composite 最高的，不用解释理由」 → 拒绝。展示评分 + 锚点是发现\"打错\"的唯一机会\n- 「把 candidates.md 里所有 entry 都重新打分一遍」 → 路由到 `/cheat-score` 单条做；批量重打分是 `/cheat-bump` 的一部分，不在 recommend 范围\n- 「按预测桶排，不要按 composite」 → 询问理由。bucket 是 composite 的离散化，按 composite 排即按 bucket 排，差异在桶内序——如果用户真想按\"押注期望值\"排，需要乘以平均播放，那是另一个独立 scoring 维度\n\n## Integration\n\n- 上游：`/cheat-trends` 把外部热点拉进 candidates.md → recommend 自动看到\n- 下游：用户挑一条后写稿 → `/cheat-predict`（candidate 的粗 composite 不进入 prediction，prediction 重新打）\n- 与 `/cheat-status` 协调：status 显示 \"candidates 池有 N 条 tier1 未发\"，recommend 提供具体推荐\n\n<!-- File: skills/cheat-retro/SKILL.md (Tokens: ~7617 | Category: anthropic-skill) -->\n---\nname: cheat-retro\ndescription: T+N 天数据回收 + 复盘 + 把实绩观察写入 rubric-memo.md。这是校准循环的反馈环节——不复盘的预测等于占星。触发词：\"复盘 [path]\"/\"retro this\"/\"T+3d 数据来了\"/\"抓数据 [path]\"/\"把这篇复盘了\"。\nargument-hint: <prediction-file> [— window: 3|5|7] [— source: manual|adapter]\nallowed-tools: Bash(*), Read, Edit, Write, Glob, Grep, Skill\n---\n\n# /cheat-retro — 数据回收与复盘\n\n抓 T+N 天的实际表现 → 对比预测 → 提炼新观察 → 写入 rubric-memo.md。**只追加 `## 复盘` 段，绝不改预测段**。`rubric_notes.md` 是 blind 白名单，只能保存通用公式、维度定义和抽象规则，不能写入样本名、实绩、评论、链接或播放/阅读数。\n\n## Overview\n\n```\n[用户：复盘 predictions/2026-05-04_...]\n  ↓\n[Phase 0: 校验 immutability + 校验时间窗口]\n  ↓\n[Phase 1: 抓数据（manual paste 或 adapter）]\n  ↓\n[Phase 2: 写实绩段 + top 评论关键词]\n  ↓\n[Phase 3: 验证/推翻预测的各假设]\n  ↓\n[Phase 4: 提炼新观察]\n  ↓\n[Phase 5: 落盘（追加到 ## 复盘 段）]\n  ↓\n[Phase 6: 写入 rubric-memo.md 的\"观察记录\"段]\n  ↓\n[Phase 7: 检测是否触发 bump 候选 → 提示用户跑 /cheat-bump]\n```\n\n## Constants\n\n- **RETRO_WINDOW_DAYS = 3** — 默认 T+3d。短视频快平台可设 1，长文设 7\n- **DATA_SOURCE = manual** — manual: 用户粘数字；adapter: 调对应平台 adapter（需配置）\n- **AUTO_PROPOSE_BUMP = true** — Claude 判断是否系统性偏差时自动提议 /cheat-bump\n  - **默认参考**：连续 ≥3 次同向偏差（high/low）→ 提议\n  - **但 Claude 可以更早提议**：1 次极端偏差（如中枢 50w 实绩 5w 这种 ≥10x），即使没有\"连续\"也提议\n  - **也可以更晚**：3 次同向但每次偏差都很小（<25%），可能只是噪声不是系统性\n- **TOP_COMMENTS_N = 20** — 抓 / 粘 top N 高赞评论\n\n> 💡 调用时覆盖：`/cheat-retro <file> — window: 7 — source: adapter`\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<prediction-file>` 或 `<video-folder>` | 用户参数；缺失则从 `.cheat-state.json` 的 `pending_retros[0]` |\n| `rubric_notes.md` | 用户项目根（只读，用于当前规则上下文；不得写入实绩观察） |\n| `rubric-memo.md` | 用户项目根（写入复盘观察、实绩证据、样本名与评论信号） |\n| `.cheat-state.json` | 状态文件 |\n\n### 入参解析（同 cheat-predict 双形态接受）\n\n用户给的可能是：\n- **`predictions/2026-05-04_<id>_<short>.md`** → 直接用这个 prediction 文件\n- **`videos/2026-05-04_<id>_<short>/`** → 找对应的 prediction 文件（按 id 匹配）+ 把 report.md 写到该 video folder 里\n- 缺省 → 从 `pending_retros[0]` 取最早的\n\n## Workflow\n\n### Phase 0: 校验\n\n1. 读 `<prediction-file>`，确认存在\n2. **识别有效预测段**：扫所有 `## 预测...` 段（可能含 `## 预测`、`## 预测 v1`、`## 预测 v2` 等）：\n   - 取**最后一个**`## 预测 vN` 作为本次校准的依据（v2 存在则用 v2；只有 v1 则用 v1；legacy 单段 `## 预测` 直接用）\n   - state.shoots 对应项的 `v2_prediction_written` 应与\"是否存在 v2 段\"一致——不一致则警告（state 与文件脱节）\n3. **校验 immutability**：在内存 cache 住所有 `## 预测...` 段的内容（用于 Phase 5 后核对——**全部段不可改**，不只是有效段）\n4. 校验文件 header 有 `Published at` → 没登记的不能复盘，提示用户先 `/cheat-publish`\n5. 校验时间窗口：今天 - published_at >= RETRO_WINDOW_DAYS。不够 → 提示\"还差 X 天\"，询问用户是否仍坚持复盘（标 `early_retro: true`）\n6. 校验已有复盘段是否已填——已填则询问\"是补充还是修正？\"\n   - 补充 → 在已有复盘段下追加新子段，标日期\n   - 修正预测段（用户错觉）→ 拒绝\n\n### Phase 1: 抓数据\n\n按 `state.data_collection` 字段分两条路径——抓回数据后**写到 video folder 的 `report.md`**（如果 prediction 关联 video folder），同时解析摘要 inline 到 prediction 的复盘段。\n\n#### Path A：`DATA_SOURCE=manual`（候补方案）\n\n- 询问用户：\"粘贴这条作品的当前数据：播放 / 点赞 / 评论 / 转发 / 收藏（顺序无所谓，能识别就行）\"\n- 用户粘 → 解析提取数字\n- **强制要求 top 评论**：让用户从平台后台或直接打开评论区贴 TOP_COMMENTS_N 条到对话里（每条带赞数）\n  - 用户拒绝 / 给少于 5 条 → **拒绝继续**：\"评论才是真信号——'她不一样'这种模因爆发只能从评论看出。\n    没评论的复盘 = 看体温计判断病情。粘 top 20 给我。如果实在拿不到，告诉我原因（比如评论被关了），我帮你标 `comments_unavailable`，但这次复盘价值打折。\"\n- 把粘的原始数据写到 `videos/<...>/report.md`（如有 video folder）\n\n#### Path B：`DATA_SOURCE=adapter`\n\n按 prediction header 的 `Platform` 字段 + state 的 `enabled_perf_adapters` 决定调哪个：\n\n| Platform | Adapter | 调用方式 |\n|---|---|---|\n| `douyin` | `adapters/perf-data/douyin-session/` | `bash <adapters-dir>/douyin-session/run.sh <aweme_id> <video_folder>` |\n| `xhs` | `adapters/perf-data/xhs-explore/` | `bash <adapters-dir>/xhs-explore/run.sh <note_id> <video_folder>` |\n| `linkedin` | `adapters/perf-data/linkedin-session/` | `bash <adapters-dir>/linkedin-session/run.sh <activity_id> <video_folder>` |\n| `wechat_channels` | `adapters/perf-data/wechat-channels/` | `bash <adapters-dir>/wechat-channels/run.sh <post_id> <video_folder>` |\n| `youtube` | `adapters/perf-data/youtube-data-api/`（planned — batch 3, not yet available） | 调 YouTube Data API（需 API key） |\n| `bilibili` | `adapters/perf-data/bilibili-stat/` | `bash <adapters-dir>/bilibili-stat/run.sh <bvid> <video_folder>` |\n| 其他 | 无 adapter | 优雅降级到 Path A |\n\n> `<adapters-dir>` = 克隆源码处的 `cheat-on-content/adapters/perf-data/`（install.sh **不**复制 adapter 到 ~/.claude/skills，只复制 15 个 skill）。定位：`find ~ -path '*/cheat-on-content/adapters/perf-data' -type d | head -1`。\n\n**douyin-session 的特殊处理**：\n- 视频 URL（如 `https://v.douyin.com/abc123`）→ 短链解析 → 提取 aweme_id\n- 调用前确认 cookie 文件存在（adapter 会找 `.auth/`）；不存在则提示用户先跑 `python <adapter>/crawler.py login`\n- adapter 输出在 `<video_folder>/report.md`（adapter 的 renderer.py 已经按这个格式写）\n- cheat-retro 读这个 report.md 解析关键数据 → 摘要写入 prediction 的复盘段\n\n**xhs-explore 的特殊处理**：\n- 笔记 URL（`https://www.xiaohongshu.com/explore/<note_id>?xsec_token=...` 或 `https://xhslink.com/xxx`）→ 提取 note_id\n- 调用前确认 cookie 存在（adapter 找 `.auth-xhs/`）；不存在则提示先跑 `python <adapter>/crawler.py login`\n- 字段已校准（观看 `view_count` 等已写死）；万一接口改版导致某项为 0，看 report.md 末尾 galaxy 原始 JSON，把新 key 加进 `crawler.py` 的 `_normalize_note`\n- **评论可能抓不到**（xsec_token 缺失 / 评论关闭）→ report.md 标\"未抓到评论\" → 此时**降级要求用户 manual 粘 top 20 评论**（评论是真信号，不能省）\n\n**linkedin-session 的特殊处理**：\n- 帖子 URL（`https://www.linkedin.com/feed/update/urn:li:activity:<id>/`）或裸 activity_id → adapter 自动提取 activity_id\n- 调用前确认 cookie 存在（adapter 找 `.auth-linkedin/`）；不存在则提示先跑 `python <adapter>/crawler.py login`\n- **只能抓你本人发的帖子**（LinkedIn 单帖分析仅作者可见）；LinkedIn 界面 日/英 随机切换，`extract.py` 的 `POST_METRICS` 已存双语标签，万一某项为 None 看 `.cheat-cache/linkedin-session-debug/post_<id>.txt` 把新标签补进去\n- **评论只给数、不给正文**（分析页限制）→ report.md 标注 → **降级要求用户 manual 粘 top 评论**（评论是真信号，不能省）\n\n**wechat-channels 的特殊处理**：\n- 仅抓用户自己登录后可见的视频号助手后台数据；调用前确认内容项目存在 `.auth-wechat-channels/`\n- 不存在登录态时，提示先运行 `python <adapter>/review.py login` 并扫码；登录态只保存在内容项目本地\n- adapter 输出 `report.md`，但不落盘原始响应、请求体、完整后台 URL、后台截图、页面文本、评论者昵称或内部用户 ID\n- 找不到目标作品或 adapter 失败时，非 0 退出并降级 manual；不得把旧 report.md 当作本次成功\n\n**bilibili-stat 的特殊处理**：\n- 视频 URL（`https://www.bilibili.com/video/<BV号>` 或 b23.tv 短链）或直接给 BV 号 → adapter 自动提取 BV 号\n- **无需登录**：B站视频数据(view)与评论(reply)都是公开接口、免 wbi 签名，adapter 是纯 httpx，没有 `crawler.py login` 步骤、不碰 `.auth/`\n- 依赖 httpx：首次用 `pip install -r <adapter>/requirements.txt`\n- 评论按热度（sort=2）抓取；B站老接口主楼评论可能偏少，不足时降级 manual 粘\n\n**任何 adapter 失败**（cookie 过期 / 接口变化 / 网络）→ **优雅降级到 manual**，提示用户：\"adapter 调用失败，原因 [X]。改用 manual 模式——粘下面的数据\"。**不阻塞流程**。\n\n#### 共同输出\n\n不管 Path A 还是 B，最终：\n- `videos/<...>/report.md` 含完整原始数据（数字 + top 评论）\n- prediction 文件复盘段含**摘要**（关键比率 + 评论关键词聚类 + 验证/推翻判定）\n- report.md 是数据真相，prediction 复盘段是判断真相\n\n### Phase 2: 写实绩段 + top 评论分析\n\n**实绩数据格式**（参考 [prediction-anatomy.md](../../shared-references/prediction-anatomy.md) 的复盘段格式）：\n\n```markdown\n### 实绩数据\n- 播放：71.1w（落在 `30-100w` 桶内偏高，相对中枢 50w **+42%**）\n- 点赞：2.4w（赞播比 3.38%）\n- 评论：899（评播比 0.126%）\n- 收藏：5251\n- 分享：1.8w（分播比 2.53%，强）\n```\n\n数据点之间的派生比率（赞播比、评播比、分播比）必须算出来——它们是单纯播放数无法暴露的信号。\n\n**top 评论关键词聚类**：\n- 把粘进来的 N 条评论分 3-5 类（高赞模因 / 概念引用 / 离题噪声 / 转发暴露暗示 / @朋友传播 等）\n- 每类列代表性评论（带赞数）\n- 报告比例（\"22% 是模因复用、35% 是概念引用、5% 是离题\"）\n\n### Phase 3: 验证/推翻\n\n对 prediction 文件里的每一项（推理因素表、关键校准假设、反事实场景），逐项判定：\n\n```markdown\n### 哪些预测被验证 ✅ / 推翻 ❌\n\n**验证 ✅**:\n- 关键校准假设完全成立：本篇 71.1w / 谁问你了 11.7w = 6.07x，远超我押的 1.5-2x\n- ER=5 主导情感传播力 → H1 强证据\n- HP=5 验证：分播比 2.53% 与\"金句被高频引用\"匹配\n\n**推翻 ❌**:\n- 中枢 50w 被超出 +42%\n- 反事实推理里\"必须搭配强社会议题才能破 30w\" 完全错误\n- SR 押注（\"H2 SR 应上调\"）反向被推翻：SR 在情感向场景几乎不贡献\n```\n\n**关键纪律**：\n- 每条验证 / 推翻必须引用具体数据（\"分播比 2.53%\"），不许写\"基本符合\"这种含糊措辞\n- 反事实的\"如果落在 X bucket 意味着什么\"——实际落在的那个 bucket 直接告诉你哪个 rubric 假设被测试了，明确写出来\n\n### Phase 4: 提炼新观察（**两类，分别写入两个文件**）\n\n#### 4a. Rubric 观察（写入 rubric-memo.md）\n\n打分维度 / 公式 / bucket 边界相关的观察：\n\n```markdown\n### 需要写进 rubric-memo.md 的新观察\n\n1. **ER 在情感向场景的真实权重应 ≥ ×2.0**：与谁问你了 6x 流量比是 v2 rubric 最强的反事实证据\n2. **议题分享冲动 (TS) 是隐藏维度**：joker / \"她不一样\" / 滤镜重构提供了安全的自嘲身份，转发不暴露处境，TS=5 的样本\n3. ……\n```\n\n每条观察必须可追溯到具体数据点（不写\"情感很重要\"——写\"ER5/SR2 vs ER3/SR4 同 composite 下流量差 6x\"）。\n\n#### 4b. 写作 Pattern 观察（写入 script_patterns.md）\n\nDiff `scripts/<id>.md`（pre-shoot 草稿，可能是 cheat-seed 写或用户写）vs `videos/<id>/script.md`（实际拍摄稿——cheat-shoot 时用户提供的版本），找出**改动且对流量有明显影响**的部分：\n\n| 用户做了什么 | 流量影响 | 是否提议追加 pattern |\n|---|---|---|\n| 砍掉某段 | 实绩 ≥ 中枢 → \"砍掉没伤流量\"——验证那段冗余 | 是，加到 script_patterns.md \"用户改稿历史观察\"表 |\n| 加了某句 / 互动钩子 | 实绩超中枢 → 可能是新 pattern | 是，候选 Pattern N，标 ≥1 样本待验证 |\n| 改了风格（如开头软化） | 高于同类样本 → 风格改动有效 | 是，候选 Pattern N |\n| 没动结构 / 改动与流量无关 | — | 不追加 |\n\n输出格式：\n\n```markdown\n### 需要写进 script_patterns.md 的新 pattern 候选\n\n1. **用户改稿模式**: 砍掉 [X 段] / 加了 [Y]\n   - 流量影响：实绩 [N] vs 中枢 [M]，[偏差 / 命中]\n   - 建议：追加到 script_patterns.md 的\"用户改稿历史观察\"表\n\n2. **新 pattern 候选 N**：[一句话描述]\n   - 单样本支持\n   - 触发条件：[何时该用]\n   - 建议：追加到 script_patterns.md 末尾的\"新发现的 Pattern\"段，标 ≥1 样本待验证\n```\n\n询问用户：\"要把这些追加到 script_patterns.md 吗？(yes / no / 选择哪几条)\"。**用户确认后才追加**——避免把单点观察直接写成正式 pattern。\n\n> **rubric 进化 ≠ 写作进化**——两者解耦：\n> - rubric_notes.md 学的是\"哪些维度真的预测流量\"\n> - script_patterns.md 学的是\"什么写法真的能起作用\"\n> 可能有交叉（如 MS 维度与\"互动钩子\" pattern），但记录在两个文件里是因为**作用域不同**——rubric 改了影响所有未来打分，pattern 改了影响所有未来 draft。\n\n如果 `videos/<id>/script.md` **缺失**（cheat-shoot 时用户标 `script_lost`） → 跳过 4b，没法 diff。\n如果 `script_consistency = \"consistent\"`（用户拍时没改稿）→ 4b 仍然有意义（diff 也许是空），但可以快速跳过细查。\n如果 `script_consistency = \"modified\"`（用户拍时改了）→ **4b 是核心**，重点学这次改动 → 流量影响。\n\n### Phase 5: 落盘到 ## 复盘 段\n\n用 Edit 工具，**仅追加**到现有 `## 复盘` 段（如有占位 `（待填）` 行先删除）：\n\n```markdown\n## 复盘\n\n**复盘时间**: 2026-05-07（发布 T+3d）\n**抓取时间**: 2026-05-07 09:30\n**数据来源**: manual paste\n\n### 实绩数据\n[Phase 2 内容]\n\n### Top 评论关键词\n[Phase 2 内容]\n\n### 哪些预测被验证 / 推翻\n[Phase 3 内容]\n\n### 需要写进 rubric-memo.md 的新观察\n[Phase 4 内容]\n```\n\n**写完后再次校验**：读取保存后的文件，对比**所有** `## 预测...` 段（v1 / v2 / legacy）的合并哈希应等于 Phase 0 cache 的合并哈希。**任一段被改 → 报错并回滚**。\n\n### Phase 6: 写入 rubric-memo.md + script_patterns.md\n\n#### 6a. rubric-memo.md（Phase 4a 的输出）\n\n按 [observation-lifecycle.md](../../shared-references/observation-lifecycle.md) 的 blind leak guard，追加到 `rubric-memo.md` 的 `## 观察记录` 段。这里可以包含真实样本名、实绩数据、评论关键词和链接；这些内容**绝不**写入 `rubric_notes.md`：\n\n```markdown\n### YYYY-MM-DD [标题简称] (id) — [一句话定性]\n- 预测：composite=X.XX，bucket=Y\n- 实绩：播放 / 点赞 / 评论 / 转发（带 T+Nd 标注）\n- Top 评论关键词：[简短摘录 + 赞数]\n- 判断：哪个维度被验证/推翻？为什么？\n- Rubric 调整：[如果有，写明 \"下次打 XX 类文章时改 YY\"]\n- 详见：[predictions/<file>.md]\n```\n\n**检测跨样本 pattern**：扫描 `rubric-memo.md` 已有\"观察记录\"，看新观察是否与某条已有观察形成 ≥2 样本支持。命中则在 `rubric-memo.md` 升级到\"重大跨样本观察\"段。只有在后续 `/cheat-bump` 落地时，才把已验证的规律抽象成通用语言写入 `rubric_notes.md`。\n\n#### 6b. script_patterns.md（Phase 4b 的输出，**用户确认后才写**）\n\n如 Phase 4b 用户回 \"yes\" 或选择性确认了某几条：\n- \"用户改稿模式\" → 追加到 script_patterns.md 的\"用户改稿历史观察\"表\n- \"新 pattern 候选 N\" → 追加到末尾\"新发现的 Pattern\"段，**显式标 ≥1 样本待验证**\n\n新 pattern 候选的格式（同 [script_patterns.template.md](../../templates/script_patterns.template.md) 的 Pattern 11/12 示例）：\n\n```markdown\n### Pattern N（来自 [视频简称]，单样本待验证）\n\n**现象**：[Phase 4b 描述]\n\n**原理**：[为什么有效——基于这一次观察的猜测]\n\n**触发条件**：[何时该用]\n\n**待验证**：需要 ≥2 样本支持才能升正式 pattern。\n```\n\n跨样本 pattern 升正式：扫描\"新发现的 Pattern\"段，看是否有 ≥2 样本支持同一现象 → 升到核心 pattern 库 + 删 \"待验证\" 标记。\n\n如用户在 Phase 4b 全否（\"no\"）→ 跳过 6b，rubric-memo.md 仍照写。\n\n### Phase 7: 检测 bump 触发\n\n读 `.cheat-state.json` 的 `consecutive_directional_errors` 字段，按本次复盘判定向更新：\n- 本次预测高估（实绩 < 中枢 -25%） → push `[\"high\"]` + 记录 deviation_magnitude（如 0.5x / 0.3x）\n- 本次预测低估（实绩 > 中枢 +25%） → push `[\"low\"]` + 记录 deviation_magnitude\n- 在 ±25% 内 → 不 push\n\n**Claude 判断是否提议 bump**（不是固定门槛）：\n\n```\n判断维度：\n1. 连续同向次数（参考默认：≥3）\n2. 单次偏差幅度（参考默认：>2x 或 <0.5x 算极端）\n3. 偏差是否能解释为单一维度漏判（如 ER 或 SR 一致偏离）\n4. 用户是否在复盘里反复提到同一现象\n\n任一足够强 → 提议 bump：\n- 3 次连续同向，每次都中等偏差 → 提议\n- 1 次极端偏差（如 ≥10x），即使没连续 → 提议（\"一次性强信号\"）\n- 2 次同向 + 评论区出现一致的反向证据 → 提议（\"评论 + 数据双信号\"）\n\n不提议的情况：\n- 3 次同向但每次都很小（<25%）→ 可能只是噪声\n- 偏差跨多个维度无清晰方向 → bump 不知道改什么\n```\n\n提议时输出：\n\n```\n🚨 检测到 [系统性偏差信号] / [极端单点偏差] 。\n\n[简短描述：连续 N 次 / 1 次极端 / 评论双信号 等]\n\n这可能是 rubric 系统性偏差的信号。建议：\n- 跑 /cheat-bump 看是否需要升级公式\n- 或先看 /cheat-status 详细分析\n\n注：本次提议是 [default-aligned: 满足 ≥3 同向] / [judgment-driven: 1 次 10x 强偏差]\n```\n\n更新 state file：\n```json\n{\n  \"calibration_samples\": <+1>,\n  \"pending_retros\": [<剔除本次>],\n  \"last_retro_at\": \"<ISO>\",\n  \"consecutive_directional_errors\": [...]\n}\n```\n\n## Key Rules\n\n1. **预测段 immutable**。Phase 0 cache + Phase 5 校验是双保险。任何 hash 不一致 → 报错回滚\n2. **数据来源必须标注**。`数据来源: manual paste` 或 `数据来源: adapter:douyin-session` 写进复盘段\n3. **观察可追溯**。每条新观察引用具体数据点\n4. **不在复盘里 bump**。Phase 7 只**提议** bump，实际升级走 `/cheat-bump`——避免一次操作做两件事\n5. **早复盘标记**。RETRO_WINDOW_DAYS 不到就复盘 → state file 记 `early_retro: true`，bump 时这种样本权重降级\n\n## Refusals\n\n- 「这条数据已经看过了，但你假装没看，按预测时的盲度做复盘」 → 复盘本来就是看完数据再做的；这个表述本身没有违规，但要确认用户没在 prediction 写之前透露过数据\n- 「把预测段的概率分布改一下，让复盘看起来更准」 → 拒绝。原则 #1\n- 「跳过观察提炼，直接结束」 → 拒绝。新观察是 rubric 进化的唯一燃料；缺它复盘退化为\"看一眼\"\n- 「直接 bump，不要单独走 /cheat-bump」 → 拒绝。bump 流程有完整的跨模型审 + cleanup pass，retro 是触发器不是执行器\n\n## Integration\n\n- 前置：`/cheat-publish` 已登记 + 时间窗口达到\n- 下游：累计 `consecutive_directional_errors` 满 3 → 触发 `/cheat-bump` 提议\n- 状态字段更新：`calibration_samples` +1（这是 cheat-status 显示进度的关键）\n- pending_retros：剔除本条\n- 与 [observation-lifecycle.md](../../shared-references/observation-lifecycle.md) 紧耦合：每次复盘是观察新增的入口\n\n<!-- File: skills/cheat-score-blind/SKILL.md (Tokens: ~4223 | Category: anthropic-skill) -->\n---\nname: cheat-score-blind\ndescription: |\n  INTERNAL sub-agent for blind 7-dim rubric scoring. **NOT a user-facing skill — do NOT invoke from main conversation.** Called via Task tool by cheat-score / cheat-predict / cheat-bump to get a context-isolated score on a script. Receives ONLY script_path + rubric_notes_path; refuses any other input. Outputs strict JSON: 9 dimensions × {score 0-5, confidence enum, one-line reason}. **Hard refuses to Read** .cheat-state.json, predictions/*, retro 段, or anything that could leak post-publish data. This is channel B in the 3-channel calibration model (A=main, B=blind sub, C=cross-model).\nallowed-tools: Read, Glob, Grep\nargument-hint: <script-path> <rubric-notes-path>\n---\n\n# /cheat-score-blind — Channel B (blind scorer sub-agent)\n\n> ⚠️ **这是子 agent，不是用户 skill**。只能由 `cheat-score` / `cheat-predict` / `cheat-bump` 通过 Task tool spawn。用户直接 trigger 没有意义——主对话已经被污染，调用 blind sub-agent 在主 context 里跑不构成隔离。\n\n---\n\n## Why this exists（绝不可省的背景）\n\ncheat-on-content 的 7/9 维打分原本 inline 在主对话——但主 Claude 已经看过：\n- 用户对话历史（含偶然提到的播放数 / 评论 / 情绪）\n- 已发布作品的实绩数据\n- 历史 `predictions/*.md` 含复盘段（**严重污染**）\n- 用户的赞美 / 抱怨 / 期待\n\ninline 打分 = **被污染的\"盲\"预测**。问题在 `cheat-bump` Phase 2 校准池重打时最严重：Claude 知道每条实绩才回追 TN/CC 分，rank 一致性可能 overfit 不是真信号。\n\n**channel B 的角色**：用 Task tool 把打分动作丢进一个**全新 context**——这个 sub-agent 没看过主对话、没读过 state、没碰过 predictions/。它只看 script 全文 + rubric_notes.md，按 rubric 打分。\n\n输出回传主对话后，主 Claude 自己对比、做最终决策。隔离的是**打分这个动作的输入**，不是决策权。\n\n## 三 channel 模型\n\n| Channel | 输入 | 用途 | 风险 |\n|---|---|---|---|\n| **A** = 主对话 | 全部上下文 | 跟用户交互、写 retro、决策 | 被实绩 / 用户态度污染 |\n| **B** = blind sub-agent (this) | **只** script + rubric_notes.md | 给一份未受污染的打分作为 anchor | 仍是 Claude，RLHF prior 共享 |\n| **C** = 跨模型 audit (`mcp__llm-chat__chat` to qwen-max) | 校准池数据 + 新公式 | bump 终局 sanity check | RPM 限制、模型差异、单点 |\n\nA 决策时把 B 当对照看 disagreement，**不当真理**。C 只在 bump 终局调一次。\n\n---\n\n## Inputs（**唯一被允许的输入**）\n\n| 必填 | 来源 | 说明 |\n|---|---|---|\n| `<script-path>` | 主 Claude 通过 Task prompt 显式传入 | `scripts/<id>.md` 全文 |\n| `<rubric-notes-path>` | 同上 | 用户项目根 `rubric_notes.md` 当前 rubric 公式 + 维度定义 |\n\n**仅此两个文件可读**。其他一切**硬拒绝**——见下方 \"Hard refusals\" 段。\n\n## 禁止读取（hard list）\n\n下面这些路径 / 模式 sub-agent **绝不能 Read** —— 即使主 Claude 在 Task prompt 里手滑塞进来，也要拒绝并在 JSON 输出标对应 `refusal` 码：\n\n| 路径模式 | 为什么禁 | refusal_code |\n|---|---|---|\n| `.cheat-state.json` | 含 calibration_samples / pending_retros / last_published_at / shoots — 全是后视数据 | `blocked_contaminated_input` |\n| `predictions/*.md` | 含 `## 预测` 段 + `## 复盘` 段，复盘段就是实绩 | `blocked_contaminated_input` |\n| `videos/*/report.md` | T+3d 抓回的真实数据 | `blocked_contaminated_input` |\n| `videos/*/script.md` | 后改拍摄稿，复盘时被对照 | `blocked_contaminated_input` |\n| `STATUS.md` | cheat-status 渲染的看板，含过去数据 | `blocked_contaminated_input` |\n| `.cheat-cache/usage.jsonl` | 行为 log | `blocked_contaminated_input` |\n| **`rubric-memo.md`** | **cheat-bump 升级 Memo 累积档案——含真实视频名 + 实绩 + 派生证据。这是 channel B 的最大泄漏入口（PR #11 实测复现）** | **`blocked_rubric_memo`** |\n| **`audience.md`** | **cheat-persona 从复盘评论派生的受众画像——含评论证据 / 实绩信号。属 channel A creative 资产，进 blind 打分 = 实绩泄漏** | **`blocked_audience`** |\n| 任何含\"播放 / 阅读 / 点赞 / 评论数 / 转发 / w / 万 / k / M\"的文件 | 直接污染 | `blocked_contaminated_input` |\n\n**白名单只有两个**：\n- `scripts/<id>.md`（pre-shoot 草稿，传入参数）\n- `rubric_notes.md`（评分公式 + 维度定义，**应**只含通用语言；如发现实绩数字 → 标 `non_blind_warning` 并降 confidence）\n\n如果主 Claude Task prompt 漏传了某条路径，sub-agent 主动询问\"我只允许读 script + rubric_notes，缺哪个？\"——**绝不**自己去 Glob 探测项目结构补全。\n\n> ⚠️ **白名单兜底自检**：读完 `rubric_notes.md` 后必跑 `grep -E '\\\\d+\\\\s*[wWmMkK万]|播放|实绩|实际'`——命中 → 标 `self_check.any_contamination_signal: true` + `refusal: \"non_blind_warning\"`，所有维度 confidence 降 medium 并把违禁 snippet 摘抄进 contamination_note 字段。**仍输出 dimensions** 让主 Claude 知道发生了什么——拒绝输出比误判更糟，但要诚实标注。\n\n---\n\n## Workflow\n\n### Phase 0：边界自检\n\n1. 解析 Task prompt 拿 `<script-path>` 和 `<rubric-notes-path>`\n2. 校验路径符合白名单——不在 `scripts/` 下的 .md → 拒绝（除非主 Claude 显式说明\"这是临时草稿临时路径，标 `non_standard_path: true`\"）\n3. Read `<rubric-notes-path>` → 解析当前 rubric_version + 维度数量（7 或 9）+ 公式\n4. Read `<script-path>` → 拿到 script 全文 + 字数\n\n⚠️ **不要做的事**：\n- 不要为了\"看看用户做啥账号\"去 Read `benchmark.md` —— benchmark 是 Channel A 的 context，不属于本 sub-agent\n- 不要为了\"看看历史风格\"去 Glob `predictions/` —— 那是污染源\n- 不要去 Read `.cheat-state.json` 看 calibration 进度 —— 你**完全不需要知道**主 Claude 跑了多少篇\n\n### Phase 1：按 rubric 打 N 维分\n\n按 `rubric_notes.md` 当前 rubric 公式：\n\n- v0：7 维等权（ER / SR / HP / QL / NA / AB / SAT）—— 默认起步\n- v1：用户校准过的（权重不同）\n- v2 / v2.1 / ...：含 MS / TS 等新增维度（9 维）\n\n对每个维度：\n1. 给一个 **0-5 整数分**\n2. 给一个 **per-dim confidence** enum：`high | medium | low`\n   - high：稿子里有直接证据（一句话指向该维度）\n   - medium：可推断但需要解释\n   - low：稿子信号太弱，纯估\n3. 给一行 **理由** ≤ 30 字，**必须引用稿子里具体词或场景**\n\n不算 composite——composite 是公式行为，主 Claude 用回传的维度分自己算。\n\n### Phase 2：返回严格 JSON\n\n输出**只能**是一个有效 JSON。所有 markdown 解释都封禁——主 Claude 要的是结构化数据回主 context 解析。\n\n```json\n{\n  \"subagent_version\": \"v1\",\n  \"rubric_version\": \"v2\",\n  \"script_path\": \"scripts/2026-05-04_abc123_短title.md\",\n  \"script_hash\": \"<sha256:12 of script content>\",\n  \"scored_at\": \"<ISO 8601 +08:00>\",\n  \"dimensions\": {\n    \"ER\": { \"score\": 4, \"confidence\": \"high\",   \"reason\": \"PPT加油猫猫开头—具象画面，情绪反差强\" },\n    \"SR\": { \"score\": 3, \"confidence\": \"medium\", \"reason\": \"AI焦虑是议题但非热点对峙\" },\n    \"HP\": { \"score\": 5, \"confidence\": \"high\",   \"reason\": \"首句\\\"第七页大屏中央 加油猫猫\\\"具象反差\" },\n    \"QL\": { \"score\": 5, \"confidence\": \"high\",   \"reason\": \"\\\"加油猫猫救了我一命\\\"双关金句\" },\n    \"NA\": { \"score\": 4, \"confidence\": \"medium\", \"reason\": \"单线反思+收束，清晰但不复杂\" },\n    \"AB\": { \"score\": 4, \"confidence\": \"medium\", \"reason\": \"一人公司题但AI焦虑普适\" },\n    \"SAT\": { \"score\": 2, \"confidence\": \"high\",  \"reason\": \"共情调，几乎无讽刺\" }\n  },\n  \"input_status\": {\n    \"rubric_notes_read\": true,\n    \"script_read\": true,\n    \"any_other_file_read\": false\n  },\n  \"self_check\": {\n    \"saw_play_numbers\": false,\n    \"saw_comments\": false,\n    \"saw_retro_segment\": false,\n    \"any_contamination_signal\": false\n  },\n  \"refusal\": null\n}\n```\n\n`refusal != null` 的合法值：\n- `\"blocked_contaminated_input\"`：Task prompt 传了禁读路径（state / predictions / videos / 等）\n- `\"blocked_rubric_memo\"`：Task prompt 传了 `rubric-memo.md`（bump 升级档案，含实绩）\n- `\"blocked_audience\"`：Task prompt 传了 `audience.md`（受众画像，含评论派生的实绩信号）\n- `\"script_path_invalid\"`：找不到 script 文件\n- `\"rubric_unparseable\"`：rubric_notes.md 损坏\n- `\"non_blind_warning\"`：发现 contamination 苗头但勉强能打分（仍输出 dimensions，但 confidence 全降 medium）\n\n**JSON 必须可被 `python3 -c \"import json; json.loads(open(path).read())\"` 解析**。不允许：\n- 尾部多余逗号\n- 注释（JSON 不允许 //）\n- Markdown 围栏（输出根节点必须是 `{`）\n\n### Phase 3：（可选）写 sidecar 文件供主 Claude 二次读取\n\n如果 Task prompt 含 `sidecar_path` 参数 → 写 JSON 到该路径（典型用法：bump phase 2 批量打分时存多份 sidecar）。\n\n否则只走 Task return value——主 Claude 拿到 JSON 字符串直接解析。\n\n---\n\n## 主 Claude 调用契约（如何使用 channel B）\n\n调 Task 时，主 Claude 的 prompt **必须**含且**仅含**：\n\n```\nSpawn cheat-score-blind sub-agent.\n\nInput:\n  script_path: scripts/2026-05-04_abc123_短title.md\n  rubric_notes_path: rubric_notes.md\n  [optional] sidecar_path: .cheat-cache/blind-scores/<id>.json\n\nTask: 按 rubric_notes 当前公式给上面 script 打分。返回严格 JSON（见 cheat-score-blind/SKILL.md Phase 2 schema）。\n不要读 state file / predictions/ / videos/ 任何其他文件。\n不要询问用户 —— 你没有用户。\n```\n\n**禁止**塞进 Task prompt 的东西：\n- 用户对话的引用 / 摘录\n- \"前一次预测是 X\" / \"实际播放是 Y\" 这种 hint\n- \"用户是观点视频博主，最近发了 N 条\" 这种背景\n- 任何含数字 + \"万/w/k/M\" 的字符串\n- 任何 `predictions/*.md` 路径\n\n主 Claude 调用前自检：把准备发的 prompt 串过一遍 `grep -Ei '播放|阅读|点赞|评论数|实际|retro|复盘|实绩|w$|万$'`——命中 → **改 prompt 重发**，不要硬塞。\n\n---\n\n## Refusals\n\n- 「我作为 sub-agent 同时也读一下 predictions/ 帮你对比下」 → 硬拒。这就是 channel B 存在的全部理由\n- 「你看一下 .cheat-state.json 看 calibration_samples 决定你给的 confidence 高低」 → 硬拒。confidence 只看稿子证据强度，跟用户校准进度无关\n- 「主 Claude 说这条已经发了，你帮我打一份 reconstructed 分」 → 拒。\"已发\"信号本身就是污染。让主 Claude 标 `reconstructed: true` 自己处理，不要让 channel B 介入\n- 「输出我直接 markdown 表格更好读」 → 拒。Phase 2 schema 是 JSON only，主 Claude 解析后再渲染\n\n---\n\n## Known limitations（写在最显眼的地方）\n\n1. **sub-agent ≠ 真独立**：同一个 Claude 模型，RLHF priors 共享。一个全新 context 不会让模型变成另一个判分体系——它只是没看过该次对话的具体污染\n2. **不解决 rubric 设计 bias**：用户自己写的 rubric_notes.md 自然让自己内容显得好。这层 bias 由 Channel C（跨模型 audit）和定期 bump 验证解决\n3. **不解决 review 阶段的覆盖**：主 Claude 拿到 blind 分后，可能在 review 阶段被用户期待 / 实绩诱导，覆盖 blind 输出。`cheat-predict` Phase 2.5 通过 disagreement detection + 用户裁定来减轻，但不消除\n4. **同 prompt 两次调可能给不同分**：Claude 不是 deterministic。主 Claude 应该把每次 blind score 当一次采样，不当唯一真理——但要记录而不是丢弃差异\n\n## Integration\n\n- **`cheat-score`** Phase 2：默认 delegate 到本 sub-agent（替代旧的 inline 打分）\n- **`cheat-predict`** Phase 2：默认 delegate；Phase 2.5 用 disagreement detection\n- **`cheat-bump`** Phase 2：**强制** delegate，bump 时**不接受 self-scored fallback**\n- **`cheat-retro`**：不调用——retro 本来就看实绩，blind 无意义\n\n<!-- File: skills/cheat-score/SKILL.md (Tokens: ~2660 | Category: anthropic-skill) -->\n---\nname: cheat-score\ndescription: 给单篇稿子打 rubric 分。**只在控制台输出，不写文件，不预测**。触发词：\"打分这篇 [path]\"/\"score this [path]\"/\"给这稿子打分\"/\"先打分看看\"。是 cheat-predict 之前的轻量探索动作。\nargument-hint: <draft-path>\nallowed-tools: Read, Glob, Grep\n---\n\n# /cheat-score — 单稿打分\n\n打分但**不预测**。用户用它快速看稿子的 composite，决定是否值得进入正式预测流程。\n\n## Overview\n\n```\n[用户：打分这篇 draft.md]\n  ↓\n[读 draft.md + rubric_notes.md]\n  ↓\n[逐维度打 0-5 + 写一行理由 + 算 composite]\n  ↓\n[控制台输出：评分 + composite + 推荐下一步]\n  ↓\n[结束 — 不写任何文件]\n```\n\n## Constants\n\n- **RUBRIC_PATH = rubric_notes.md** — 当前 rubric 来源\n- **OUTPUT_DETAIL = full** — full: 含每维度理由；compact: 仅分数表\n\n> 💡 调用时覆盖：`/cheat-score draft.md — OUTPUT_DETAIL: compact`\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<draft-path>` | 用户作为参数传入；如缺失则在对话里询问 |\n| `rubric_notes.md` | 用户项目根 |\n| `.cheat-state.json` | 用户项目根（用于读当前 `rubric_version` 与 mode） |\n\n## Workflow\n\n### Step 1：前置检查\n\n1. 读 `.cheat-state.json` → 不存在则提示用户先跑 `/cheat-init`，停止\n2. 读 `<draft-path>` → 不存在或无内容 → 报错并停止\n3. 读 `rubric_notes.md` 找到当前生效的公式段（一般在\"当前评分维度\"或\"综合分公式\"位置）\n\n### Step 2：识别公式与维度\n\n从 `rubric_notes.md` 解析出：\n- 当前 rubric_version\n- 维度列表与权重（如 `ER×1.5 + SR×1.5 + HP×1.5 + QL + NA + AB + SAT`）\n- 归一化常数（如 `/ 8.5 × 2.0`）\n- 每个维度的 0-5 含义（从\"当前评分维度\"段表格读）\n\n如果 `rubric_notes.md` 格式与预期不符（用户手改过结构）→ 询问用户当前公式是哪一行，**不要自己猜**。\n\n### Step 3：**delegate 到 blind sub-agent**（不再 inline 打分）\n\n主对话已经被用户对话 / 已发数据 / 历史 retro 段污染——inline 打分等于带着后视镜判分。\n\n改成**通过 Task tool 调 `/cheat-score-blind` sub-agent**，主 Claude 只做调度 + review。详见 [skills/cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md)。\n\n**Task prompt 模板**（**只能含**下面这些）：\n\n```\nSpawn cheat-score-blind sub-agent.\n\nInput:\n  script_path: <用户给的 draft path>\n  rubric_notes_path: rubric_notes.md\n\nTask: 按 rubric_notes 当前公式给上面 script 打分。返回严格 JSON（见 cheat-score-blind SKILL.md Phase 2 schema）。\n不要读 state file / predictions/ / videos/ 任何其他文件。\n不要询问用户 —— 你没有用户。\n```\n\n**禁止**塞进 Task prompt 的东西（[cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md) 的\"主 Claude 调用契约\"段）：\n- 用户对话引用 / 摘录\n- 含播放数 / 万 / w / k 等字眼\n- \"前一次预测是 X\" / \"实际播放是 Y\" 等 hint\n- 任何 `predictions/*.md` 路径\n\n调用前 grep 自检：`echo \"<prompt>\" | grep -Ei '播放|阅读|点赞|评论数|实际|retro|复盘|实绩|w$|万$'` 命中 → 改 prompt 重发。\n\n### Step 4：解析 sub-agent 回传 JSON + review\n\nsub-agent 返回严格 JSON。主 Claude：\n\n1. 解析 dimensions 段（含 score + per-dim confidence + reason）\n2. 校验 `self_check.any_contamination_signal == false`，否则警告\n3. 按 rubric_notes 公式算 composite（公式逻辑在主，分数来自 sub-agent）\n4. **不修改 sub-agent 给的维度分**——score 只是显示。如果用户挑刺（\"AB 给 3 不是 4\"），主 Claude 记录到 `User Override` 但 sub-agent 原始分留档\n\n如果 sub-agent 返回 `refusal != null`：\n- `blocked_contaminated_input` → 报告 Task prompt 含违禁字段，让主 Claude 重发\n- `script_path_invalid` → 检查路径\n- `rubric_unparseable` → 提示用户 rubric_notes.md 损坏\n- `non_blind_warning` → 仍接受 dimensions（但 confidence 全 medium），警告\n\n### Step 5：算 composite + 输出\n\n按当前公式算综合分。控制台输出（OUTPUT_DETAIL=full）：\n\n```\n📊 [draft.md 短标题] — 打分（rubric: v2）\n\n| 维度 | 分 | 理由 |\n|---|---|---|\n| ER (情感共鸣)        | 5 | \"半夜三点翻聊天记录\" 极端具象 |\n| HP (钩子强度)        | 5 | IS 句一句锁定受众 |\n| QL (金句密度)        | 5 | MVP 句\"间歇性希望\"独立可传 |\n| NA (叙事性)          | 3 | 平铺直叙，弱弧线 |\n| AB (受众广度)        | 5 | 暗恋/前任普适 |\n| SR (社会议题共振)    | 2 | 纯个人情感，无社会托底 |\n| SAT (讽刺深度)       | 4 | 致谢段自指反讽 |\n\n公式：(ER×1.5 + SR×1.5 + HP×1.5 + QL + NA + AB + SAT) / 8.5 × 2.0\ncomposite = (5×1.5 + 2×1.5 + 5×1.5 + 5 + 3 + 5 + 4) / 8.5 × 2.0 = **8.24**\n\n📍 落在 30-100w 桶（基于 starter-rubrics 的 bucket 边界）\n\n下一步建议：\n- 如果你已写定最终稿、准备发布 → 说 \"启动预测\"\n- 如果想再改稿子 → 改完再打一次（多次打分不留痕迹）\n- 如果想看历史相近 composite 的样本 → 说 \"找 composite 8.0-8.5 的锚点\"\n```\n\nOUTPUT_DETAIL=compact 时仅输出分数表 + composite，不附理由列。\n\n### Step 6：**绝不**做的事\n\n- ❌ 写任何文件（包括 predictions/、rubric_notes.md、candidates.md）\n- ❌ 给 bucket 概率分布（那是 cheat-predict 的活）\n- ❌ 触发\"已发布\"或\"复盘\"逻辑\n- ❌ 提议 rubric 升级（即使打分时发现明显异常也只在控制台提示，不动 rubric）\n\n## Key Rules\n\n1. **打分走 sub-agent**。主 Claude 不再 inline 打分。看 [cheat-score-blind/SKILL.md](../cheat-score-blind/SKILL.md) 的隔离协议\n2. **整数分**。不允许 4.5、3.7\n3. **盲打优先**。sub-agent 只看 script + rubric，天然盲——这是它存在的全部理由\n4. **理由是诊断工具**。每个维度的 1-30 字理由不是装饰——复盘时用来找出哪个维度判断错了\n5. **不写文件**。这是 score 与 predict 的核心区别。score 是探索，predict 是承诺\n6. **不算 candidate composite**。candidates.md 里的 composite 字段在 cheat-trends/cheat-recommend 里写——score 只服务\"已写好的具体稿子\"\n\n## Refusals\n\n- 「打分顺便预测一下」 → 拒绝。请改用 `/cheat-predict`。原因：predict 必须走 blind check + 写 immutable 日志，score 跳过这些\n- 「打完分把分数写进 rubric_notes.md 的观察段」 → 拒绝。observation lifecycle 规定观察必须有\"实绩 vs 预测\"对比，光有打分不构成观察\n- 「能不能直接告诉我会不会爆」 → 拒绝。给具体 composite + bucket 的判定要求走 predict 流程；score 只输出当前 rubric 下的机械计算\n- 「跳过 blind sub-agent 让主 Claude 直接打」 → cheat-score **不接受**这种 escape hatch（与 cheat-predict 不同；cheat-predict 有 `--skip-blind`）。score 是轻量探索，没理由放弃隔离。如真的 Task tool 不可用 → 提示用户配置后再试\n\n## Integration\n\n- 是 `cheat-predict` 的前置探索：用户可以反复 score 不同稿子版本，确定一份再 predict\n- score 不更新 `.cheat-state.json`——这是无副作用操作\n- 如果用户连续 score 同一稿子 ≥3 次 → 控制台温和提示\"反复打分会引入决策疲劳，差不多可以决定了\"\n\n<!-- File: skills/cheat-seed/SKILL.md (Tokens: ~9338 | Category: anthropic-skill) -->\n---\nname: cheat-seed\ndescription: 跟用户对话讨论选题——**默认一次一个**，用户主动给主题或经历，AI 围绕用户的输入深挖、提炼角度、写一份 draft。不是 AI 拿三个开放问题追用户，也不是一次给 5 个候选。触发词：\"找选题\"/\"我想做一条 X\"/\"最近有个想法\"/\"seed\"/\"启动种子\"。可选 batch 模式：`/cheat-seed --batch 5` 走旧的 brainstorm 5 候选 + 写 5 draft 流程。\nargument-hint: \"[— batch: N] [— sources: <comma-separated>]\"\nallowed-tools: Bash(*), Read, Write, Edit, Glob, WebFetch, Skill\n---\n\n# /cheat-seed — 选题对话（默认）/ 批量 brainstorm（可选）\n\ncheat-seed 的核心是**跟用户讨论选题**，不是机械地 brainstorm。好内容来自用户的真实经历 + 观察 + 情绪——这些是 AI 不可能凭空 brainstorm 出来的。AI 的角色是**听用户讲 → 帮提炼角度 → 写一份 draft**，不是 dump 15 候选让用户挑。\n\n**默认模式**：对话式一次一个。\n**Batch 模式**（`--batch N`）：保留旧的 brainstorm N 候选 + 写 N 份 draft 流程，给\"完全没想法 + 想批量初始化\"的用户。\n\n## 三种 Mode（自动识别）\n\n```\nMode A — 用户主动给主题（**最常见**）：\n  用户：\"/cheat-seed\" + 直接说\"我想做一条关于 X 的\"\n       或：\"/cheat-seed 我最近开会被领导...\"\n  ↓\n  AI 围绕 X / 这件事**深挖**——什么瞬间触发？最让你 [情绪 / 不爽 / 觉得有意思] 的是哪点？\n  ↓\n  收敛到一个具体角度 → 提议 → 用户认可 → 写 1 份 draft → 完成\n  ↓\n  问\"下一篇？\" 或用户说\"今天就这样\"\n\nMode B — 用户给方向但不具体：\n  用户：\"最近想做点关于 [职场 / 婚恋 / AI / ...] 的\"\n  ↓\n  AI：\"[范围] 太广。最近你接触到的具体哪件事让你想做这个方向？\"\n  ↓\n  收敛到 Mode A 的具体经历\n\nMode C — 用户完全没想法（少见）：\n  用户：\"我不知道做什么\" / \"帮我想个题\"\n  ↓\n  AI：\"好，进 brainstorm 模式——先抓热点 + 你之前的兴趣方向，给你 1 个建议\"\n  ↓\n  跑 trend-sources 抓热点 + 读 candidates.md / predictions/ 看用户历史\n  ↓\n  提议 1 个角度（不是 5 个） → 用户认可 → 写 draft\n\nBatch Mode — 用户显式要批量（`/cheat-seed --batch 5`）：\n  按旧版 brainstorm 流程：3 问题 → 15 候选 → 用户挑 → 写 5 draft。\n  给\"今天想一次性把未来 2 周的选题搞定\"的用户。\n```\n\n**关键纠正**（与旧版的区别）：\n- AI **不主动开放问**——等用户给输入再深挖\n- 一次一个选题，不是 5 个\n- 默认对话式 + 一次一个，batch 是 escape hatch\n\n## Constants\n\n- **DEFAULT_TREND_SOURCES = [\"manual-paste\"]** — 仅 Mode C / Mode A 灰色场景 / Batch 用到。用户可在 state 里加 aihot / trendradar-mcp\n- **TREND_TOOL_ROUTING** — 按 `content_form` 路由数据源，详见 [shared-references/data-source-routing.md](../../shared-references/data-source-routing.md)\n- **MODE_B_MAX_REPROBE_TURNS = 2** — Mode B \"为什么\" 反问最多 2 轮；超过则转 Mode C\n- **MAX_DEEP_DIVE_TURNS = 4** — Mode A 收敛阶段最多 4 轮反问，避免 AI 过度盘问\n- **WITH_DRAFT = yes** — 默认确认角度后立刻写 draft；用户可说 \"等下，我自己写\" 跳过\n- **DRAFT_LENGTH** — 派生自 `state.typical_duration_seconds`：30s→100-200字 / 90s→250-500字 / 240s→600-1000字 / 450s→1100-2000字 / 900s→2200+字\n- **HUMANIZE_DRAFT = on**（默认）/ off —— 写完 draft 后用 `humanizer` skill 过一遍，去掉 AI 写作 tells（em-dash 滥用 / rule of three / inflated 词汇 / 空泛归因等）。off 时直接出原始 AI draft。**只 humanize 正文，不动 header 的\"必须改写\"警告**\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `.cheat-state.json` | 读 calibration_samples / typical_duration / cadence |\n| `rubric_notes.md` | 读当前 rubric（粗打分用） |\n| `script_patterns.md` | 读已有 pattern（写 draft 时按 cheat sheet 选结构） |\n| `predictions/*.md`（如有） | 已发历史，brainstorm 时作为 context |\n| `audience.md`（如有） | 受众画像——选题 / 写稿时的\"谁在看\"镜子（由 `/cheat-persona` 派生） |\n\n## Workflow\n\n### Phase 0: 前置检查 + 加载所有 context（**核心：3 个 context 来源**）\n\n1. 读 `.cheat-state.json` → 不存在则提示先跑 `/cheat-init`\n2. 读 `rubric_notes.md` 拿当前公式（粗打分用）\n3. 读 `script_patterns.md`——写 draft 时按 cheat sheet 选结构\n4. **读已有 prediction 文件**（含 init 时 import 的 reconstructed）作为 **context 来源 A**（用户自己历史）：\n   - 0 个 → A 为空\n   - ≥1 个 → A 有内容，提取 (title / 7 维 / 实绩)\n5. **读 `benchmark.md`**（如存在）作为 **context 来源 B**（对标账号）：\n   - `state.benchmark_status = imported` → B 有内容，提取对标账号的样本主题分布、调性、Pattern\n   - `state.benchmark_status = none / pending` → B 为空\n6. **读 `audience.md`**（如存在且非空骨架）作为 **受众镜子**：\n   - Confidence 🟡 以上（有真实复盘数据派生）→ 选题 / 写 draft 时作为\"这个 persona 会在乎吗\"的检验视角\n   - Confidence 🔴/🟠（空 / 仅 benchmark seed）→ 当弱参考，不当硬约束\n   - **不进粗打分**——audience.md 是 creative lens，粗打分仍只用 rubric。persona 影响\"写什么 / 哪个角度\"，不影响分数\n7. 检查用户的入参——是否含具体 topic / 经历，决定走 Mode A/B/C/Batch\n\n**brainstorm 时的 context 优先级**（**Claude 判断**——下面是参考默认）：\n\n- **A 主导**（用户自己数据）：当 Claude 判断用户数据已能驱动方向时（参考默认：`calibration_samples ≥ 10`，但 Claude 可以更早——如 N=5 但出现 ≥3 个与 benchmark 明显不一致的强样本）\n- **B 主导**（benchmark）：用户数据少 + benchmark 有内容时\n- **B 缺席**（benchmark 为空）+ 用户数据少：纯靠用户 input + 抓热点；明确告诉用户\"没 benchmark 也没足够自己数据，建议跑 /cheat-learn-from 后再回来 brainstorm\"\n\n判断依据**不是死磕样本数**，而是看：\n- 用户最近 N 个样本的实绩**是否与 benchmark 的高表现样本类型一致**——一致说明 benchmark 仍有借鉴价值；不一致说明用户已经走自己的路\n- 用户的样本**多样性**——3 篇都是同类内容不算成熟；3 篇覆盖不同类目反而比 10 篇同类更可信\n\n### Phase 1: Mode 分流\n\n读用户输入，识别：\n\n**含具体名词 + 情绪 / 经历词**（\"我昨天开会...\" / \"我看到 X 让我...\" / \"我对 Y 觉得...\"）→ **Mode A**（深挖；如话题是时事，Phase 2A.5 询问要不要拉外部数据）\n\n**含方向词但无具体内容**（\"想做职场\" / \"AI 方向\" / \"婚恋\"）→ **Mode B**（单问\"为什么想做这个\"——用户内省窗口，**不调任何热点工具**）\n\n**显式说没想法**（\"不知道做什么\" / \"帮我想\" / \"随便给个\"）→ **Mode C**（按 [data-source-routing.md](../../shared-references/data-source-routing.md) 调热点工具 + 用户挑 + 回到内省；都不行则走\"聊经历\"三选项兜底）\n\n**显式 `--batch N`**（用户主动批量）→ **Batch Mode**\n\n**纯 `/cheat-seed` 无附加内容** → **询问入口问题**：\n\n```\n你今天想干嘛？\n\n- 有想做的主题 / 经历 → 直接告诉我（\"我想做一条 X\" / \"我最近 X...\"）\n- 知道大致方向 → 告诉我（\"想做职场\" / \"AI 方向\"）→ 我会单问你为啥想做\n- 完全没想法 → 说\"帮我想\"→ 我用 [aihot / trendradar] 拉今天的热点给你看\n- 批量搞定 → 说 \"batch <N>\"\n\n(我不会拿一堆开放问题追你——你给一句话我就开始)\n```\n\n注意这是**唯一的开放式问题**——只在用户**纯触发** `/cheat-seed` 时问。如果用户已经在触发词里给了内容（\"/cheat-seed 我想做...\" 或 \"找选题 我最近开会...\"），直接进 Mode A/B/C 不再问这一句。\n\n### Phase 2A: Mode A 深挖（用户给了具体 topic / 经历）\n\n**核心原则**：围绕用户给的内容**深挖**，**不要切到别的话题**。\n\n**反问类型（按场景挑）**：\n\n- 触发瞬间：\"你说 X 这件事，最初是哪个具体瞬间触发你想做的？\" / \"是什么让你觉得这值得讲一条视频？\"\n- 情绪锚点：\"这里面最让你 [生气 / 觉得荒唐 / 觉得有意思] 的是哪个细节？\"\n- 角度选择：\"你想说的是 [角度 a：现象批判] 还是 [角度 b：自我反思] 还是 [角度 c：泛化到普遍]？\"\n- 受众想象：\"你心里想着是说给哪种人听？她/他听完会怎么想 / 怎么转发？\"\n- 反对意见探测：\"如果有人反驳说 [反方观点 X]，你会怎么回？\"——逼用户先想清楚立场\n\n**反问纪律**：\n- 一次只问 **1 个**问题（不要塞 3 个连珠炮）\n- 最多 `MAX_DEEP_DIVE_TURNS` 轮（默认 4）——超过就主动收敛：\"OK 我感觉够了，帮你提议一个角度试试\"\n- 用户的回答如果含 emoji / 简短 / 不耐烦 → 立刻收敛，不要逼\n\n**收敛输出**：\n\n```\n我感觉这个角度能做：\n\n[一句话立意：50 字以内]\n\n走法：\n- 用 [Pattern X 结构]（来自 script_patterns.md）\n- 钩子：[具体场景 / 句子]\n- 主体：[3 个观察是什么]\n- 收尾：[MVP 句方向]\n\n粗打分（v0 等权 7 维）：ER=X HP=X QL=X NA=X AB=X SR=X SAT=X → composite ≈ X.X\nConfidence: 🔴 极低 (你才校准 0/N 篇)\n\n要不要让我先写一份 draft？(yes / 换角度 / 我自己写)\n```\n\n用户回 yes → Phase 4 写 draft。\n用户说\"换角度\" → 回 Phase 2A 深挖更多。\n用户说\"我自己写\" → 把 candidate 加进 candidates.md 标 tier1，结束。\n\n### Phase 2B: Mode B — 单问\"为什么\"，触发用户内省\n\n用户给了方向但不具体（\"想做职场\" / \"AI 方向\" / \"婚恋\"）。**这阶段不调任何热点工具**——是用户内省的窗口，外部信息会污染。\n\n只问一个问题，**单刀直入**：\n\n```\n为什么想做这个主题？\n```\n\n不要问\"a/b/c 三种例子\"——那是 dump 选项让用户挑，破坏内省。让用户自己组织语言。\n\n**根据用户回答分流**：\n\n| 用户答 | 分类 | 行为 |\n|---|---|---|\n| 含具体经历 / 个人卡点（\"我自己经常加班\" / \"我看到 X 让我...\"） | **真动机** | 转 Mode A 深挖（Phase 2A） |\n| 抽象热度归因（\"这话题最近热\" / \"别人都在做\" / \"听说能涨粉\"） | **空动机** | 反问\"那你自己对这个话题最有感觉的角度是啥？\"——逼出个人 stake；继续空 → 转 Mode C |\n| \"我也不知道\" / \"朋友说赚钱\" / 模糊推搡 | **真没想法** | 直接转 Mode C |\n\n**反问纪律**：最多 2 轮。第 2 轮还问不出真动机 → 直接转 Mode C，**不要无限挖**。\n\n> 设计意图：Mode B 是\"过滤器\"，不是\"工厂\"。用户来这里要么暴露真实动机（→ 进 Mode A）要么暴露自己其实没想法（→ 进 Mode C）。两条都比硬要在 Mode B 里产出选题更好。\n\n### Phase 2C: Mode C — 外部素材 + 用户挑 + 回到内省\n\n用户完全没想法（直接显式说 / 从 Mode B 转过来）。**这是唯一默认调热点工具的入口**。\n\n按 [shared-references/data-source-routing.md](../../shared-references/data-source-routing.md) 的路由规则：\n\n1. **拉外部素材**（按 `content_form` 选 trend source）：\n   - `tutorial-builder` / AI 类 → 调 aihot skill\n   - `opinion-video` / `long-essay` / `podcast` / `other` → 调 trendradar-mcp（如启用）\n   - `mixed` → 两个都调\n   - 都不可用 → 走 manual-paste（询问用户：\"今天看到啥可以拍的？粘几条 URL/标题\"）\n\n2. **聊经历兜底**（用户拒绝看外部素材 / 外部素材都不感兴趣）：\n\n   ```\n   外部素材你都没感觉，那回到你自己。三个开口，挑一个开始讲：\n\n   a) 你最近真实接触到的某件具体事？（\"上周我看到我同事 X...\"）\n   b) 你最近读到 / 看到的某条让你想吐槽的内容？（\"知乎上有个回答...\"）\n   c) 你长期琢磨的某个 unsolved 困惑？（\"我一直没想明白为啥 X...\"）\n\n   随便挑一个开始讲。\n   ```\n\n3. **拉到外部素材后**，用 rubric 粗筛 + 按 content_form 过滤，留 5 条最契合的：\n\n   ```\n   今天这 5 个跟你形态契合：\n   1. [标题 A]（来源: trendradar / 微博热搜 / hot_score: 8.5）\n   2. [标题 B]（来源: aihot / 模型类 / 精选）\n   3. ...\n\n   你哪个最有感觉？没感觉就回 '都没'，我换方向问。\n   ```\n\n4. **用户挑了一条 → 回到内省**：\n\n   ```\n   OK [标题 X]。你为啥对这条最有感觉？\n   是 [angle1] 还是 [angle2] 还是别的？\n   ```\n\n   → 用户答 → 转 Mode A 深挖。\n\n5. **用户回 \"都没\"** → 转回 Mode C 第 2 步的\"聊经历兜底\"。\n\n**关键**：热点不是\"塞 5 候选让用户挑\"，是\"给材料 + 强制问用户的个人 stake\"——AI 不替用户决定哪条最值得做。\n\n### Phase 2A.5: Mode A 灰色场景 — 用户讲了时事话题\n\nMode A 默认深挖用户经历。但如果**用户讲的本身是时事话题**（产品名 + 时间词 / 人名 + 事件词），按 [data-source-routing.md](../../shared-references/data-source-routing.md) 的\"时事判定\"规则，**询问**用户要不要拉外部数据作参考：\n\n```\n💡 [话题] 是时事——我可以拉一下今天的舆论风向（各平台情绪 + 主要 angles）作参考。\n\n要看吗？回 '看' 我调；回 '不用' 我直接跟你深挖你的角度。\n```\n\n用户回 \"看\" → 调对应 trend source → 把数据 inline 到深挖 context；\n用户回 \"不用\" → 标准 Mode A 深挖，不动外部数据。\n\n**永远不主动调**——用户的 angle 优先于外部数据，避免外部信息**带偏**用户视角。\n\n### Phase 2D: Batch Mode（用户显式 `--batch N`）\n\n**保留旧 brainstorm 流程**：\n\n1. 问 3 个清单问题（兴趣 / 调性 / 红线）—— Batch 模式才问这些\n2. 抓热点 + Claude brainstorm 15 候选\n3. 用户挑 N\n4. 写 N 份 draft 到 scripts/——**每份都走 Phase 4 的段落版格式 + Phase 4.5 自检**（line-format + humanizer），不因为批量就跳过\n\n详见 commit history（旧 cheat-seed 的 Phase 1-3）。这是 escape hatch，不是默认。\n\n### Phase 3: 计算 candidate ID + 落候选池\n\n不管 Mode A/B/C 哪条路径，确认角度后：\n\n1. 算 candidate id：`sha256(\"seed-\" + 立意 + 触发时间)[:12]`\n2. 写一行 entry 到 `candidates.md`（按 [candidate-schema.md](../../shared-references/candidate-schema.md) 格式）\n3. 标 `tier=tier1` + `read_status=deep_read`（已经讨论过，不是 skim）\n\n### Phase 4: 写 draft\n\n`WITH_DRAFT=yes` → 顺次写到 `scripts/<YYYY-MM-DD>_<id>_<short-title>.md`：\n\n**写 draft 前必读** `script_patterns.md` —— 按\"结构选型 cheat sheet\"对应用户的 topic 选合适结构。如果文件还在抽象骨架阶段（用户没填几个 pattern），就用 starter rubric 对应的通用框架。\n\n**字数**：按 `DRAFT_LENGTH` 派生（基于 `typical_duration_seconds`）。\n\n#### ⚠️ 正文必须是段落版，不是字幕格式（**最常见的生成跑偏**）\n\n模型的训练先验会把\"视频脚本\"默认写成提词器/字幕的短行格式。**这是错的**——cheat-seed 的 draft 是给用户**改写**的散文稿，不是拍摄字幕。字幕是剪映拍后自动断的，不是写作时的形态。\n\n生成正文时，眼睛盯住下面这个对照：\n\n```\n❌ 字幕格式（不要这样写）：\n你有没有发现\n所有审稿人都在说一样的话\n你的研究太老套了\n但你仔细看\n他们引用的全是 5 年前的反应\n\n✅ 段落版（必须这样写）：\n你有没有发现，所有审稿人都在说一样的话——你的研究太老套了。但你仔细看，他们引用的全是 5 年前的反应。AI 不是新东西，新的是这次大家集体觉醒了。\n```\n\n规则：\n- **每段 100-300 字**，逗号 / 句号 / 破折号自然连，**不在句子边界硬断行**\n- 段与段之间空一行（自然的主题切换才换段）\n- 一份 draft 正文一般 3-6 段，**不该有几十个单句行**\n\n#### 格式：\n\n```markdown\n# [立意标题]\n\n> ⚠️ **Draft by Claude — 你必须改写后再拍**\n>\n> 这是脚手架，不是成品。你的语气 / 节奏 / 个人经历无法 AI 生成。\n> 改写流程：\n> 1. **直接在本文件改写**（同 path：scripts/<...>.md）\n>    - 加你的语气、个人经历、真实金句\n>    - 砍铺垫、砍模型缩写、砍学术包装\n> 2. 改完后跑 `/cheat-predict scripts/<本文件>.md`\n> 3. 拍完跑 `/cheat-shoot scripts/<本文件>.md`\n\n**Article ID**: <12 位 hash>\n**调性**: [基于讨论得出的，不是清单 Q]\n**目标时长**: <state.typical_duration_seconds 转换> 分钟\n**目标字数**: <按时长派生>\n**结构选型**: [按 script_patterns.md 的 cheat sheet 显式标，如 \"metaphor 优先\" / \"数据反转开场\"]\n**用到的 patterns**: [编号 + 简短说明]\n**讨论种子**: [一句话回顾 deep dive 出来的核心]\n\n---\n\n[draft 正文 —— **段落版**，3-6 段，每段 100-300 字，不是单句碎行]\n```\n\n`WITH_DRAFT=no`（用户说\"我自己写\"）→ 跳过 Phase 4 + Phase 4.5。\n\n### Phase 4.5: draft 自检 pass（版式 + 去 AI 味）\n\ndraft 写完落盘后、**在展示给用户前**跑两步自检。**顺序固定：先 4.5a 修版式，再 4.5b 去 AI 味**——humanizer 处理散文，喂它字幕格式的碎行会乱。\n\n**为什么安全**（不污染校准）：cheat-seed 的 draft 不是被预测/发布的东西——用户改写后、cheat-predict 打分的是**用户最终稿**。这两步只是给用户更干净的起点。\n\n#### Phase 4.5a: line-format 自检（字幕格式 → 段落版）\n\nPhase 4 的散文指令 + ❌/✅ 对照已经在生成时压先验，但生成仍可能跑偏。这一步是**确定性兜底**：\n\n```bash\n# 只看正文段（--- 分隔线之后）\nbody=$(awk '/^---$/{f=1;next} f' scripts/<id>.md)\nline_count=$(printf '%s\\n' \"$body\" | grep -c .)        # 非空行数\nchar_count=$(printf '%s' \"$body\" | wc -m | tr -d ' ')\navg_chars_per_line=$(( char_count / (line_count > 0 ? line_count : 1) ))\n```\n\n判定：**`avg_chars_per_line < 15` 且 `line_count >= 8`** → 判定为字幕格式 → **自动重排**：\n- 把句子边界的硬断行合并回自然段落\n- 按主题切换分 3-6 段，每段 100-300 字\n- 用 Edit 替换正文段（header 不动）\n- 在 Phase 5 输出里标一行：\"📐 检测到字幕格式，已重排为段落版\"\n\n不命中 → 跳过，正文已经是段落版。\n\n#### Phase 4.5b: humanizer 去 AI 味\n\n`HUMANIZE_DRAFT=on`（默认）—— 用 `humanizer` skill 过一遍。Claude 自己写的初稿天然带 AI tells（em-dash 滥用 / rule of three / \"inflated\" 词汇 / 空泛归因 / -ing 浅层分析），这一步把它们清掉。\n\n步骤：\n\n1. 检查 `humanizer` skill 是否可用（`~/.claude/skills/humanizer/` 存在）：\n   - 不可用 → 跳过 4.5b，在 Phase 5 输出里加一行\"（humanizer 未装，draft 是原始 AI 版——`git clone https://github.com/blader/humanizer` 到 ~/.claude/skills/ 可启用自动去 AI 味）\"\n2. 可用 → 通过 Skill tool 调 `humanizer`，**只传 draft 正文**（`---` 分隔线之后、4.5a 已重排好的段落版），**绝不传 header**：\n   - header 的 `⚠️ Draft by Claude — 你必须改写后再拍` 警告是**有意的脚手架标记**，不是要 humanize 的散文\n   - **voice calibration**：如果用户有历史脚本（`videos/*/script.md`）或填过 `script_patterns.md`，把最近 1-2 份作为 humanizer 的 voice 参考样本一起传——让它往\"**这个用户的声音**\"靠，而不是\"通用人声\"\n3. humanizer 返回去 AI 味的正文 → 用 Edit 替换 draft 文件的正文段（header 不动）\n4. 记录 humanizer 报告的\"修了哪些 tell\"（如 `em-dash 滥用 ×3 / rule of three ×2 / inflated 词汇: \"深刻\" \"本质上\"`），Phase 5 输出里展示\n\n**纪律**：\n- humanizer 是**去 AI 味**，不是**替用户改写**。它让 draft 不那么像机器写的，但**仍不是用户的声音**——header 的\"必须改写\"警告依然成立，Phase 5 输出要重申\n- 如果 humanizer 把某句改得偏离了 `结构选型` / 用到的 pattern → 以 pattern 为准，那句回滚（pattern 是和用户讨论定的，humanizer 不该推翻）\n- humanizer **不负责版式**——断行问题在 4.5a 已经修完，humanizer 拿到的已是段落版\n\n### Phase 5: 输出\"下一步\" + 询问继续\n\n```\n✅ Draft 写完：scripts/2026-05-04_<id>_<short>.md\n📐 版式自检：通过（段落版）  ← 或\"检测到字幕格式，已重排为段落版\"\n🧹 humanizer 过了一遍：修了 em-dash 滥用 ×3 / rule of three ×2 / inflated 词汇 2 处\n   （draft 现在不那么\"机器味\"了——但这仍是脚手架，不是你的声音）\n\n接下来你可以：\n- 改写这份 draft（直接在原文件改）—— 加你的语气、经历、真实金句\n- 改完跑 \"打分这篇 scripts/<...>.md\" 看 7 维评分\n- 决定要拍 → \"启动预测 scripts/<...>.md\"\n\n下一篇你想做什么？\n（直接告诉我具体经历 / topic，或者说\"今天就这样\"结束）\n```\n\n> humanizer 那行只在 `HUMANIZE_DRAFT=on` 且 skill 可用时出现。未装时换成一行提示如何启用。\n\n用户说\"今天就这样\" → 结束 cheat-seed。\n用户给新 topic → 回 Phase 1 重新分流。\n\n## Key Rules\n\n1. **AI 不主动开放问**——只在用户纯触发 `/cheat-seed` 时问一次入口问题，其他时候**等用户给输入再深挖**\n2. **一次一个选题**——默认 Mode A/B/C 都给 1 个建议；用户主动要批量才走 Batch\n3. **反问纪律**：一次问 1 个，最多 4 轮，用户不耐烦立刻收敛\n4. **深挖围绕用户给的话题**，不要切到别的——你说\"开会被领导骂\"，AI 不该问\"那你最近有没有觉得 AI 让大家...\"这种平行话题\n5. **写 draft 必须读 script_patterns.md**——按用户已有 pattern 选结构\n6. **draft 是脚手架**——header 加醒目警告\"必须改写\"\n7. **humanizer 只去 AI 味，不替用户改写**——Phase 4.5b 让 draft 不那么机器味，但它仍不是用户的声音；\"必须改写\"的警告不因为过了 humanizer 就失效\n8. **正文是段落版不是字幕格式**——生成时盯 Phase 4 的 ❌/✅ 对照；Phase 4.5a 用 `avg_chars_per_line < 15 且行数 ≥ 8` 做确定性兜底，命中就重排。字幕是剪映拍后自动断的，不是写作时的形态\n\n## Refusals\n\n- 「跳过深挖，直接写 draft」 → 询问\"你想直接给主题让我写吗？OK 但 draft 质量可能差——我不知道你的角度。给我一句话立意我就写\"\n- 「AI 替我决定 topic」 → 拒绝。Mode A/B/C 路径里 AI 永远只**呈现外部素材** + **问用户角度**，不替用户拍板\"做哪条\"\n- 「Mode B 我懒得回答为什么，直接给我 5 个候选吧」 → 拒绝。Mode B 的\"为什么\"是过滤器——你答不出来就不该用 Mode B 的方向。要么进 Mode A 给具体经历，要么进 Mode C 我帮你找素材\n- 「Mode A 时直接帮我拉热点，不问我同意」 → 拒绝。Mode A 用户已经有 angle，未经允许拉外部数据会污染他的视角。详见 [data-source-routing.md](../../shared-references/data-source-routing.md)\n- 「一次写 5 个 draft」 → 不在默认流程。用户必须显式 `--batch 5`\n- 「我懒得改写，直接拍 AI draft」 → 警告\"AI 直接生成的稿子拍出来 ER 偏低，会污染你的校准数据\"，但用户坚持也允许（标 `unmodified_ai_draft: true`）\n\n## Integration\n\n- 上游：`/cheat-init` Phase 5 末尾在 `pool_status=none + calibration_samples=0` 时主动询问\"现在跑 /cheat-seed？\"\n- 上游：`/cheat-recommend` 在 candidates 空时引导文案中提及 `/cheat-seed`\n- 上游：`/cheat-status` 在 `pool_status=none + 距 init >24h` 时提示\"还没拍——跑 /cheat-seed？\"\n- 下游：用户的 candidate → candidates.md（tier1，已 deep_read）\n- 下游：（默认）draft → Phase 4.5 humanizer 去 AI 味 → scripts/<id>.md → 用户改写 → /cheat-predict\n- 可选依赖：[`humanizer`](https://github.com/blader/humanizer) skill（MIT，外部项目）。装在 `~/.claude/skills/humanizer/` 时 Phase 4.5 自动启用；未装则优雅跳过。**不打包进 cheat-on-content**——用户自己 clone\n- 与 `/cheat-trends` 区别：cheat-seed 是**讨论 + 写 draft**（重 conversation）；cheat-trends 是**多 adapter 抓 + 粗打分**（重 fetch）。两者目的不同，不互相替代。\n\n<!-- File: skills/cheat-shoot/SKILL.md (Tokens: ~3903 | Category: anthropic-skill) -->\n---\nname: cheat-shoot\ndescription: 登记一条视频已拍摄。**建 video folder + 询问实际拍摄稿是否与 scripts/<id>.md 一致 + buffer +1**。与 cheat-publish 配对：拍了进队列，发了出队列。触发词：\"拍了\"/\"拍了 X\"/\"shot\"/\"shot it\"/\"已拍 X\"/\"录完了\"。\nargument-hint: <scripts-path-or-id>\nallowed-tools: Bash(*), Read, Write, Edit, Glob\n---\n\n# /cheat-shoot — 登记拍摄完成 + 建 video folder + (改稿则) 触发 v2 预测\n\n把视频从\"已写预测、未拍摄\"状态推进到\"已拍摄、未发布\"状态。这一步：\n1. **建 `videos/<同 id>/`** 目录（之前没有的话）\n2. **询问用户**：\"实际拍摄时用的稿子和 `scripts/<id>.md` 一致吗？\"\n3. 算 diff——超过 V2_TRIGGER_THRESHOLD (默认 30%) → **delegate 到 `/cheat-predict — mode: v2`** 在原 prediction 文件 append `## 预测 v2` 段\n4. 把 video folder 加进 state.shoots 队列，buffer +1\n\ncheat-shoot 自己**不**写预测内容——所有预测落盘逻辑在 cheat-predict。cheat-shoot 只负责检测改稿 + 派发。\n\n为什么单独一个 skill：\n- buffer 警戒系统需要明确区分\"拍了\" vs \"发了\"。视频可以批量拍（一天拍 5 条），分散发（每天发 1 条）\n- \"实际拍摄稿\" ≠ \"pre-shoot 草稿\"是常态。这一步是把 diff 显式化、触发 v2 重判、采集\"用户改稿 pattern\"信号的入口\n- v2 预测 vs v1 预测的差异本身就是 rubric 升级证据——比如 v1 给 ER=4，v2 给 ER=5（用户改稿改高了 hook 强度），就告诉 rubric \"这个用户的 ER 阈值跟我现在公式不一致\"\n\n## Overview\n\n```\n[用户：拍了 scripts/2026-05-04_abc123_停止期待.md]\n  ↓\n[Phase 0: 解析路径 + 验证 prediction 已存在]\n  ↓\n[Phase 1: 检查是否已登记（避免重复）]\n  ↓\n[Phase 2: 建 videos/<id>/ + 询问\"实际拍摄稿一致吗？\"]\n  ↓\n[Phase 3: 写 videos/<id>/script.md]\n  ↓\n[Phase 4: append state.shoots]\n  ↓\n[Phase 5: 输出 buffer 状态]\n```\n\n## Constants\n\n- **REQUIRE_PREDICTION = true** — 拍前必须先有 v1 prediction 文件\n- **V2_TRIGGER_THRESHOLD = 0.30** — normalize 后 char-level diff 超过 30% → 默认建议 v2 重判；低于 30% 询问用户是否仍要 v2\n- **DIFF_METRIC = char_levenshtein_normalized**（**默认**）—— 通过 [`tools/diff_pct.py`](../../tools/diff_pct.py) 调用：先 normalize（去 markdown header / 分隔线 / 列表标记 / 装饰标点 / 折叠所有空白），再算 char-level Levenshtein / max(len_a, len_b)。preferred backend `rapidfuzz`，fallback `difflib.SequenceMatcher`（stdlib，永远可用）。**旧版 line-level 在口语化转录场景误报严重**（draft 长 markdown 句 vs whisper 转录的短断句，内容几乎不变但 line-level 算出 ~200% diff）—— PR #14 修复\n- **DIFF_METRIC=lines** —— legacy fallback：当 python3 完全不可用或 tools/diff_pct.py 找不到时降级到 `diff -u | grep '^[+-]' | wc -l` 算法\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `<scripts-path-or-id>` | 用户参数；缺失则询问 |\n| `.cheat-state.json` | 状态文件 |\n| `scripts/*.md` | pre-shoot 草稿 |\n| `predictions/*.md` | 验证对应预测存在 |\n\n## Workflow\n\n### Phase 0：解析 + 验证\n\n1. 解析用户给的路径——支持几种形态：\n   - 完整路径 `scripts/2026-05-04_abc123_停止期待.md`\n   - 简写 `2026-05-04_abc123_停止期待`\n   - id 简写 `abc123` → glob `scripts/*_abc123_*.md` 找匹配\n2. 验证 `scripts/<id>.md` 存在：不存在 → 报错\"找不到 pre-shoot 草稿\"\n3. 验证有对应 prediction `predictions/<同名>.md`：\n   - 不存在 → **拒绝登记**，提示\"先跑 /cheat-predict 写预测，否则违反盲预测原则——你不能拍完才写预测，那等于事后看了画面写\"\n   - 存在 → 通过\n\n### Phase 1：检查重复\n\n读 `.cheat-state.json`，检查 `shoots[]` 是否已含此 id：\n- 已存在 → 警告\"已登记过（X 天前）。是要重新登记，还是要用 /cheat-publish 发布？\"\n- 不存在 → 进入 Phase 2\n\n### Phase 2：建 video folder + 询问稿子一致性\n\n1. 建目录 `videos/<id>_<short>/`（同 scripts/ + predictions/ 的命名）\n2. **询问用户**：\n\n```\n拍 「<title>」 的时候，你实际用的稿子和 scripts/<id>.md 一致吗？\n\na) 一致——按草稿拍的\nb) 改了一些——你能给我看下实际拍摄稿吗？我重新打分一次（v2 预测）\nc) 大改了，基本是另一条 → 走 _redo 流程：\n   scripts/<id>_redo.md → 重新 cheat-predict → 再 cheat-shoot（原 prediction 留档脱钩）\n```\n\n### Phase 3：写 videos/<id>/script.md + (b 路径) 触发 v2 预测\n\n**a 路径（一致）**：\n- `cp scripts/<id>.md → videos/<id>/script.md`\n- `script_consistency = consistent`\n- 不重判，进 Phase 4\n\n**b 路径（改了）**：\n1. 询问用户实际拍摄稿——粘贴文本 / 文件路径 / 转录文件\n2. 若用户提供 → 写入 `videos/<id>/script.md`\n3. 若用户没保留（即兴）→ 标 `script_lost`，写占位文件 + 警告\"v2 重判跳过——下次建议留稿（哪怕 voice memo 转录）\"，进 Phase 4\n4. 提供了的话：算 diff\n   ```bash\n   # 解析 cheat-on-content 源码根（cheat-shoot 是 symlink 装的）\n   SKILL_REAL=\"$(readlink -f ~/.claude/skills/cheat-shoot 2>/dev/null || readlink ~/.claude/skills/cheat-shoot 2>/dev/null)\"\n   if [[ -n \"$SKILL_REAL\" ]]; then\n     REPO_ROOT=\"$(cd \"$SKILL_REAL/../..\" && pwd)\"\n     DIFF_TOOL=\"$REPO_ROOT/tools/diff_pct.py\"\n   fi\n\n   if [[ -n \"${DIFF_TOOL:-}\" && -f \"$DIFF_TOOL\" ]] && command -v python3 >/dev/null 2>&1; then\n     # 默认 char-level Levenshtein on normalized text（rapidfuzz preferred, difflib fallback）\n     diff_pct=$(python3 \"$DIFF_TOOL\" \"scripts/<id>.md\" \"videos/<id>/script.md\")\n   else\n     # legacy line-level fallback——只在 python3 或 diff_pct.py 都不可用时用\n     added=$(diff -u scripts/<id>.md videos/<id>/script.md | grep -c '^+')\n     removed=$(diff -u scripts/<id>.md videos/<id>/script.md | grep -c '^-')\n     total_orig=$(wc -l < scripts/<id>.md)\n     diff_pct=$(( (added + removed) * 100 / total_orig ))\n     echo \"⚠️  fallback 到 line-level diff——口语化转录会 inflate diff_pct，可能误触发 v2\"\n   fi\n   ```\n\n   **为什么 normalize + char-level**：line-level diff 在创作者真实场景（draft 是 markdown 长句、拍摄稿是 whisper 转录的口语化短行）算出 ~200% 差异但内容几乎不变。char-level Levenshtein 在 normalize 后稳定反映**内容**差异，而非格式差异。详见 [`tools/diff_pct.py`](../../tools/diff_pct.py) + `tools/diff_pct_test.sh`（3 fixture 在两个 backend 上全过）。\n5. **判定 v2 触发**：\n   - `diff_pct >= 30` → 默认建议 v2 重判，**主动调用** `/cheat-predict — mode: v2 — prediction-file: predictions/<id>.md` 传 `videos/<id>/script.md` 作 input。cheat-predict 走 v2 模式 append `## 预测 v2`\n   - `diff_pct < 30` → 询问用户：\"只改了 N% 的内容，要重判吗？默认不（v1 预测仍有效）\"。用户说要 → 同上调用；用户说不 → 跳过 v2，继续 Phase 4\n6. cheat-predict 完成 v2 落盘后，控制权回到 cheat-shoot 进 Phase 4\n\n**c 路径（大改）**：\n- 不写 `videos/<id>/script.md`，提示走 `_redo` 流程\n- 退出 cheat-shoot（不进 Phase 4）\n\n### Phase 4：state 更新\n\n```json\n{\n  \"shoots\": [\n    ...,\n    {\n      \"video_folder\": \"videos/2026-05-04_abc123_停止期待/\",\n      \"prediction_file\": \"predictions/2026-05-04_abc123_停止期待.md\",\n      \"scripts_path\": \"scripts/2026-05-04_abc123_停止期待.md\",\n      \"shot_at\": \"<ISO timestamp>\",\n      \"script_consistency\": \"consistent\" | \"modified\" | \"lost\",\n      \"script_diff_pct\": <0-100 int 或 null>,\n      \"v2_prediction_written\": <true/false>,\n      \"script_hash_at_shoot\": \"<sha256:12 of videos/<id>/script.md>\"\n    }\n  ]\n}\n```\n\n`v2_prediction_written: true` 表示 prediction 文件里现在有 `## 预测 v2` 段，cheat-retro 应读 v2 算偏差；`false` 表示沿用 v1。\n\n### Phase 5：输出 buffer 状态\n\n读完 state 后立即算 buffer + 颜色（按 [cadence-protocol.md](../../shared-references/cadence-protocol.md) 的派生规则）：\n\n```\n✅ 已登记拍摄：videos/2026-05-04_abc123_停止期待/\n   预测文件：predictions/2026-05-04_abc123_停止期待.md\n\n📦 当前 buffer：3 篇（🟢 绿色，正常）\n   按你的 cadence（隔日更）= 6 天 buffer，节奏稳定。\n\n下一步：拍其他候选 / 等下个发布日 / 不动\n```\n\n如果 buffer 颜色变了（如从绿到蓝）→ 高亮提醒：\n```\n📦 当前 buffer：6 篇（🔵 蓝色，**积压**）\n⚠️  建议暂停拍摄，全力发布存货 + 复盘。\n   按你的 cadence（日更）= 6 天预备，已超过健康上限。\n```\n\n## Key Rules\n\n1. **不写 prediction**——拍了 ≠ 发了。预测在 /cheat-predict 锁，拍只是事件\n2. **不动 video folder 内容**——script.md / draft-v0.md 都不改\n3. **必须先有 prediction**——否则违反盲预测（拍完看了画面再写预测 = 数据泄漏到判断）\n4. **buffer 计算实时**——每次 shoot / publish 后立刻重算，state.shoots 是真值\n5. **支持批量**：用户可以一天连说 \"拍了 X / 拍了 Y / 拍了 Z\" 三次连续登记\n\n## Refusals\n\n- 「拍了 X，但我从来没跑过 cheat-predict」 → 拒绝。v1 预测**必须拍前写**——拍完才写预测会被画面诱导事后修改。请先 /cheat-predict 写 v1 再来 /cheat-shoot。（v2 重判是另一回事——v1 已存在 + 拍后改稿才允许）\n- 「我没有 video folder，我直接拍的」 → 询问用户 → 帮他建 video folder + 提示下次走完整流程；登记时标 `ad_hoc: true`\n- 「我改稿了但你直接覆盖 v1 吧，别留 v2 段」 → 拒绝。v1 是档案，v2 才是当前判断——append 不覆盖。两段一起留是 rubric 学习的关键证据\n\n## Integration\n\n- 上游：`/cheat-predict` 写完 prediction → 用户拍摄 → `/cheat-shoot` 登记\n- 下游：`/cheat-publish` 发布时把对应项从 state.shoots 移除\n- `/cheat-status` 看板的 buffer 数字直接来自 `state.shoots.length`\n- `/cheat-recommend` 看 buffer 颜色调推荐策略\n- SessionStart hook 看 buffer 颜色决定报告第一行\n\n## state.shoots 数据结构\n\n```json\n{\n  \"shoots\": [\n    {\n      \"video_folder\": \"videos/2026-05-04_abc123_停止期待/\",\n      \"prediction_file\": \"predictions/2026-05-04_abc123_停止期待.md\",\n      \"shot_at\": \"2026-05-04T18:30:00+08:00\",\n      \"ad_hoc\": false  // true if user shot without going through full flow\n    }\n  ]\n}\n```\n\n按 `shot_at` 升序——最早拍的在前面。`/cheat-status` 显示最早一项的 days-since-shoot 警告（避免有视频拍了 30 天没发）。\n\n<!-- File: skills/cheat-status/SKILL.md (Tokens: ~3309 | Category: anthropic-skill) -->\n---\nname: cheat-status\ndescription: cheat-on-content 的状态看板。显示当前模式 / rubric 版本 / 校准进度 / 待复盘 / pool 状态 / 是否该升级 SQLite / 是否该 bump rubric。**任何时候都可调，无副作用**。触发词：\"状态\"/\"看板\"/\"status\"/\"我现在该做什么\"/\"进度怎么样\"。\nallowed-tools: Bash(*), Read, Glob, Grep\n---\n\n# /cheat-status — 状态看板\n\n读 state file + 扫描用户项目 → 汇总当前进度 → 输出\"今天该做什么\"清单。\n\n## Overview\n\n```\n[用户：状态]\n  ↓\n[Phase 1: 读 .cheat-state.json + 扫文件系统]\n  ↓\n[Phase 2: 计算派生指标]\n  ↓\n[Phase 3: 检测建议触发器（升级 / bump / 清算）]\n  ↓\n[Phase 4: 输出看板]\n```\n\n## Constants\n\n- **SQLITE_UPGRADE_THRESHOLD = 30** — calibration_samples 达到 N 时建议升 SQLite\n- **CLEANUP_LINE_THRESHOLD = 600** — rubric_notes.md 行数超 N 时建议清算\n- **STALE_PREDICTION_DAYS = 30** — in_progress prediction 超 N 天未发布提示清理\n\n## Inputs\n\n| 来源 | 用途 |\n|---|---|\n| `.cheat-state.json` | 主要状态 |\n| `predictions/*.md` | 校准样本数 / pending retros |\n| `candidates.md` | 候选池规模 |\n| `rubric_notes.md` | 行数 / 当前版本 |\n| `.cheat-cache/usage.jsonl`（如有） | meta-logging 数据，用于\"距上次 bump 多少次预测\" |\n\n## Workflow\n\n### Phase 1: 读状态\n\n```python\nstate = read_json('.cheat-state.json')\nif not state:\n    return \"你还没初始化。请先跑 /cheat-init。\"\n\npredictions = glob('predictions/*.md')\ncandidates_count = parse_candidates_md_entries()\nrubric_lines = wc -l rubric_notes.md\n```\n\n### Phase 2: 派生指标\n\n| 指标 | 算法 |\n|---|---|\n| **Buffer 数** | `len(state.shoots)` |\n| **Buffer 颜色** | 按 [cadence-protocol.md](../../shared-references/cadence-protocol.md) 派生：`buffer_days = buffer_count × target_publish_cadence_days`，`<1 红 / 1-2 橙 / 3-5 绿 / >5 蓝`。如 `target_publish_cadence_days=null` → 颜色禁用 |\n| **Confidence 等级** | 按 [state-management.md confidence 表](../../shared-references/state-management.md) 派生：从 `calibration_samples` 整数派生 emoji + 标签 |\n| **最早一拍至今天数** | `now - state.shoots[0].shot_at`，用于警告\"拍了 N 天没发\" |\n| 校准样本数 | predictions 中含完整复盘段（实绩数据非空）的文件数 |\n| 待复盘 | state.pending_retros 中已过 RETRO_WINDOW_DAYS 的 |\n| 池大小 | candidates.md 中 tier!=skip 的 entry 数 |\n| 上次 bump 至今几次预测 | predictions 中 published_at > state.last_bump_at 的数量 |\n| 同向偏差队列 | state.consecutive_directional_errors |\n| in_progress 陈旧度 | now - state.in_progress_session.started_at（如有） |\n\n### Phase 3: 检测建议触发器\n\n按优先级（高→低）逐项检查：\n\n1. **Buffer 颜色 = 🔴 红** → 第一行高优先级警戒：\"buffer 已 0/1 篇，下个发布日可能断更——今天必须拍 ≥1 条。说'推荐选题'我只推 top 1 稳分（不推实验性）\"\n2. **Buffer 颜色 = 🔵 蓝** → 高优先级提示：\"buffer 已 N 篇积压。**暂停拍摄**，先发存货 + 复盘。说'已发布 ...'我帮你出队\"\n3. **state.shoots 中最早一项 shot_at > 14 天** → \"你有视频拍了 N 天还没发——议题时效流失风险，建议尽快发或弃稿\"\n4. **in_progress 陈旧** (>= STALE_PREDICTION_DAYS) → 高优先级提示\"清理或 publish\"\n5. **待复盘 ≥ 1** → 高优先级\"今天该复盘 X 篇\"\n6. **`pool_status=none` + `calibration_samples=0` + 距 init >24h** → \"🌱 你 init 完已经 N 天但还没拍——是因为没选题吗？跑 /cheat-seed 5 分钟拿 5 个候选 + 5 个 draft\" 高优先级\n7. **Claude 判断系统性偏差信号**（**不是死磕 ≥3 同向**） → 提示\"建议跑 /cheat-bump\"\n   - **默认参考**：连续 ≥3 次同向偏差\n   - **但 Claude 可以更早**：1 次极端偏差（≥10x）或 2 次同向 + 评论区强反向证据\n   - **也可以更晚**：3 次同向但每次幅度都 <25%（可能只是噪声）\n   - 提示时显式标注：\"本次是 [default-aligned] / [judgment-driven]\"\n8. **calibration_samples 跨入新 confidence 等级**（0→1, 2→3, 5→6, 10→11, 20→21）→ 提示\"🎉 confidence 升级：<旧等级> → <新等级>。bucket 中枢精度从 ±X% 提到 ±Y%\"。**仅作通知，无任何用户必须确认的操作**——所有 skill 都已经按 calibration_samples 自动调整\n9. **calibration_samples 跨过 5** → \"你的 rubric 形态可以第一次正式 bump 了。回顾 rubric_notes.md 看观察记录段是否有 ≥3 样本支持的 pattern → 跑 /cheat-bump\"\n10. **calibration_samples 跨过 10** → \"可以跑 /cheat-bump --bucket-only --scheme percentile 让 bucket 边界改用 percentile（永远自洽）\"\n11. **calibration_samples 跨过 SQLITE_UPGRADE_THRESHOLD** 且 data_layer=markdown → \"建议跑 tools/md-to-sqlite.py\"（planned — batch 3, not yet available）\n12. **rubric_notes.md 行数 > CLEANUP_LINE_THRESHOLD** → \"建议清算观察段（手动或下次 bump 触发）\"\n13. **calibration_samples ≥ 5 + pool_status=none** → \"可以开始建立选题池了\"\n14. **calibration_samples ≥ 15 + pool_status=none** → \"强烈建议建池：/cheat-trends 或手动建 candidates.md\"\n15. **state.hooks_installed=false** → \"你的 immutability 是君子协定，建议跑 /cheat-init 装 hook\"\n16. **state.last_bump_self_audited=true** → \"上次 bump 是自审。建议配置 mcp__llm-chat__chat 后下次 bump 走外部审\"\n17. **state.rubric_form_mismatch=true** → \"你的 content_form 不是 opinion-video，用了内置观点 rubric。前几篇预测会更不准，下次 bump 时建议自行调整权重适配你的形态\"\n18. **state.benchmark_status=pending** → \"🎯 你 init 时答应等下找对标账号但还没找。跑 /cheat-learn-from 导入 ≥3 条对标视频，工具就有 anchor 了\"\n19. **state.benchmark_status=imported + Claude 判断用户数据信号已超过 benchmark** → \"📊 你的真实数据已经成为主信号，benchmark 影响淡出\"\n   - **默认参考**：calibration_samples ≥ 10\n   - **但 Claude 可以更早**：N=5 但用户的 (打分, 实绩) 配对里出现 ≥3 条与 benchmark pattern 不一致的——说明你的账号已经走出对标的路径\n   - **也可以更晚**：N=15 但用户的样本都很相似，没足够多样性 → benchmark 仍有信号价值\n   - 提示是**通知不是 gate**——benchmark.md 永远保留作 sanity check，cheat-seed 仍可读\n\n### Phase 4: 输出看板\n\n```\n🎛️ cheat-on-content 状态（更新于 2026-05-04 15:00）\n\n内容形态：opinion-video / 时长 3-5min / cadence: 隔日更\n当前 rubric：v2 (上次 bump: 2026-04-22)\n校准样本：18 篇\nConfidence: 🟢 较高 (中枢 ±15%，rubric 形态稳定)\nBaseline: 4.2w 中位数\n\n📦 Buffer：3 篇（🟢 绿色）\n   按你的 cadence (隔日更)= 6 天 buffer，节奏稳定\n\n📊 进度条\n  [█████████████░░░░░] 18 / 30 → SQLite 升级建议门槛\n  [██████████░░░░░░░░] 18 / 10 → percentile 桶可用（已超过门槛）\n\n🎬 待办（按紧急度）\n  🚨 复盘 1 篇（已过 T+3d）\n     - predictions/2026-05-01_db063817_你已不在关系里.md（T+3d 到了）\n  ⚠️  同向偏差 3 次（high, high, high）→ 建议 /cheat-bump\n  💤 in-progress prediction 已陈旧 35 天\n     - predictions/2026-04-01_xxx.md → 是已发了忘登记？还是弃稿？\n\n🔥 候选池\n  - candidates.md: 27 条（tier1: 12, tier2: 9, tier3: 6）\n  - 距上次抓热点: 4 天 — 可以再跑 /cheat-trends\n\n📈 健康度\n  - rubric_notes.md: 412 行（健康，<600 警戒线）\n  - hooks_installed: ✅\n  - external audit configured: ❌ → 建议配 mcp__llm-chat__chat\n\n下一步建议（按推荐优先级）：\n1. /cheat-retro predictions/2026-05-01_db063817_你已不在关系里.md  ← 最紧急\n2. /cheat-bump  ← 同向偏差 3 次的处理\n3. 处理陈旧 in-progress（手动或回 \"清理 in-progress\"）\n\n完整的命令清单见主 SKILL.md。\n```\n\n输出风格：**直白、具体、可点击**。每个建议附确切的命令——用户应能 copy-paste 直接执行。\n\n## Key Rules\n\n1. **无副作用**。读多写零。任何状态修改是其他 skill 的事\n2. **不假装数据可用**。state file 字段缺失 → 显式标\"未知\"，不猜\n3. **建议带优先级**。10 个建议同时显示用户会麻木——按紧急度排\n4. **每个建议附命令**。不能只说\"该 bump 了\"——要给 `/cheat-bump --propose \"...\"` 的精确入口\n\n## Refusals\n\n- 「顺便帮我自动跑一下 retro」 → 拒绝。status 是只读，retro 是另一个动作（避免一次操作做两件事）\n- 「我不想看 rubric_notes 行数，太琐碎」 → 输出仍包含但折叠到底部\"健康度\"区——状态信息的存在让用户在出问题前可见\n\n## Integration\n\n- 上游：所有其他 skill 完成时更新 .cheat-state.json，status 是这些更新的可视化\n- 下游：每个建议都路由到具体子 skill\n- meta-logging hook（如启用） → 写 usage.jsonl，status 用它算\"距上次 X 多少次\"\n\n<!-- File: skills/cheat-trends/SKILL.md (Tokens: ~2895 | Category: anthropic-skill) -->\n---\nname: cheat-trends\ndescription: 从配置的热点源（HN / Reddit / YouTube trending / B 站热门 / 等）抓今天的热门话题，去重 + 粗打分 + 写入 candidates.md。**绝大部分人没有候选池——这是让\"我没素材\"问题在 onboarding 第二步就消失的钥匙**。触发词：\"抓热点\"/\"fetch trends\"/\"今天有什么可做的\"/\"trending now\"/\"找选题\"。\nargument-hint: \"[— sources: <comma-separated>] [— max-per: 20]\"\nallowed-tools: Bash(*), Read, Write, Edit, Glob, WebFetch, Skill\n---\n\n# /cheat-trends — 热点抓取\n\n多 adapter 模式：读各 `trend-sources` adapter 的输出 → 去重 → 粗打分 → 写入 `candidates.md`。\n\n## Overview\n\n```\n[用户：抓热点]\n  ↓\n[Phase 0: 读 .cheat-state.json 拿 enabled adapters]\n  ↓\n[Phase 1: 对每个 adapter 调 fetch]\n  ↓\n[Phase 2: normalize 到 candidate-schema]\n  ↓\n[Phase 3: 去重（vs candidates / predictions / trends-history）]\n  ↓\n[Phase 4: 对每个新 item 粗打分（调 cheat-score 内联逻辑）]\n  ↓\n[Phase 5: 排序 + 询问用户哪些加入 candidates.md]\n  ↓\n[Phase 6: 写入 + 更新 trends-history.jsonl 缓存]\n```\n\n## Constants\n\n- **TREND_SOURCES = [\"manual-paste\"]** — 启用的 adapter 列表（默认仅 manual-paste，最稳）\n- **LOOKBACK_HOURS = 24** — 抓最近 N 小时的热点\n- **MAX_PER_SOURCE = 20** — 每个 adapter 最多 N 条\n- **DEDUPE = true** — 去重开关\n- **AUTO_SCORE = true** — 抓回来后自动调 cheat-score 粗打分\n- **MIN_COMPOSITE_TO_SUGGEST = 6.0** — 低于此分的不推荐用户加入候选池（仍写入 trends-history 避免下次重复推）\n\n> 💡 调用时覆盖：`/cheat-trends — sources: manual-paste,aihot,weibo-hot — max-per: 10`\n\n## Inputs\n\n| 必填 | 来源 |\n|---|---|\n| `.cheat-state.json` | 默认 sources |\n| `adapters/trend-sources/<name>.md` | 各 adapter 的实现描述 |\n| `candidates.md` | 去重对照 |\n| `predictions/*.md` | 去重对照（已发的不再推） |\n| `.cheat-cache/trends-history.jsonl` | 历史抓取去重缓存 |\n\n## Workflow\n\n### Phase 0: 读启用的 adapters\n\n```python\n# 伪代码\nstate = read('.cheat-state.json')\nenabled_adapters = args.sources or state.get('enabled_trend_sources', ['manual-paste'])\n```\n\n如 enabled_adapters 为空 → 输出引导：\n\n```\n你目前没有启用任何热点源。\n\n最快配法：\n- 临时跑：/cheat-trends — sources: manual-paste,aihot\n- 永久启用：编辑 .cheat-state.json 的 enabled_trend_sources 数组\n\n可用 adapter（详见 adapters/trend-sources/）：\n- manual-paste（默认，永远能用）\n- aihot（AI 热点聚合，无需 key）\n- weibo-hot（微博热搜，无需 key）\n- zhihu-hot（知乎热榜，无需 key）\n- trendradar-mcp（TrendRadar MCP 服务，需配置）\n```\n\n### Phase 1-2: 对每个 adapter 调 fetch + normalize\n\n对每个 adapter，读其 `adapters/trend-sources/<name>.md` 中描述的 fetch 接口（实际是 Bash 调底层 Python / shell / WebFetch）：\n\n| Adapter | 实现机制 |\n|---|---|\n| `manual-paste` | 询问用户：\"粘贴你今天的候选 URL/标题列表（每行一条）\" → 解析每行，对 URL 做 WebFetch 拓展 snippet |\n| `aihot` | 读 adapters/trend-sources/aihot.md 描述的 fetch 接口 |\n| `weibo-hot` | 读 adapters/trend-sources/weibo-hot.md 描述的 fetch 接口 |\n| `zhihu-hot` | 读 adapters/trend-sources/zhihu-hot.md 描述的 fetch 接口 |\n| `trendradar-mcp` | 读 adapters/trend-sources/trendradar-mcp.md 描述的 fetch 接口 |\n\n每个 adapter 输出符合 [candidate-schema.md](../../shared-references/candidate-schema.md) 的 items。\n\n**优雅降级**：单 adapter 失败（API key 缺失 / 端点 503 / cookie 失效）→ skip 该 adapter，**不抛异常**，在汇总里说明：\n```\n✅ aihot: 拉到 18 条\n✅ weibo-hot: 拉到 15 条\n✅ zhihu-hot: 拉到 12 条\n⚠️  trendradar-mcp: 跳过（MCP 服务未配置——配置见 adapters/trend-sources/trendradar-mcp.md）\n```\n\n### Phase 3: 去重\n\n按 [candidate-schema.md](../../shared-references/candidate-schema.md) 的\"去重协议\"：\n\n1. 对每个 item 算 id（`sha256(source_type + normalized_title + url_path)[:12]`）\n2. 检查 `candidates.md` 已含此 id → 跳过\n3. 检查 `predictions/*.md` 已含此 id → 跳过\n4. 检查 `.cheat-cache/trends-history.jsonl` 已含此 id 且 `rejected_at != null` → 跳过\n\n去重统计写到汇总报告里。\n\n### Phase 4: 粗打分\n\n`AUTO_SCORE=true` 时，对每条新 item：\n1. 用 item 的 `snapshot_text` 作为输入\n2. 按当前 rubric 给 7 维打分（**不**调 `/cheat-score` 子 skill 走 IO；inline 复用打分逻辑）\n3. 算 composite\n4. 给一句 rationale\n\n**注意**：粗打分 ≠ 正式预测。预测必须基于最终稿（用户改过的），这里的打分只是\"是否值得展开写\"的粗筛。\n\n`AUTO_SCORE=false` 时，items 写入 candidates.md 时 composite=null，需要后续手动 `/cheat-score`。\n\n### Phase 5: 排序 + 询问\n\n按 composite 降序，过滤掉 composite < `MIN_COMPOSITE_TO_SUGGEST` 的：\n\n```\n🔥 抓热点完成。各源拉取统计：\n- manual-paste: 5 条（用户输入）\n- aihot: 18 条\n- weibo-hot: 15 条\n跳过 trendradar-mcp（MCP 服务未配置）\n\n去重后剩 27 条新 item。\n粗打分后 12 条 composite ≥ 6.0：\n\n| # | 标题 | source | composite | bucket | rationale |\n|---|---|---|---|---|---|\n| 1 | 为什么我们都讨厌主动联系朋友 | aihot | 8.4 | 30-100w | ER+QL 双 5，AB 普适 |\n| 2 | \"她不一样\"的一千种变体 | weibo-hot | 8.1 | 30-100w | MS 候选维度高 |\n| 3 | ...... |\n\n哪些加入 candidates.md？\n- 全部加 → 回 \"all\"\n- 选几个 → 回 \"1, 3, 5\"\n- 都不要 → 回 \"none\"（这些会被记到 trends-history 避免下次重复推）\n```\n\n### Phase 6: 落盘\n\n用户响应后：\n1. 选中的 items → 按 [candidate-schema.md](../../shared-references/candidate-schema.md) 的\"Markdown 表示\"格式追加到 `candidates.md`\n2. 所有抓回来的 items（不管选中与否）→ append 到 `.cheat-cache/trends-history.jsonl`：\n   ```jsonl\n   {\"id\": \"...\", \"title\": \"...\", \"source\": \"...\", \"snapshot_at\": \"...\", \"rejected_at\": null|\"<ISO>\", \"fetched_at\": \"<ISO>\"}\n   ```\n\n### Phase 7: 状态更新\n\n```json\n{\n  \"last_trends_run_at\": \"<ISO>\",\n  \"last_trends_added_count\": 5\n}\n```\n\n## Key Rules\n\n1. **不抛异常**。单 adapter 失败 → skip + 报告。多 adapter 全失败 → 报错\"所有源都失败\"，附排查指引\n2. **manual-paste 永远在**。即使其他所有 adapter 都坏了，manual-paste 模式必须能跑——它是兜底\n3. **去重是硬约束**。同 id 不重复推；用户拒绝过的 6 个月内不再推\n4. **粗打分要诚实标注**。在 candidates.md 的 entry 里标 `composite (rough, snapshot-based)`，避免与 prediction 的精打分混淆\n5. **不直接进 predictions/**。trends 只产 candidates，predict 是另一个动作\n\n## Refusals\n\n- 「直接抓抖音热门 feed，不用 cookie」 → 拒绝。抖音反爬极严，无 cookie 必失败；引导到 douyin-session adapter 配置文档\n- 「跳过去重，把所有抓到的都写进去」 → 拒绝。会污染候选池，下次 recommend 时排序失效\n- 「跳过粗打分，直接写 raw 标题」 → 允许（`AUTO_SCORE=false`），但提示用户后续需要 `/cheat-score` 才能进 recommend 池\n\n## Integration\n\n- 上游：用户配置 `.cheat-state.json` 的 `enabled_trend_sources` 数组\n- 下游：`/cheat-recommend` 直接读 `candidates.md` 排序——trends 写完，recommend 立刻看到\n- 与 `/cheat-init`：onboarding Q4 选\"没有候选池\"的用户被引导到这里\n- 与 `/cheat-status`：status 看板显示\"上次抓热点：X 天前 / 待清理候选池：Y 条\"\n\n## Adapter 实现注意事项\n\n每个 `adapters/trend-sources/<name>.md` 必须文档化以下：\n1. **依赖**：API key / cookie / package\n2. **fetch 接口**：调用方式（python script path / shell command / API endpoint）\n3. **输出 schema**：必须符合 candidate-schema.md\n4. **失败模式**：常见错误 + 优雅降级行为\n5. **稳定性等级**：★ 1-5 颗星\n\n详见 [adapters/HOWTO.md](../../adapters/HOWTO.md)（待批次 3 实现）。\n</yakaai_skills>"}