
hard-cheese
Checks whether an author can explain a code change before review. Use when the user requests /hard-cheese, /cheese --hard, or an understanding check. Use it before a pull request or through the --hard pipeline flag. Do not use it for reviews, test hardening, or fixes.
- Installs
- 644
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/paulnsorensen/easy-cheese --skill hard-cheese- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads hard-cheese automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
/hard-cheese
The gate reduces epistemic debt. This debt exists when code passes checks, but the author cannot explain it.
Inputs
/hard-cheese [<slug>] [--socratic-cap N=3] [--passing-score N=3] [--no-judge]
Arguments:
<slug>identifies the artifact at.cheese/hard-cheese/<slug>.md. This argument is optional. Without it, use the short SHA ofHEAD. An explicit slug overrides the SHA.--socratic-cap Nsets the maximum number of retries. The gate then marks the artifactFAILEDand returns a non-zero status. The default is3. Vibecheck has no limit, but easy-cheese prevents infinite loops.--passing-score Nsets the minimum SOLO score for PASS. Use a value from1through5. The default is3. The gate treats a previous PASS below this value as stale.--no-judgeenables log-only mode. Record the user's explanation withstatus: LOGGED. Do not start the judge sub-agent. This mode is the easy-cheese equivalent of the optional JSONL telemetry mode in vibecheck. It retains more content. See## Divergence from the paper.
Invocation modes
| Mode | How the gate runs | Where the gate sits |
|---|---|---|
| standalone | The user runs /hard-cheese <slug> before a pull request. | Outside the pipeline. No upstream skill is required. |
| propagated | /plate --hard runs /hard-cheese <slug> after the final writes and before publication. | At the verified-artifacts to share-for-review boundary. |
--hard passes through /cheese → /mold → /cook (⇄ /press) → /age → /cure → /plate. Only /plate runs /hard-cheese.
See ../cheese/references/harness-portability.md for portability requirements. It covers helper resolution, sub-agent dispatch, GitHub operations, and handoff transitions.
Use the bundle or repository helper first. Use ${CLAUDE_SKILL_DIR} only as an optional host fallback.
The handoff blocks define the portable contract because slash commands are host renderings, not the control model.
Flow
-
Resolve scope.
- Set
diff_base = origin/mainanddiff_head = <short-sha of HEAD>. - Load
.cheese/specs/<slug>.mdas the optional intent reference when it exists. The diff remains the source of truth. - Use the short SHA of
HEADwhen no slug exists. - If the diff against
origin/mainis empty, return0with"nothing to gate on". Do not write an artifact.
- Set
-
Freshness check. Run the freshness check before you run the gate:
python3 skills/hard-cheese/scripts/hard-cheese.pyz freshness-check \ --slug <slug> --passing-score <n>The command prints one JSON object and exits
0for every state. Read itsstatefield. Forpreviously_passed, print"previously passed"and stop. Continue to step 3 forstaleornew. A nonzero exit status is an input error; its stderr line is JSON witherrorandexit_code. A result is stale whenHEADchanges or the last PASS score is too low. -
Rank the hunks, then compose the vibecheck prompt. Keep it faithful to Sankaranarayanan 2026. Use "share for review" to keep the gate implementation independent.
Run
rank-hunksbefore you compose the prompt:python3 skills/hard-cheese/scripts/hard-cheese.pyz rank-hunks \ --base origin/main --head HEADShow the ranked regions next to the diff summary, one line per hunk, as
path:start-end — reasons. Put the changed lines from every ranked region in the 80-line diff summary before other context. Make no judge call before the first attempt.Before this is shared for review, explain in your own words what this change is for. How does <feature or fix> work? Why does it produce the desired behavior? What state, control flow, or invariants does it rely on? Start with the highlighted regions.
Show a diff summary with the prompt. For
/plate, also show the complete final evidence:- the final artifact inventory,
- each
{target, backend, verified}completion row, - the tracked artifact diff,
- the quality gate result.
Stop with a non-zero status when
/plateomits one of these four values. Stop with a non-zero status when a completion row hasverified: false. -
Record the user's explanation as free text. Do not provide coaching or example answers. The explanation is the artifact under test.
-
Start the judge sub-agent in a fresh context. Use the same pattern as the
/cookfan pathway.- Use
references/judge-prompt.mdas the system prompt. - Provide the passing score, diff summary, optional spec excerpt, the
targetsJSON fromrank-hunks, and user's explanation. - Require this JSON object:
{score, level, pass, feedback, socratic_qs, targets_addressed}.
See
references/judge-prompt.mdfor the full system prompt and output shape.Skip this step when the user sets
--no-judge. Mark the attemptstatus: LOGGED, write the artifact, and return0. - Use
-
Process the judge result.
- Mark the attempt PASS when
score >= <passing-score>. - Mark the attempt FAIL when
score < <passing-score>. Show the Socratic questions; at least one question anchors to a ranked hunk fromtargets_addressed. Whentargetsis empty or absent, skip the anchoring requirement and expect an emptytargets_addressed. Return to step 4 while retries remain. - Mark the attempt ERROR when the judge fails. Print a warning and return
0. See## Divergence from the paper.
Append the attempt row:
python3 skills/hard-cheese/scripts/hard-cheese.pyz append-attempt \ --slug <slug> --status <PASS|FAIL|ERROR> --score <n> \ --feedback "<judge feedback>" --explanation "<user explanation>" - Mark the attempt PASS when
-
Process an exhausted limit. Set the artifact
status: FAILED. Print the artifact path and return a non-zero status. Stop downstream chains.
Artifact
.cheese/hard-cheese/<slug>.md contains the audit trail. The .gitignore file excludes .cheese/, so the audit trail remains local.
Each file starts with this YAML frontmatter block:
---
slug: <slug>
attribution: Sankaranarayanan 2026 / vibecheck
rubric: SOLO Taxonomy (1-5), pass threshold = <passing-score>
passing_score: <n>
divergence: fail-open on judge error (vibecheck fails closed)
diff_base: <sha>
diff_head: <short-sha>
status: PASS | FAIL | FAILED | LOGGED | ERROR
attempts: <n>
---
append-attempt writes the attempt log as this six-column markdown table:
| timestamp | head_sha | status | score | feedback | explanation |
| --- | --- | --- | --- | --- | --- |
| 2026-06-25T10:00:00+00:00 | a1b2c3d | FAIL | 2 | "Unistructural: lists steps but no causal link" | <user explanation verbatim> |
| 2026-06-25T10:05:00+00:00 | a1b2c3d | PASS | 4 | "Relational: explains why invariant holds" | <user explanation verbatim> |
Each invocation appends attempts and does not overwrite rows. If HEAD changes, append new rows below the earlier rows.
Sub-agent contract — fresh judge
- Use fresh context for every invocation. The code-writing context can bias the judge.
- Resolve a no-tool or read-only
revieweratpowerfulpower andhigheffort. Use the shared agent resolver. - The shared resolver pins each reviewer to
powerful. Do not lower this value for the judge. - Use a general worker only with no-write enforcement. Set
degraded: true. - Use
references/judge-prompt.mdas the system prompt. - Give the judge the diff summary, the optional spec excerpt, the
targetsJSON, and the explanation. Require a JSON reply. Prohibit repository writes. - Parse the JSON output. On a parse error, log an
ERRORattempt and fail open.
The gate requires a host sub-agent feature. Without this feature, recommend /hard-cheese --no-judge to record the explanation without a grade.
Attribution
Sankaranarayanan, S. (2026). Mitigating 'Epistemic Debt' in Generative AI-Scaffolded Novice Programming using Metacognitive Scripts. Proceedings of the 13th ACM Conference on Learning at Scale. https://arxiv.org/abs/2602.20206
The implementation uses the open-source VS Code extension from the paper's author:
https://github.com/sreecharansankaranarayanan/vibecheck
This SKILL.md, references/judge-prompt.md, and each artifact include the attribution. Thus, the citation stays with the audit trail.
Divergence from the paper
Hard-cheese has three differences from vibecheck:
1. Judge errors. Vibecheck fails closed. The modal blocks code application until the judge recovers or the user retries.
Hard-cheese fails open. The gate records an ERROR, prints a warning, and returns 0.
This policy prevents API failures from blocking pull request work.
2. Telemetry content. Vibecheck records the length of an explanation. It never records the text of an explanation.
Hard-cheese records the complete text of every explanation in the local artifact. The .gitignore file excludes .cheese/, so this text remains on the author's machine.
Tell the user about this retention before --no-judge records the first explanation.
3. Prompt wording. Vibecheck's prompt states its three questions without a lead sentence.
Hard-cheese adds a purpose-first lead sentence and a pointer to the ranked regions. The paper's three questions stay verbatim.
Add each new difference to this section.
Composition with --auto
--hard and --auto can operate together. Terminal /plate --hard pauses automation once before publication, after /plate verifies the final artifacts.
The user responds to the prompt. PASS permits publication. FAILED stops publication. ERROR uses the documented fail-open behavior.
Commit-only /plate --hard does not run the gate. That path shares nothing. See references/composition.md for new pull requests and non-TTY behavior.
Output
When the gate ends, print:
Hard-cheese artifact: .cheese/hard-cheese/<slug>.md
Status: PASS | FAILED | LOGGED | ERROR
Score: <n>/5 (<SOLO level>, pass ≥ <passing-score>)
Attempts: <n>
The Score line reports the latest judged attempt. Omit this line for LOGGED mode or an ERROR without a scored attempt.
Then print one applicable message:
- On PASS:
Ready to share for review. - On FAILED:
Cap exhausted. Improve understanding of the change before sharing. - On LOGGED:
Telemetry only — judge skipped via --no-judge. - On ERROR: Print one warning that identifies the failure. Include
Fail-open divergence active — gate exited 0; you may share for review at your discretion.
Preferred tools and fallbacks
| Need | Prefer | Fallback |
|---|---|---|
| Diff inspection for the user-facing summary | delta | git diff --unified=3 |
| Ranking the hunks for the prompt | rank-hunks bundle command | none — skip the ranked regions and note the gap |
| Reading the spec (when present) | bounded file read per code-intelligence-routing.md | host file read |
| Spawning the judge | host sub-agent primitive (Agent() or harness equivalent) | none — without sub-agent spawn, run --no-judge mode and tell the user the judge is unavailable |
| GitHub / PR context (out of scope here) | n/a | n/a |
Rules
- Run the judge sub-agent in fresh context. Do not use the code-writing context to grade the author's understanding.
- Do not coach the user before the answer. The explanation is the artifact under test.
- Show only the judge's Socratic questions after a FAIL. Do not add hints.
- Show the ranked regions; add no question or hint before the first attempt.
- Pass the user's explanation to the judge unchanged.
- Always run the freshness check. A changed
HEADrequires a new attempt sequence. - Record every ERROR attempt. Show a warning for each judge failure.
- Do not call a GitHub tool or a pull request tool. The gate operates before code enters review.
- Apply the shared voice rules from
../age/references/voice.md. Report the result. Classify the residual risk ascertain | speculating | don't know. - Do not describe FAILED as
"almost passing".
References
references/judge-prompt.mddefines the SOLO Taxonomy rubric, judge prompt, and JSON output.references/composition.mddefines the complete--hardand--automatrix.references/commands.mdlists the generated bundle commands.
Agent resolution
Resolve the fresh judge through ../cheese/references/agent-resolution.md.
| Work | Preferred types | Permissions/isolation | Minimum power | Effort | Fallback |
|---|---|---|---|---|---|
| Grade the explanation | reviewer | no-tool or read-only, fresh-context | powerful | high | compatible reviewer, then general |
The canonical hard-cheese audit includes the shared agent_resolution block.
FAQ
What does the hard-cheese skill do?
Checks whether an author can explain a code change before review. Use when the user requests /hard-cheese, /cheese --hard, or an understanding check. Use it before a pull request or through the --hard pipeline flag. Do not use it for reviews, test hardening, or fixes.
How do I install hard-cheese?
Run npx skills add https://github.com/paulnsorensen/easy-cheese --skill hard-cheese in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/hard-cheese/ (personal) or .claude/skills/hard-cheese/ (project).
Which AI agents can use hard-cheese?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is hard-cheese safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains hard-cheese?
It is published by paulnsorensen in paulnsorensen/easy-cheese. It has 644 recorded installs.
More from paulnsorensen/easy-cheese
cook
paulnsorensen/easy-cheese
Implement an approved spec or focused task through stale-safe source edits. Use this skill when the user says "implement this", "cook this spec", or "fix this bug". Use it for /cook .cheese/specs/ .md. Also use it when the user says "go" with clear acceptance criteria. Run it alone for an unambiguous task. Use --prototype when the user says "prototype this", "go prototype it", or "spike this out" and wants to refine the result round by round. A spec helps but is not required. Do not use it for fuzzy planning (/mold). Do not use it for no-write discussion (/culture) or review-only work (/age).
mold
paulnsorensen/easy-cheese
Turn a high-leverage design decision into a validated spec through grounded dialogue. Use it when a leverage trigger fires (auth, irreversible effects, concurrency, contracts, destructive ops, a new slice, a cross-slice dependency, an invariant gap) or when the user asks for a spec or design. Typical phrases include "let's design X", "shape this into a spec", "what should the API for Z look like", and "/mold". A feature ask with zero fired triggers is a /cook mini-spec, not a mold; route it through /cheese. Do NOT use it for free-form discussion without artifact intent (/culture), direct implementation (/cook), or research-only questions (/briesearch). Ceremony scales to the ask through references/tiers.md, so a small clear change gets a validated mini-spec without a write-approval turn.
cure
paulnsorensen/easy-cheese
Apply selected fixes from an /age report, findings list, or CI failure. Run the project gates and hand a clean result to /plate. Use when the user wants selected findings resolved. Do not use for review, test authoring, or direct publication.
age
paulnsorensen/easy-cheese
Review a diff, PR, branch, or path across thirteen dimensions. Emit a severity-grouped findings report. Use when the user wants a code review. Trigger on "review this", "/age", "is this safe to merge", or "find bugs". Also trigger on "spot security issues", "check for slop", "review my PR", or "what's wrong with this code". Review every requested dimension. Review all thirteen dimensions by default. Do not apply fixes. Route them to /cure. Do not harden tests; route that work to /press.
cheese
paulnsorensen/easy-cheese
Route an idea, path, pull request, issue, failure, question, or bare /cheese to the correct workflow skill. Use this skill for /cheese, routing requests, help requests, or opening messages without a named workflow skill.
culture
paulnsorensen/easy-cheese
Models problems and trade-offs without writing code or specifications. Use when a workflow needs silent reasoning, or the user asks for "/culture", "no writes", "rubber-duck this", or "these bugs feel connected". Not for specs (/mold), code (/cook), or bug diagnosis and fixes (/pasteurize).
Popular code quality & workflow skills
grill-me
mattpocock/skills
A relentless interview to sharpen a plan or design.
improve-codebase-architecture
mattpocock/skills
Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.
handoff
mattpocock/skills
Compact the current conversation into a handoff document for another agent to pick up.
domain-modeling
mattpocock/skills
Build and sharpen a project's domain model. Use when discussing codebase terminology, writing or editing a GLOSSARY.md, or recording or editing an ADR.
codebase-design
mattpocock/skills
Shared vocabulary for designing deep modules. Use when the user wants to design or improve a module's interface, find deepening opportunities, decide where a seam goes, make code more testable or AI-navigable, or when another skill needs the deep-module vocabulary.
diagnosing-bugs
mattpocock/skills
Diagnosis loop for hard bugs and performance regressions. Use when the user says "diagnose"/"debug this", or reports something broken/throwing/failing/slow.