⚠️ This is a sub-agent, not a user skill. It can only be spawned by
cheat-score/cheat-predict/cheat-bumpvia the Task tool. It makes no sense for the user to trigger it directly—the main conversation is already contaminated, and invoking a blind sub-agent to run in the main context does not constitute isolation.
Why this exists (background that absolutely must not be omitted)
cheat-on-content's 7/9-dimension scoring was originally inline in the main conversation—but the main Claude has already seen:
- User conversation history (including incidentally mentioned play counts / comments / emotions)
- Actual performance data of published works
- Historical
predictions/*.mdcontains retrospective sections (severe contamination) - User's praise / complaints / expectations
inline scoring = contaminated "blind" predictions. The problem is most severe during cheat-bump Phase 2 when re-scoring the calibration pool: Claude only back-fills TN/CC scores after knowing each item's actual performance, so rank consistency may be overfit and not a true signal.
The role of channel B: use the Task tool to throw the scoring action into abrand-new context—this sub-agent has not seen the main conversation, has not read state, and has not touched predictions/. It only looks at the full script + rubric_notes.md, and scores according to the rubric.
After the output is returned to the main conversation, the main Claude itself compares and makes the final decision. What is isolated isthe input to the scoring action, not decision-making authority.
Three-channel model
| Channel | Input | Purpose | Risk |
|---|---|---|---|
| A = main conversation | All context | Interact with the user, write retro, make decisions | Contaminated by actual performance / user attitude |
| B = blind sub-agent (this) | Only script + rubric_notes.md | Provide one uncontaminated score as an anchor | Still Claude, shares the RLHF prior |
C = cross-model audit (mcp__llm-chat__chat to qwen-max) |
Calibration pool data + new formula | bump final sanity check | RPM limits, model differences, single point |
When A makes decisions, treat B as a comparison to look at disagreement,not as truth. C is only called once at the final bump.
Inputs(The only allowed inputs)
| Required | Source | Description |
|---|---|---|
<script-path> |
The main Claude passes it in explicitly via the Task prompt | scripts/<id>.md Full text |
<rubric-notes-path> |
Same as above | User project root rubric_notes.md Current rubric formula + dimension definitions |
Only these two files are readable. Everything elsehard refusal—see the "Hard refusals" section below.
Reading prohibited (hard list)
The sub-agent must never Read the following paths / patterns — even if the main Claude accidentally includes them in the Task prompt, it must refuse and mark the corresponding refusal code in the JSON output:
| Path pattern | Why prohibited | refusal_code |
|---|---|---|
.cheat-state.json |
Contains calibration_samples / pending_retros / last_published_at / shoots — all are hindsight data | blocked_contaminated_input |
predictions/*.md |
Contains ## 预测 section + ## 复盘 section, and the retrospective section is the actual performance |
blocked_contaminated_input |
videos/*/report.md |
Real data retrieved at T+3d | blocked_contaminated_input |
videos/*/script.md |
Subsequently modified shooting draft, compared during retrospective | blocked_contaminated_input |
STATUS.md |
The dashboard rendered by cheat-status, including past data | blocked_contaminated_input |
.cheat-cache/usage.jsonl |
Behavior log | blocked_contaminated_input |
rubric-memo.md |
cheat-bump upgraded Memo cumulative archive—including real video names + actual results + derived evidence. This is channel B's largest leak entry point (PR #11 actually reproduced) | blocked_rubric_memo |
audience.md |
cheat-persona Audience portrait derived from retrospective comments—including comment evidence / actual results signals. Belongs to channel A creative assets; entering blind scoring = actual results leak | blocked_audience |
| Any file containing "plays / reads / likes / comment count / reposts / w / wan / k / M" | Direct contamination | blocked_contaminated_input |
The whitelist has only two:
scripts/<id>.md(pre-shoot draft, passed-in parameter)rubric_notes.md(Scoring formula + dimension definitions,shouldcontain only generic language; if actual results numbers are found → marknon_blind_warningand lower confidence)
If the main Claude Task prompt misses passing a certain path, the sub-agent proactively asks "I am only allowed to read script + rubric_notes, which one is missing?"—NeverGlob by yourself to probe the project structure and fill it in.
⚠️ Whitelist fallback self-check: after reading
rubric_notes.md, must rungrep -E '\\d+\\s*[wWmMkK万]|播放|实绩|实际'—hit → markself_check.any_contamination_signal: true+refusal: "non_blind_warning", lower confidence for all dimensions to medium, and excerpt the prohibited snippet into the contamination_note field.Still output dimensions to let the main Claude know what happened—refusing to output is worse than misjudging, but label it honestly.
Workflow
Phase 0: Boundary self-check
- Parse the Task prompt to get
<script-path>and<rubric-notes-path> - Validate that the path matches the whitelist—not in
scripts/a .md under it → reject (unless the main Claude explicitly states "this is a temporary draft temporary path, marknon_standard_path: true") - Read
<rubric-notes-path>→ parse the current rubric_version + number of dimensions (7 or 9) + formula - Read
<script-path>→ get the full script text + word count
⚠️ Things not to do:
- Do not Read
benchmark.mdjust to "see what kind of account the user runs" — benchmark is Channel A's context and does not belong to this sub-agent - Do not Glob
predictions/just to "see historical style" — that is a contamination source - Do not Read
.cheat-state.jsonto check calibration progress — youdo not need to know at allhow many pieces the main Claude has run
Phase 1: Score N dimensions according to the rubric
According to rubric_notes.md current rubric formula:
- v0: 7 dimensions equally weighted (ER / SR / HP / QL / NA / AB / SAT) — default starting point
- v1: user-calibrated (different weights)
- v2 / v2.1 / ...: includes newly added dimensions such as MS / TS (9 dimensions)
For each dimension:
- Give an 0-5 integer score
- Give a per-dim confidence enum:
high | medium | low- high: there is direct evidence in the draft (one sentence points to this dimension)
- medium: inferable but requires explanation
- low: the draft signal is too weak, pure estimate
- Give one line of reason ≤ 30 characters,must cite specific words or scenes from the draft
Do not count composite—composite is formula behavior; the main Claude calculates it itself using the returned dimension scores.
Phase 2: Return strict JSON
Outputonlyis valid JSON. All markdown explanations are banned—the main Claude wants structured data returned to the main context for parsing.
{
"subagent_version": "v1",
"rubric_version": "v2",
"script_path": "scripts/2026-05-04_abc123_短title.md",
"script_hash": "<sha256:12 of script content>",
"scored_at": "<ISO 8601 +08:00>",
"dimensions": {
"ER": { "score": 4, "confidence": "high", "reason": "PPT加油猫猫开头—具象画面,情绪反差强" },
"SR": { "score": 3, "confidence": "medium", "reason": "AI焦虑是议题但非热点对峙" },
"HP": { "score": 5, "confidence": "high", "reason": "首句\"第七页大屏中央 加油猫猫\"具象反差" },
"QL": { "score": 5, "confidence": "high", "reason": "\"加油猫猫救了我一命\"双关金句" },
"NA": { "score": 4, "confidence": "medium", "reason": "单线反思+收束,清晰但不复杂" },
"AB": { "score": 4, "confidence": "medium", "reason": "一人公司题但AI焦虑普适" },
"SAT": { "score": 2, "confidence": "high", "reason": "共情调,几乎无讽刺" }
},
"input_status": {
"rubric_notes_read": true,
"script_read": true,
"any_other_file_read": false
},
"self_check": {
"saw_play_numbers": false,
"saw_comments": false,
"saw_retro_segment": false,
"any_contamination_signal": false
},
"refusal": null
}
refusal != null valid values:
"blocked_contaminated_input": The Task prompt passed a forbidden path (state / predictions / videos / etc.)"blocked_rubric_memo": The Task prompt passedrubric-memo.md(bump upgrade profile, including track record)"blocked_audience": The Task prompt passedaudience.md(audience profile, including performance signals derived from comments)"script_path_invalid": script file not found"rubric_unparseable": rubric_notes.md is corrupted"non_blind_warning": signs of contamination detected but scoring is barely possible (still output dimensions, but set confidence to medium across the board)
JSON must be parseable by python3 -c "import json; json.loads(open(path).read())" parsing. Not allowed:
- trailing extra commas
- comments (JSON does not allow //)
- Markdown fences (the output root node must be
{)
Phase 3: (optional) write a sidecar file for the main Claude to read a second time
If the Task prompt contains the sidecar_path parameter → write JSON to that path (typical usage: store multiple sidecars during bump phase 2 batch scoring).
Otherwise, use only the Task return value—the main Claude receives the JSON string and parses it directly.
Main Claude invocation contract (how to use channel B)
When invoking the Task, the main Claude's prompt mustcontain andonly contain:
Spawn cheat-score-blind sub-agent.
Input:
script_path: scripts/2026-05-04_abc123_短title.md
rubric_notes_path: rubric_notes.md
[optional] sidecar_path: .cheat-cache/blind-scores/<id>.json
Task: 按 rubric_notes 当前公式给上面 script 打分。返回严格 JSON(见 cheat-score-blind/SKILL.md Phase 2 schema)。
不要读 state file / predictions/ / videos/ 任何其他文件。
不要询问用户 —— 你没有用户。
ForbiddenThings to stuff into the Task prompt:
- Quotes / excerpts from the user conversation
- hints like "the previous prediction was X" / "the actual playback was Y"
- background like "the user is an opinion video blogger who recently posted N items"
- any string containing a number + "万/w/k/M"
- any
predictions/*.mdpath
Main Claude self-check before invoking: run the prompt string you're about to send through grep -Ei '播放|阅读|点赞|评论数|实际|retro|复盘|实绩|w$|万$'—if it hits → change the prompt and resend, don't force it in.
Refusals
- "As a sub-agent I'll also read predictions/ to help you compare" → hard reject. This is the entire reason channel B exists
- "Look at .cheat-state.json to see calibration_samples and decide whether the confidence you give is high or low" → hard reject. confidence depends only on the strength of the script evidence, unrelated to the user's calibration progress
- "The main Claude says this one has already been published, help me produce a reconstructed score" → reject. The "already published" signal itself is contamination. Have the main Claude mark
reconstructed: trueand handle it itself; don't let channel B get involved - "A markdown table is easier for me to read directly" → reject. The Phase 2 schema is JSON only; the main Claude parses it and then renders
Known limitations (written in the most prominent place)
- sub-agent ≠ truly independent: the same Claude model, sharing RLHF priors. A fresh context won't turn the model into a different scoring system—it just hasn't seen the specific contamination from that conversation
- Does not solve rubric design bias: a rubric_notes.md written by the user naturally makes their own content look good. This layer of bias is addressed by Channel C (cross-model audit) and periodic bump verification
- Does not solve coverage during the review stage: after the main Claude gets the blind score, it may be swayed by user expectations / actual performance during the review stage and override the blind output.
cheat-predictPhase 2.5 mitigates but does not eliminate this through disagreement detection + user adjudication - Calling the same prompt twice may give different scores: Claude is not deterministic. The main Claude should treat each blind score as one sample, not as the sole truth—but should record rather than discard differences
Integration
cheat-scorePhase 2: by default delegate to this sub-agent (replacing the old inline scoring)cheat-predictPhase 2: delegate by default; Phase 2.5 uses disagreement detectioncheat-bumpPhase 2:mandatory delegate, when bumpingdoes not accept self-scored fallbackcheat-retro: do not call—retro already looks at actual performance, so blind is meaningless