# Hard Cheese

> Metacognitive vibecheck gate before code is shared for review — make the author explain the diff's causal logic, graded by a fresh-context judge against the SOLO Taxonomy. Use when the user wants this gate — phrases like "/hard-cheese", "/cheese --hard", "gate this before I push", "vibecheck me", "make sure I understand this diff", "epistemic-debt check". Reads the working diff, asks the human au…

- **Type:** Skill
- **Install:** `agentstack add skill-paulnsorensen-easy-cheese-hard-cheese`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [paulnsorensen](https://agentstack.voostack.com/s/paulnsorensen)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [paulnsorensen](https://github.com/paulnsorensen)
- **Source:** https://github.com/paulnsorensen/easy-cheese/tree/main/skills/hard-cheese
- **Website:** https://cheeselord.dev/easy-cheese/

## Install

```sh
agentstack add skill-paulnsorensen-easy-cheese-hard-cheese
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# /hard-cheese

The gate mitigates **epistemic debt** — the failure mode where AI-scaffolded code passes review, type-checks, and tests green while the author cannot explain it to a reviewer.

## Inputs

```text
/hard-cheese [] [--socratic-cap N=3] [--no-judge]
```

Arguments:

- `` — optional. Identifies the artifact at `.cheese/hard-cheese/.md`. When omitted, fall back to the git short SHA of `HEAD`. An explicit slug always wins.
- `--socratic-cap N` — max retry attempts before the gate marks the artifact `FAILED` and exits non-zero. Default `3`. Vibecheck does not cap; easy-cheese does to avoid infinite loops.
- `--no-judge` — log-only mode. Capture the user's explanation, write the artifact with `status: LOGGED`, skip the judge sub-agent spawn. Mirrors vibecheck's optional JSONL telemetry mode.

## Invocation modes

| Mode | How it fires | Where the gate sits |
| --- | --- | --- |
| **standalone** | User runs `/hard-cheese ` directly before opening a pull request. | Outside the pipeline. No upstream skill required. |
| **propagated** | `/cure` invokes `/hard-cheese ` when `--hard` is in scope and the user selects the share-for-review option at cure's handoff (or, under `--auto --hard`, at the end of cure's final auto pass). | At the `cure → share for review` boundary — the moment code escapes the local machine. |

`--hard` propagates through `/cheese → /mold → /cook → /press → /age → /cure`. Upstream skills only pass the flag along; `/cure` is the only skill that actually invokes `/hard-cheese`. See `references/composition.md` for the full matrix.

## Flow

1. **Resolve scope.**
   - `diff_base = origin/main`, `diff_head = `.
   - If `.cheese/specs/.md` exists, load it as the intent reference (optional — diff is the ground truth).
   - Slug fallback when none supplied: the HEAD short SHA.
   - If the working tree has no diff against `origin/main`, exit `0` with `"nothing to gate on"` and write no artifact.

2. **Freshness check.**
   Check freshness before launching the gate:

   ```
   python3 ${CLAUDE_SKILL_DIR}/scripts/hard-cheese.pyz freshness-check --slug 
   ```

   Exit 0 (`previously_passed`): print `"previously passed"` and exit `0`. Exit 2 (`stale`) or 3 (`new`): continue to step 3.

3. **Compose the vibecheck prompt** (faithful to Sankaranarayanan 2026, generalised to "share for review" so the gate stays implementation-agnostic):

   > Before this is shared for review, explain its causal logic in your own words. How does ** work? Why does it produce the desired behavior? What state, control flow, or invariants does it rely on?

   Render a diff summary alongside the prompt: files changed, key hunks. Cap the diff excerpt at roughly 80 lines. The spec excerpt (if loaded) is shown above the diff summary.

4. **Capture the user's explanation** as free text. No coaching, no example answers — the explanation is the artifact under test.

5. **Spawn the judge sub-agent** in fresh context (same pattern `/ultracook` uses for adversarial review). The judge:
   - Reads `references/judge-prompt.md` as its system prompt.
   - Receives the diff summary, the spec excerpt (if any), and the user's explanation as context.
   - Returns a JSON object: `{score, level, pass, feedback, socratic_qs}`.

   See `references/judge-prompt.md` for the full system prompt and output shape.

   Skip this step when `--no-judge` is set: mark the attempt `status: LOGGED`, write the artifact, exit `0`.

6. **On judge result:**
   - `score >= 3` → PASS. `score  --status  --score  \
     --feedback "" --explanation ""
   ```

7. **On cap exhaustion:** set the artifact `status: FAILED`, print the path, exit non-zero. Downstream chains must not proceed.

## Artifact

`.cheese/hard-cheese/.md` is the audit trail. The directory is gitignored by repo convention (`.gitignore` already ignores `.cheese/`), so the trail stays local — matching vibecheck's local-only stance on telemetry.

Each file opens with a YAML frontmatter block that travels with the audit trail:

```yaml
---
slug: 
attribution: Sankaranarayanan 2026 / vibecheck
rubric: SOLO Taxonomy (1-5), pass threshold = 3
divergence: fail-open on judge error (vibecheck fails closed)
diff_base: 
diff_head: 
status: PASS | FAIL | FAILED | LOGGED
attempts: 
---
```

The attempt log uses a 6-column markdown table (written by `append-attempt`):

```markdown
| timestamp | head_sha | status | score | feedback | explanation |
| --- | --- | --- | --- | --- | --- |
| 2026-06-25T10:00:00+00:00 | a1b2c3d | FAIL | 2 | "Unistructural: lists steps but no causal link" |  |
| 2026-06-25T10:05:00+00:00 | a1b2c3d | PASS | 4 | "Relational: explains why invariant holds" |  |
```

Attempts append; nothing is overwritten within a single invocation. If a re-invocation finds the artifact stale (HEAD moved), new attempt rows are appended below the prior ones — the trail is cumulative.

## Sub-agent contract — fresh peer, not diminutive

- **Fresh context, every invocation.** Same-context judging is biased toward "yes you understand it" because the model that helped write the code believes the code is good. The fresh context is the entire reason the judge means anything.
- **`subagent_type: "general-purpose"`** with `references/judge-prompt.md` as the system prompt. Model inherits from the parent — do not pass `haiku` or any other tier downgrade.
- **No tools needed for the judge.** It reads the prompt, diff summary, spec excerpt, and explanation, then returns JSON. No file access, no shell, no MCP — the judge is a graded read-only call.
- **JSON output is parsed.** If parsing fails, the attempt is logged as `ERROR` and the gate fails open (see `## Divergence from the paper`).

If the host harness has no sub-agent primitive, `/hard-cheese` is the wrong skill — the gate cannot run without a fresh judge. Recommend `/hard-cheese --no-judge` for users who still want the explanation captured as telemetry without the grading step.

## 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. 

The implementation reference (intercept-at-acceptance, SOLO rubric, Socratic retry) is the open-source VS Code extension by the paper's author:

The attribution appears in this `SKILL.md`, in `references/judge-prompt.md`, and in every `.cheese/hard-cheese/.md` artifact so the citation travels with the audit trail.

## Divergence from the paper

Hard-cheese departs from vibecheck in exactly one place, and the divergence is called out explicitly so it stays legible:

**Vibecheck fails closed on judge error.** If the Judge LLM cannot produce a verdict, the modal blocks code application until the judge recovers or the user retries with a different model.

**Hard-cheese fails open on judge error.** If the fresh-context judge sub-agent crashes, times out, or returns malformed JSON, the gate writes an `ERROR` attempt, prints a clear warning, and exits `0` — the user is allowed to proceed.

Rationale: judge invocation is per-PR-attempt and per-retry, and a strict fail-closed policy creates a worse experience under API hiccups than the epistemic-debt cost it averts. New divergences must be added here.

## Composition with `--auto`

`--hard` and `--auto` may coexist. The gate is the **only** point at which `--hard` punctures `--auto`. Everywhere else, auto's skip-handoff semantics apply.

Concretely, under `/cure --auto --hard --stake medium+`:

- The pipeline runs auto through `cook → press → age → cure` per `--auto`'s normal contract.
- At the end of cure's final auto pass, the chain pauses and `/hard-cheese ` fires once.
- The user must respond to the vibecheck prompt. The judge grades.
- On PASS: chain exits with `"gate passed → ready to share for review"`.
- On FAILED (cap exhausted): chain exits non-zero with the artifact path; the user must improve their understanding before sharing.
- On ERROR: chain exits `0` with a warning (the fail-open divergence).

Non-TTY guard: see `references/composition.md` `## Non-TTY guard`.

`/cure --auto` alone (no `--hard`) is unchanged — the gate never fires. The single puncture point is documented in `references/composition.md` and in `skills/cure/SKILL.md`.

## Output

When the gate ends, print:

```
Hard-cheese artifact: .cheese/hard-cheese/.md
Status: PASS | FAILED | LOGGED | ERROR
Attempts: 
```

Followed by:

- 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: a one-line warning naming the failure mode and `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` |
| Reading the spec (when present) | `cheez-read` | 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

- The judge sub-agent runs in fresh context. Do not let the same conversation that wrote the code grade the human's understanding of it.
- Do not coach the user before they answer. The explanation is the artifact under test. Socratic questions appear only *after* a FAIL, and only the questions returned by the judge — no extra hints from the parent.
- Do not paraphrase the user's explanation before passing it to the judge. The judge grades what the user wrote, verbatim.
- Do not skip the freshness check. Re-invoking after HEAD has moved must trigger a fresh attempt sequence — prior comprehension is stale once the code changes.
- Do not silently drop ERROR attempts. The fail-open divergence requires that every judge failure is recorded in the artifact and surfaced to the user as a warning.
- Do not invoke `/gh` or any specific PR-creation tool. The gate's contract is "before code is shared for review" — implementation-agnostic.
- Apply the shared voice kernel (lives at `skills/age/references/voice.md` in this repo): say what the gate result was, flag residual risk as `certain | speculating | don't know`, do not soften FAILED into "almost passing".

## References

- `references/judge-prompt.md` — SOLO Taxonomy rubric, judge sub-agent system prompt, JSON output shape.
- `references/composition.md` — the full `--hard` / `--auto` matrix and the single puncture point.
- `${CLAUDE_SKILL_DIR}/scripts/hard-cheese.pyz freshness-check` — checks whether a previous PASS is still fresh for the current HEAD (step 2).
- `${CLAUDE_SKILL_DIR}/scripts/hard-cheese.pyz append-attempt` — atomically appends an attempt row to the audit trail (step 6).

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [paulnsorensen](https://github.com/paulnsorensen)
- **Source:** [paulnsorensen/easy-cheese](https://github.com/paulnsorensen/easy-cheese)
- **License:** MIT
- **Homepage:** https://cheeselord.dev/easy-cheese/

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** yes
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-paulnsorensen-easy-cheese-hard-cheese
- Seller: https://agentstack.voostack.com/s/paulnsorensen
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
