Purpose
Verify that the human absorbed a completed change, the object under test is the
person merging the work, never the artifact. After Claude finishes a change, generate an
HTML report of what was done (context, intuition, decisions) with a quiz at the bottom the
user answers. The failure mode this addresses: people glaze over plans and explainers, so
the human merging a PR cannot represent the change to a reviewer and their mental model of
the codebase decays, degrading future prompting.
Three value props:
- Representation accountability. Can the user explain this change to a reviewer?
- Loop retention. Keeping the user's mental model of the codebase current keeps their
prompting sharp.
- Late intent-mismatch detection. A failed quiz surfaces "that's not what I intended"
while there is still time to fix it, before merge.
Use when: the user asks to be quizzed on completed work, or to recall past work
(quiz me, do I understand this change, what did we do on <ticket>). Skip when:
the request is to verify the artifact (does it work / is it right), to extract the user's
intent before work starts, or to coach a general subject, see "What this skill does NOT
do". This skill auto-invokes (no disable-model-invocation) so policy-driven offers can
fire; /education:quiz-me is the guaranteed path.
Effective configuration (substituted at load)
The values below substitute from this plugin's stored configuration when this skill loads.
A surviving literal ${user_config.…} placeholder means that key is unset, apply its
documented unset behavior.
| Key |
Value |
Unset behavior |
quiz_policy |
${user_config.quiz_policy} |
on-request. Act only when invoked. Values govern OFFER CADENCE only (see "Non-gating posture"). Unknown value → treat as on-request. |
report_library_dir |
${user_config.report_library_dir} |
unset → artifacts land under ${CLAUDE_PLUGIN_DATA} (see "Retention mechanics"). Set to a corpus checkout to redirect the library root there. |
Configure via the /plugin dialog, or headless with claude plugin install education@<marketplace> --config KEY=VALUE (your installed marketplace name). A literal
non-home report_library_dir may be blocked by the hardcoded-path guardrails. When a write is
blocked, report the block and fall back to the ${CLAUDE_PLUGIN_DATA} default rather than
working around the guardrail.
Action router
Parse $ARGUMENTS: recall is the ONLY reserved first token. Anything else, including no
arguments, is the default action, with any argument text taken as context describing the
change to quiz on.
| Action |
Purpose |
(default. Empty, or any first token other than recall) |
Offer or generate a report + quiz for the change just completed; any argument text is context describing the change. Offer vs generate depends on who invoked it (below). |
recall <query> |
Answer "what did we do on X" from the retained report library first, git/tracker archaeology second, stating which source answered (see "Recall"). |
The default action branches on who invoked it: a user-initiated invocation
(/education:quiz-me, or "quiz me") is itself acceptance, generate the report + quiz
immediately. A model-initiated invocation that fires to satisfy quiz_policy
always/above-threshold is an OFFER, present it and wait for the user to accept before
generating anything (see "Non-gating posture"; generation is always user-confirmed).
Report contract
Produce a self-contained single-file HTML report (all CSS/JS inline, no remote fetch,
openable via file://, synthetic data only, never real secrets or tokens). Markdown
fallback where the project convention prefers it. Sections: context, intuition, decisions,
what-was-done, then the quiz at the bottom the user must answer, the canonical prompt
pattern ("a quiz at the bottom on the changes that I must pass"). Match each narrative
section's length to what the change needs: cover the substance, but do not pad with filler,
redundant summaries, or boilerplate.
- Questions are diff-sourced. Author each quiz question from the change's actual diff
and the report sections that explain it, never from generic topic knowledge a reader
could answer without having followed this change.
- Each question carries a source anchor, and a miss routes to it. Anchor every
question to the report section that teaches its answer (a report-internal anchor, or a
durable pointer per the reference discipline below). On a missed question, send the
reader to that exact section — the skimmed material, quoted or linked — before any
retry; the miss's job is routing, not scoring.
- The answer key is fresh-context authored. Produce or verify the embedded key with a
fresh-context pass that reads only the report and the diff, not the authoring
conversation, so grading tests what the artifact actually supports rather than what its
author remembers meaning. Where no fresh sub-agent surface is available, re-derive the
key from the artifact alone before embedding it.
- Answer key persists with the artifact. Embed the key in the report, a collapsed
<details> block in HTML, an appendix section in the markdown fallback. Grade
in-conversation in the same session; a later or compacted session grades by reading the
key back from the retained artifact, re-deriving from the report + diff only when the
key is missing. Without the embedded key a report recalled weeks later could not be
graded at all.
- A failed quiz is a signal, not a gate. Surface it as a possible intent mismatch to
resolve before merge; never block the merge yourself (see "Non-gating posture").
- Reference discipline, durable pointers only. The report is self-contained.
Restrict any external reference to durable, checkout-independent pointers: PR/issue
URLs, commit SHAs or permalinks, promoted docs reachable on the default branch. Never
link memory-tier paths (
.work/…) or contract-slice paths (docs/topics/…). Both are
pruned or checkout-local and will dangle. Distill ephemeral inputs (exploration/research
notes, session context) inline instead of linking them.
Retention mechanics
Artifacts NEVER land in the consuming repo's working tree. They land under a per-repo
library keyed on repo identity, not checkout path. This repo's per-ticket worktrees
are pruned after merge, so a path-keyed slug would strand every report under a dead slug
and leave recall from the main clone empty. The remote is normalized to a
protocol-agnostic host/org/repo form before hashing, so the SSH and HTTPS remotes of one
repo resolve to the same library. Derive the slug and destination:
url="$(git remote get-url origin 2>/dev/null)"
if [ -n "$url" ]; then
canon="$(printf '%s' "$url" | sed -e 's/\.git$//' -e 's#/$##' \
-e 's#^[a-z+]*://##' -e 's#^[^@/]*@##' -e 's#:#/#')"
base="$(printf '%s' "$canon" | sed -e 's#^.*[/:]##' | tr '[:upper:]' '[:lower:]' \
| tr -cs 'a-z0-9' '-' | sed 's/^-*//;s/-*$//')"
hash="$(printf '%s' "$canon" | { sha256sum 2>/dev/null || shasum -a 256; } | cut -c1-8)"
else # no remote — fall back to canonicalized project path
p="$(realpath "${CLAUDE_PROJECT_DIR}" 2>/dev/null \
|| readlink -f "${CLAUDE_PROJECT_DIR}" 2>/dev/null || printf '%s' "${CLAUDE_PROJECT_DIR}")"
base="$(basename "$p" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-' | sed 's/^-*//;s/-*$//')"
hash="$(printf '%s' "$p" | { sha256sum 2>/dev/null || shasum -a 256; } | cut -c1-8)"
fi
repo_slug="$base-$hash"
- Destination:
${CLAUDE_PLUGIN_DATA}/$repo_slug/quiz-me/reports/ by default, or
<report_library_dir>/$repo_slug/quiz-me/reports/ when that userConfig is set. The
quiz-me/ segment fences these artifacts off from teach's path-keyed workspaces in the
shared per-plugin data directory.
- Repo-tree guard: resolve
report_library_dir to an absolute path before writing; if
it is ${CLAUDE_PROJECT_DIR} or nested under it, refuse it, warn the user, and fall back
to the ${CLAUDE_PLUGIN_DATA} default, reports must never land in the consuming repo's
working tree, regardless of how the userConfig is set.
- Filename:
<date>-<change-slug>-<short-hash>.html (or .md). <date> is
YYYY-MM-DD, <change-slug> a kebab slug of the change (from the PR/ticket title),
<short-hash> the short HEAD commit hash, so reports stay unique and sortable.
Non-gating posture
quiz_policy governs offer cadence only. No value ever auto-generates a report.
Generation is ALWAYS user-confirmed: the report + quiz is produced only after the user
accepts an offer, and a direct invocation ("quiz me") is itself that acceptance.
quiz_policy |
Offer behavior |
off |
Never offers. Direct invocation still works. |
on-request (default) |
Offers only when the user asks. |
always |
Suggests a quiz after each completed change. |
above-threshold |
Suggests when the change meets the threshold below. |
- Threshold (
above-threshold): the change meets ANY of, more than 5 files touched,
more than 200 changed LOC, or the governing plan records blast radius HIGH/CRITICAL.
Resolve the default branch first, then judge from the merge-base diff against its
remote-tracking ref at offer time (well-defined even after commits, and correct in a PR
worktree with no local branch): d="$(git remote show origin 2>/dev/null | awk '/HEAD branch/ {print $NF}')"; git diff --stat "$(git merge-base HEAD "origin/$d")"..HEAD. If
origin is absent the threshold is unjudgeable, offer nothing.
- Offers are best-effort, model-initiated from the description triggers and this
posture, there is no hook. An unknown
quiz_policy value falls back to on-request.
Recall
recall <query> answers "what did we do on X" (a ticket, a change, a date). Search the
retained report library (see "Retention mechanics") FIRST; fall back to git history and
the tracker second. State which source answered.
Coverage boundary: the library holds ONLY work that was actually quizzed
(retention-at-write). It is not a general work-history engine. When a query names work
that was never quizzed, say so and route to the git/tracker archaeology fallback rather
than implying the library is complete.
Composition
This skill is an optional post-work step; it never edits another plugin's gate sequence.
Consumers that run a staged workflow (session-flow's workflow skill, if installed) can
route to /education:quiz-me as a comprehension step by pointer, the composition lives
here and in that consumer's own on-ramps, not by mutating a shared stage list.
Gotchas
- The object is the human, not the artifact. If you find yourself checking whether the
code works, you are in the wrong skill (see "What this skill does NOT do").
- Never write to the consuming repo's tree. Reports go to the retention library only;
a report committed into the product repo is a defect, and a
report_library_dir pointing
inside the repo tree is refused and defaulted, not honored.
- Embed the answer key in the artifact. A report with no key cannot be graded in a
later session, the retention use case depends on it.
- Don't imply library completeness on
recall. Only quizzed work is retained; name
the boundary and fall back to archaeology for the rest.
- Durable pointers only in reports. A link to
.work/… or docs/topics/… dangles the
moment the checkout is pruned; distill those inputs inline instead.
What this skill does NOT do
- Not artifact verification. "Did we build the right thing, and does it work?", object
= the artifact, belongs to
verification:confirm (if installed). This skill's object is
the human's comprehension of completed work.
- Not pre-work intent extraction.
planning:interview (if installed) runs BEFORE work
to extract the USER's intent, where the user holds the answers. Here the work is done and
Claude holds the answer key; a failed quiz can surface an intent mismatch after the fact.
- Not teach's
assess / exercise actions. /education:teach assess and
/education:teach exercise quiz the learner on LEARNING CONTENT inside a teach workspace.
/education:quiz-me's object is the COMPLETED WORK of a change, with no learning
workspace. Namespacing keeps them distinct.
- Not a merge gate. A failed quiz is a signal to resolve, not a block,
quiz_policy
only tunes how often a quiz is OFFERED, never whether the merge proceeds, and no report
generates without the user's confirmation.
1---2name: quiz-me3description: Post-work comprehension check: after a change is complete, generate a self-contained HTML report of what was done (context, intuition, decisions) with a quiz at the bottom that you answer. Verifying the HUMAN absorbed the work, not the artifact. Non-gating by default; the quiz_policy userConfig tunes offer cadence. Also recalls prior work from the retained report library. Use when: 'quiz me', 'quiz me on this change', 'do I understand this change', 'comprehension check', 'a quiz at the bottom that I must pass', 'I want to make sure I understand everything that happened', 'what did we do on <ticket>'. Sibling to education:teach (multi-session coach) and education:explain (one-shot explainer); this verifies comprehension of COMPLETED WORK. Not artifact verification. That is verification:confirm (if installed).4---56## Purpose78Verify that the **human** absorbed a completed change, the object under test is the9person merging the work, never the artifact. After Claude finishes a change, generate an10HTML report of what was done (context, intuition, decisions) with a quiz at the bottom the11user answers. The failure mode this addresses: people glaze over plans and explainers, so12the human merging a PR cannot represent the change to a reviewer and their mental model of13the codebase decays, degrading future prompting.1415Three value props:1617- **Representation accountability**. Can the user explain this change to a reviewer?18- **Loop retention**. Keeping the user's mental model of the codebase current keeps their19 prompting sharp.20- **Late intent-mismatch detection**. A failed quiz surfaces "that's not what I intended"21 while there is still time to fix it, before merge.2223**Use when:** the user asks to be quizzed on completed work, or to recall past work24(`quiz me`, `do I understand this change`, `what did we do on <ticket>`). **Skip when:**25the request is to verify the artifact (does it work / is it right), to extract the user's26intent before work starts, or to coach a general subject, see "What this skill does NOT27do". This skill auto-invokes (no `disable-model-invocation`) so policy-driven offers can28fire; `/education:quiz-me` is the guaranteed path.2930## Effective configuration (substituted at load)3132The values below substitute from this plugin's stored configuration when this skill loads.33A surviving literal `${user_config.…}` placeholder means that key is unset, apply its34documented unset behavior.3536| Key | Value | Unset behavior |37| --- | --- | --- |38| `quiz_policy` | `${user_config.quiz_policy}` | `on-request`. Act only when invoked. Values govern OFFER CADENCE only (see "Non-gating posture"). Unknown value → treat as `on-request`. |39| `report_library_dir` | `${user_config.report_library_dir}` | unset → artifacts land under `${CLAUDE_PLUGIN_DATA}` (see "Retention mechanics"). Set to a corpus checkout to redirect the library root there. |4041Configure via the `/plugin` dialog, or headless with `claude plugin install42education@<marketplace> --config KEY=VALUE` (your installed marketplace name). A literal43non-home `report_library_dir` may be blocked by the hardcoded-path guardrails. When a write is44blocked, report the block and fall back to the `${CLAUDE_PLUGIN_DATA}` default rather than45working around the guardrail.4647## Action router4849Parse `$ARGUMENTS`: `recall` is the ONLY reserved first token. Anything else, including no50arguments, is the default action, with any argument text taken as context describing the51change to quiz on.5253| Action | Purpose |54| --- | --- |55| *(default. Empty, or any first token other than `recall`)* | Offer or generate a report + quiz for the change just completed; any argument text is context describing the change. Offer vs generate depends on who invoked it (below). |56| `recall <query>` | Answer "what did we do on X" from the retained report library first, git/tracker archaeology second, stating which source answered (see "Recall"). |5758The default action branches on **who invoked it**: a **user-initiated** invocation59(`/education:quiz-me`, or "quiz me") is itself acceptance, generate the report + quiz60immediately. A **model-initiated** invocation that fires to satisfy `quiz_policy`61`always`/`above-threshold` is an OFFER, present it and wait for the user to accept before62generating anything (see "Non-gating posture"; generation is always user-confirmed).6364## Report contract6566Produce a **self-contained single-file HTML** report (all CSS/JS inline, no remote fetch,67openable via `file://`, synthetic data only, never real secrets or tokens). Markdown68fallback where the project convention prefers it. Sections: context, intuition, decisions,69what-was-done, then the **quiz at the bottom** the user must answer, the canonical prompt70pattern ("a quiz at the bottom on the changes that I must pass"). Match each narrative71section's length to what the change needs: cover the substance, but do not pad with filler,72redundant summaries, or boilerplate.7374- **Questions are diff-sourced.** Author each quiz question from the change's actual diff75 and the report sections that explain it, never from generic topic knowledge a reader76 could answer without having followed this change.77- **Each question carries a source anchor, and a miss routes to it.** Anchor every78 question to the report section that teaches its answer (a report-internal anchor, or a79 durable pointer per the reference discipline below). On a missed question, send the80 reader to that exact section — the skimmed material, quoted or linked — before any81 retry; the miss's job is routing, not scoring.82- **The answer key is fresh-context authored.** Produce or verify the embedded key with a83 fresh-context pass that reads only the report and the diff, not the authoring84 conversation, so grading tests what the artifact actually supports rather than what its85 author remembers meaning. Where no fresh sub-agent surface is available, re-derive the86 key from the artifact alone before embedding it.87- **Answer key persists with the artifact.** Embed the key in the report, a collapsed88 `<details>` block in HTML, an appendix section in the markdown fallback. Grade89 in-conversation in the same session; a later or compacted session grades by reading the90 key back from the retained artifact, re-deriving from the report + diff only when the91 key is missing. Without the embedded key a report recalled weeks later could not be92 graded at all.93- **A failed quiz is a signal, not a gate.** Surface it as a possible intent mismatch to94 resolve before merge; never block the merge yourself (see "Non-gating posture").95- **Reference discipline, durable pointers only.** The report is self-contained.96 Restrict any external reference to durable, checkout-independent pointers: PR/issue97 URLs, commit SHAs or permalinks, promoted docs reachable on the default branch. Never98 link memory-tier paths (`.work/…`) or contract-slice paths (`docs/topics/…`). Both are99 pruned or checkout-local and will dangle. Distill ephemeral inputs (exploration/research100 notes, session context) inline instead of linking them.101102## Retention mechanics103104Artifacts NEVER land in the consuming repo's working tree. They land under a per-repo105library keyed on **repo identity, not checkout path**. This repo's per-ticket worktrees106are pruned after merge, so a path-keyed slug would strand every report under a dead slug107and leave `recall` from the main clone empty. The remote is normalized to a108protocol-agnostic `host/org/repo` form before hashing, so the SSH and HTTPS remotes of one109repo resolve to the same library. Derive the slug and destination:110111```bash112url="$(git remote get-url origin 2>/dev/null)"113if [ -n "$url" ]; then114 canon="$(printf '%s' "$url" | sed -e 's/\.git$//' -e 's#/$##' \115 -e 's#^[a-z+]*://##' -e 's#^[^@/]*@##' -e 's#:#/#')"116 base="$(printf '%s' "$canon" | sed -e 's#^.*[/:]##' | tr '[:upper:]' '[:lower:]' \117 | tr -cs 'a-z0-9' '-' | sed 's/^-*//;s/-*$//')"118 hash="$(printf '%s' "$canon" | { sha256sum 2>/dev/null || shasum -a 256; } | cut -c1-8)"119else # no remote — fall back to canonicalized project path120 p="$(realpath "${CLAUDE_PROJECT_DIR}" 2>/dev/null \121 || readlink -f "${CLAUDE_PROJECT_DIR}" 2>/dev/null || printf '%s' "${CLAUDE_PROJECT_DIR}")"122 base="$(basename "$p" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-' | sed 's/^-*//;s/-*$//')"123 hash="$(printf '%s' "$p" | { sha256sum 2>/dev/null || shasum -a 256; } | cut -c1-8)"124fi125repo_slug="$base-$hash"126```127128- **Destination:** `${CLAUDE_PLUGIN_DATA}/$repo_slug/quiz-me/reports/` by default, or129 `<report_library_dir>/$repo_slug/quiz-me/reports/` when that userConfig is set. The130 `quiz-me/` segment fences these artifacts off from teach's path-keyed workspaces in the131 shared per-plugin data directory.132- **Repo-tree guard:** resolve `report_library_dir` to an absolute path before writing; if133 it is `${CLAUDE_PROJECT_DIR}` or nested under it, refuse it, warn the user, and fall back134 to the `${CLAUDE_PLUGIN_DATA}` default, reports must never land in the consuming repo's135 working tree, regardless of how the userConfig is set.136- **Filename:** `<date>-<change-slug>-<short-hash>.html` (or `.md`). `<date>` is137 `YYYY-MM-DD`, `<change-slug>` a kebab slug of the change (from the PR/ticket title),138 `<short-hash>` the short HEAD commit hash, so reports stay unique and sortable.139140## Non-gating posture141142`quiz_policy` governs **offer cadence only**. No value ever auto-generates a report.143Generation is ALWAYS user-confirmed: the report + quiz is produced only after the user144accepts an offer, and a direct invocation ("quiz me") is itself that acceptance.145146| `quiz_policy` | Offer behavior |147| --- | --- |148| `off` | Never offers. Direct invocation still works. |149| `on-request` (default) | Offers only when the user asks. |150| `always` | Suggests a quiz after each completed change. |151| `above-threshold` | Suggests when the change meets the threshold below. |152153- **Threshold** (`above-threshold`): the change meets ANY of, more than 5 files touched,154 more than 200 changed LOC, or the governing plan records blast radius HIGH/CRITICAL.155 Resolve the default branch first, then judge from the merge-base diff against its156 remote-tracking ref at offer time (well-defined even after commits, and correct in a PR157 worktree with no local branch): `d="$(git remote show origin 2>/dev/null | awk '/HEAD158 branch/ {print $NF}')"; git diff --stat "$(git merge-base HEAD "origin/$d")"..HEAD`. If159 `origin` is absent the threshold is unjudgeable, offer nothing.160- Offers are **best-effort**, model-initiated from the description triggers and this161 posture, there is no hook. An unknown `quiz_policy` value falls back to `on-request`.162163## Recall164165`recall <query>` answers "what did we do on X" (a ticket, a change, a date). Search the166retained report library (see "Retention mechanics") FIRST; fall back to git history and167the tracker second. **State which source answered.**168169**Coverage boundary:** the library holds ONLY work that was actually quizzed170(retention-at-write). It is not a general work-history engine. When a query names work171that was never quizzed, say so and route to the git/tracker archaeology fallback rather172than implying the library is complete.173174## Composition175176This skill is an optional post-work step; it never edits another plugin's gate sequence.177Consumers that run a staged workflow (session-flow's workflow skill, if installed) can178route to `/education:quiz-me` as a comprehension step by pointer, the composition lives179here and in that consumer's own on-ramps, not by mutating a shared stage list.180181## Gotchas182183- **The object is the human, not the artifact.** If you find yourself checking whether the184 code works, you are in the wrong skill (see "What this skill does NOT do").185- **Never write to the consuming repo's tree.** Reports go to the retention library only;186 a report committed into the product repo is a defect, and a `report_library_dir` pointing187 inside the repo tree is refused and defaulted, not honored.188- **Embed the answer key in the artifact.** A report with no key cannot be graded in a189 later session, the retention use case depends on it.190- **Don't imply library completeness on `recall`.** Only quizzed work is retained; name191 the boundary and fall back to archaeology for the rest.192- **Durable pointers only in reports.** A link to `.work/…` or `docs/topics/…` dangles the193 moment the checkout is pruned; distill those inputs inline instead.194195## What this skill does NOT do196197- **Not artifact verification.** "Did we build the right thing, and does it work?", object198 = the artifact, belongs to `verification:confirm` (if installed). This skill's object is199 the human's comprehension of completed work.200- **Not pre-work intent extraction.** `planning:interview` (if installed) runs BEFORE work201 to extract the USER's intent, where the user holds the answers. Here the work is done and202 Claude holds the answer key; a failed quiz can surface an intent mismatch after the fact.203- **Not teach's `assess` / `exercise` actions.** `/education:teach assess` and204 `/education:teach exercise` quiz the learner on LEARNING CONTENT inside a teach workspace.205 `/education:quiz-me`'s object is the COMPLETED WORK of a change, with no learning206 workspace. Namespacing keeps them distinct.207- **Not a merge gate.** A failed quiz is a signal to resolve, not a block, `quiz_policy`208 only tunes how often a quiz is OFFERED, never whether the merge proceeds, and no report209 generates without the user's confirmation.