Advance a video from the "prediction written, not shot" state to the "shot, not published" state. This step:
- Create
videos/<同 id>/directory (if not already present) - Ask the user: "Is the script used during actual shooting consistent with
scripts/<id>.md?" - Compute diff — exceeds V2_TRIGGER_THRESHOLD (default 30%) → delegate to
/cheat-predict — mode: v2append to the original prediction file## 预测 v2section - Add the video folder to the state.shoots queue, buffer +1
cheat-shoot itselfdoes notwrite prediction content — all prediction persistence logic is in cheat-predict. cheat-shoot is only responsible for detecting script changes + dispatching.
Why a separate skill:
- The buffer warning system needs to clearly distinguish "shot" vs "published". Videos can be shot in batches (5 in a day), published spread out (1 per day)
- "Actual shooting script" ≠ "pre-shoot draft" is the norm. This step is the entry point for making the diff explicit, triggering v2 re-judgment, and collecting "user script-change pattern" signals
- The difference between v2 prediction and v1 prediction is itself evidence of rubric upgrading — e.g., v1 gives ER=4, v2 gives ER=5 (the user's script change increased hook strength), which tells the rubric "this user's ER threshold is inconsistent with my current formula"
Overview
[用户:拍了 scripts/2026-05-04_abc123_停止期待.md]
↓
[Phase 0: 解析路径 + 验证 prediction 已存在]
↓
[Phase 1: 检查是否已登记(避免重复)]
↓
[Phase 2: 建 videos/<id>/ + 询问"实际拍摄稿一致吗?"]
↓
[Phase 3: 写 videos/<id>/script.md]
↓
[Phase 4: append state.shoots]
↓
[Phase 5: 输出 buffer 状态]
Constants
- REQUIRE_PREDICTION = true — a v1 prediction file must exist before shooting
- V2_TRIGGER_THRESHOLD = 0.30 — after normalization, char-level diff exceeds 30% → by default recommend v2 re-judgment; below 30%, ask the user whether v2 is still wanted
- DIFF_METRIC = char_levenshtein_normalized(default)—— via
tools/diff_pct.pycall: first normalize (remove markdown headers / separators / list markers / decorative punctuation / collapse all whitespace), then compute char-level Levenshtein / max(len_a, len_b). preferred backendrapidfuzz,fallbackdifflib.SequenceMatcher(stdlib, always available).The old line-level version produced severe false positives in conversational transcription scenarios(draft has long markdown sentences vs whisper transcription's short broken sentences, content almost unchanged but line-level computes ~200% diff) — fixed in PR #14 - DIFF_METRIC=lines —— legacy fallback: when python3 is completely unavailable or tools/diff_pct.py cannot be found, degrade to
diff -u | grep '^[+-]' | wc -lalgorithm
Inputs
| required | source |
|---|---|
<scripts-path-or-id> |
user parameter; if missing, ask |
.cheat-state.json |
state file |
scripts/*.md |
pre-shoot draft |
predictions/*.md |
verify the corresponding prediction exists |
Workflow
Phase 0: Parse + validate
- Parse the path given by the user — several forms are supported:
- full path
scripts/2026-05-04_abc123_停止期待.md - abbreviated
2026-05-04_abc123_停止期待 - id abbreviation
abc123→ globscripts/*_abc123_*.mdfind a match
- full path
- Verify
scripts/<id>.mdexists: if not → error "cannot find pre-shoot draft" - Verify a corresponding prediction exists
predictions/<同名>.md:- does not exist → refuse registration, prompt "run /cheat-predict first to write a prediction, otherwise it violates the blind prediction principle — you cannot write the prediction only after shooting, that amounts to writing it after seeing the footage"
- Exists → Pass
Phase 1: Check for duplicates
Read .cheat-state.json, check whether shoots[] already contains this id:
- Already exists → Warn "Already registered (X days ago). Do you want to re-register, or publish with /cheat-publish?"
- Does not exist → Proceed to Phase 2
Phase 2: Create video folder + ask about script consistency
- Create directory
videos/<id>_<short>/(same naming as scripts/ + predictions/) - Ask the user:
拍 「<title>」 的时候,你实际用的稿子和 scripts/<id>.md 一致吗?
a) 一致——按草稿拍的
b) 改了一些——你能给我看下实际拍摄稿吗?我重新打分一次(v2 预测)
c) 大改了,基本是另一条 → 走 _redo 流程:
scripts/<id>_redo.md → 重新 cheat-predict → 再 cheat-shoot(原 prediction 留档脱钩)
Phase 3: Write videos//script.md + (b path) trigger v2 prediction
a path (consistent):
cp scripts/<id>.md → videos/<id>/script.mdscript_consistency = consistent- Do not re-judge, proceed to Phase 4
b path (changed):
-
Ask the user for the actual shooting script—pasted text / file path / transcript file
-
If the user provides it → write to
videos/<id>/script.md -
If the user did not keep it (improvised) → mark
script_lost, write a placeholder file + warn "v2 re-judgment skipped—next time it is recommended to keep a script (even a voice memo transcript)", proceed to Phase 4 -
If provided: compute diff
# 解析 cheat-on-content 源码根(cheat-shoot 是 symlink 装的) SKILL_REAL="$(readlink -f ~/.claude/skills/cheat-shoot 2>/dev/null || readlink ~/.claude/skills/cheat-shoot 2>/dev/null)" if [[ -n "$SKILL_REAL" ]]; then REPO_ROOT="$(cd "$SKILL_REAL/../.." && pwd)" DIFF_TOOL="$REPO_ROOT/tools/diff_pct.py" fi if [[ -n "${DIFF_TOOL:-}" && -f "$DIFF_TOOL" ]] && command -v python3 >/dev/null 2>&1; then # 默认 char-level Levenshtein on normalized text(rapidfuzz preferred, difflib fallback) diff_pct=$(python3 "$DIFF_TOOL" "scripts/<id>.md" "videos/<id>/script.md") else # legacy line-level fallback——只在 python3 或 diff_pct.py 都不可用时用 added=$(diff -u scripts/<id>.md videos/<id>/script.md | grep -c '^+') removed=$(diff -u scripts/<id>.md videos/<id>/script.md | grep -c '^-') total_orig=$(wc -l < scripts/<id>.md) diff_pct=$(( (added + removed) * 100 / total_orig )) echo "⚠️ fallback 到 line-level diff——口语化转录会 inflate diff_pct,可能误触发 v2" fiWhy normalize + char-level: line-level diff in creators' real scenarios (the draft is long markdown sentences, the shooting script is short colloquial lines from a whisper transcript) computes ~200% difference but the content is almost unchanged. After normalize, char-level Levenshtein stably reflectscontentdifferences, rather than formatting differences. See
tools/diff_pct.py+tools/diff_pct_test.shfor details (3 fixtures all pass on both backends). -
Determine v2 trigger:
diff_pct >= 30→ by default recommend v2 re-judgment,actively call/cheat-predict — mode: v2 — prediction-file: predictions/<id>.mdpassvideos/<id>/script.mdas input. cheat-predict runs in v2 mode and appends## 预测 v2diff_pct < 30→ ask the user: "Only N% of the content was changed, do you want to re-judge? Default is no (the v1 prediction is still valid)". If the user says yes → call as above; if the user says no → skip v2, continue to Phase 4
-
After cheat-predict completes the v2 write to disk, control returns to cheat-shoot to proceed to Phase 4
c path (major change):
- Do not write
videos/<id>/script.md, prompt to follow the_redoflow - Exit cheat-shoot (do not proceed to Phase 4)
Phase 4: state update
{
"shoots": [
...,
{
"video_folder": "videos/2026-05-04_abc123_停止期待/",
"prediction_file": "predictions/2026-05-04_abc123_停止期待.md",
"scripts_path": "scripts/2026-05-04_abc123_停止期待.md",
"shot_at": "<ISO timestamp>",
"script_consistency": "consistent" | "modified" | "lost",
"script_diff_pct": <0-100 int 或 null>,
"v2_prediction_written": <true/false>,
"script_hash_at_shoot": "<sha256:12 of videos/<id>/script.md>"
}
]
}
v2_prediction_written: true indicates that the prediction file now has the ## 预测 v2 section; cheat-retro should read v2 to compute deviation;false indicates to continue using v1.
Phase 5: Output buffer status
Immediately after reading state, compute buffer + color (per cadence-protocol.md 's derivation rules):
✅ 已登记拍摄:videos/2026-05-04_abc123_停止期待/
预测文件:predictions/2026-05-04_abc123_停止期待.md
📦 当前 buffer:3 篇(🟢 绿色,正常)
按你的 cadence(隔日更)= 6 天 buffer,节奏稳定。
下一步:拍其他候选 / 等下个发布日 / 不动
If the buffer color changed (e.g., from green to blue) → highlight reminder:
📦 当前 buffer:6 篇(🔵 蓝色,**积压**)
⚠️ 建议暂停拍摄,全力发布存货 + 复盘。
按你的 cadence(日更)= 6 天预备,已超过健康上限。
Key Rules
- Do not write prediction—shooting ≠ publishing. The prediction is locked at /cheat-predict, shooting is just an event
- Do not touch video folder contents—neither script.md nor draft-v0.md is changed
- There must be a prediction first—otherwise it violates blind prediction (looking at the footage after shooting and then writing a prediction = data leakage into judgment)
- Compute buffer in real time—recompute immediately after each shoot / publish; state.shoots is the ground truth
- Batch supported: the user can say "shot X / shot Y / shot Z" three times in a row in one day and register them consecutively
Refusals
- "Shot X, but I never ran cheat-predict" → Reject. The v1 predictionmust be written before shooting——Writing a prediction after shooting will be influenced by the footage and lead to after-the-fact edits. Please run /cheat-predict to write v1 first, then come back to /cheat-shoot. (Re-judging as v2 is a separate matter—only allowed if v1 already exists + the draft is revised after shooting)
- "I don't have a video folder, I shoot directly" → ask the user → help them create a video folder + remind them to follow the full process next time; when registering, mark
ad_hoc: true - "I revised the draft but just overwrite v1, don't keep a v2 section" → refuse. v1 is the archive, v2 is the current judgment—append, don't overwrite. Keeping both sections together is key evidence for rubric learning
Integration
- Upstream:
/cheat-predictfinish writing prediction → user shoots →/cheat-shootregister - Downstream:
/cheat-publishwhen publishing, remove the corresponding item from state.shoots /cheat-statusthe dashboard's buffer number comes directly fromstate.shoots.length/cheat-recommendadjust the recommendation strategy based on the buffer color- The SessionStart hook looks at the buffer color to decide the first line of the report
state.shoots data structure
{
"shoots": [
{
"video_folder": "videos/2026-05-04_abc123_停止期待/",
"prediction_file": "predictions/2026-05-04_abc123_停止期待.md",
"shot_at": "2026-05-04T18:30:00+08:00",
"ad_hoc": false // true if user shot without going through full flow
}
]
}
In ascending order by shot_at —the earliest shot comes first./cheat-status Show a days-since-shoot warning for the earliest item (to avoid having a video that was shot 30 days ago and still not published).