feat(education): add quiz-me post-work comprehension-verification skill - #807
Conversation
#799) Contract slice for the education quiz-me skill: interview-locked Brief, Tier-B design resolution, and the five-phase plan with reviewer findings folded in. Awaiting plan approval before implementation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4RMR3G1rHAdNjxTqenVJL
Generation always user-confirmed (policies are offer-cadence only), recall coverage bounded to quizzed work, report reference discipline restricted to durable checkout-independent pointers with ephemeral inputs distilled inline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4RMR3G1rHAdNjxTqenVJL
Post-work comprehension-verification skill: after a change is complete, generate a self-contained HTML report of what was done with a quiz the human answers, verifying the person absorbed the work rather than the artifact. Non-gating by default with opt-in gating via a quiz_policy userConfig; retention to a repo-identity-keyed library under CLAUDE_PLUGIN_DATA; a recall action queries the retained library first, git/tracker archaeology second. Boundaries documented against verification:confirm, planning:interview, and teach assess/exercise. Refs #799. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4RMR3G1rHAdNjxTqenVJL
…config Add the plugin's first userConfig (quiz_policy string default on-request; report_library_dir directory, no default), bump 0.4.0 -> 0.5.0, and add the quiz/comprehension keywords. Top-insert the 0.5.0 CHANGELOG entry. Rewrite the README Configuration section — which previously stated the plugin has no userConfig — to document both keys and their defaults, and add quiz-me to the skill list and sibling framing. Refs #799. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4RMR3G1rHAdNjxTqenVJL
Six model-graded evals covering the load-bearing behaviors: canonical blog prompt yields report + quiz with an embedded answer key; default policy is non-gating; quiz_policy off suppresses unsolicited offers; artifacts land in the repo-identity-keyed library, never the repo tree; recall queries the library before git/tracker archaeology and names its coverage boundary; an artifact-verification request routes to verification:confirm instead of being quizzed. Refs #799. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4RMR3G1rHAdNjxTqenVJL
Add quiz and comprehension to the education marketplace entry's discovery tags, and regenerate the root README plugin catalog so its education description matches the updated manifest. Refs #799. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4RMR3G1rHAdNjxTqenVJL
…semantics The approval-round bound made quiz_policy govern offer cadence only — no value gates a merge and no value auto-generates. Replace the residual "opt-in gating" phrasing in four places (the SKILL.md frontmatter description, the SKILL.md "What this skill does NOT do" merge-gate bullet, the 0.5.0 CHANGELOG entry, and the README quiz-me bullet) with offer-cadence wording that no longer implies quiz_policy can block a merge. The README Configuration table row was already correct. Refs #799. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4RMR3G1rHAdNjxTqenVJL
|
Claude finished @kyle-sexton's task in 3m 4s —— View job Security Review — PR #807
|
|
Claude finished @kyle-sexton's task in 2m 41s —— View job PR Review:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a714a0dd1c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… exception entry, policy evals, library_dir phrasing Address four branch-owned findings from the PR #807 independent review: 1. Retention slug split across SSH vs HTTPS remotes of the same repo (git@host:org/repo.git and https://host/org/repo.git hashed differently). Root-cause fix in the SKILL.md slug snippet: normalize the remote to a protocol-agnostic host/org/repo form (strip scheme, userinfo, and scp-style colon) before hashing, and note in the prose that SSH and HTTPS remotes of one repo now share a library. 2. quiz-me's object-pronoun qualifier was a fifth naming deviation with no entry on PLUGIN-PHILOSOPHY.md's per-name exception list, its rationale living only in the prunable plan slice. Add the entry in the existing style. 3. Evals covered only 2 of 4 quiz_policy branches. Add above-threshold (offer, not auto-generate; generation still waits for acceptance) and always (offer after a change; declining produces no report). 4. "same class as knowledge's library_dir" overstated the parallel (library_dir is project-relative; report_library_dir is a standalone base path). Rephrase SKILL.md and README to claim only the shared hardcoded-path-guardrail collision and #798 indirection adoption. Refs #799. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4RMR3G1rHAdNjxTqenVJL
Close-out per the topic-docs lifecycle: the approved PLAN.md is published in the PR #807 description; the naming-grammar exception graduated into docs/PLUGIN-PHILOSOPHY.md; remaining follow-up work is tracked in #798. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4RMR3G1rHAdNjxTqenVJL
|
Claude finished @kyle-sexton's task in 2m 25s —— View job PR Review:
|
|
Claude finished @kyle-sexton's task in 2m 30s —— View job Security Review — PR #807
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0359bf8ad7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… install target, offer routing, repo-tree guard Address four valid findings from the PR #807 bot reviews (Claude lane + Codex): 1. The above-threshold command carried a literal <default-branch> placeholder. Resolve the default branch from the origin remote first (git remote show origin | awk '/HEAD branch/'), then diff against the merge-base, so the command runs as written. 2. The headless install examples omitted the required plugin argument. Add education@melodic-software to the `claude plugin install` commands in SKILL.md and README, matching the README Install section. 3. The action router's default row implied generation on every invocation. Distinguish who invoked it: a user-initiated invocation is acceptance and generates; a model-initiated invocation firing to satisfy quiz_policy always/above-threshold is an OFFER that waits for acceptance — consistent with the Non-gating posture section. 4. A report_library_dir set to ${CLAUDE_PROJECT_DIR} or inside it would write reports into the consuming repo, violating the never-in-repo-tree invariant. Add a repo-tree guard to Retention mechanics — resolve the dir, refuse it when it is the repo tree, warn, and fall back to the ${CLAUDE_PLUGIN_DATA} default — with a matching Gotchas note. Refs #799. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4RMR3G1rHAdNjxTqenVJL
|
Claude finished @kyle-sexton's task in 2m 2s —— View job PR Review —
|
|
Claude finished @kyle-sexton's task in 2m 38s —— View job Security Review — PR #807
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 872f20b93e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The bot-review fix that added the plugin target to the SKILL.md install example hardcoded education@melodic-software, which the plugin-contracts gate bans in skills/**/*.md — reusable skill content must not require a publisher-specific runtime identifier. Replace it with the marketplace-agnostic placeholder education@<marketplace>. The README Install section legitimately names the marketplace and is not scanned by this rule; left as committed. Refs #799. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4RMR3G1rHAdNjxTqenVJL
|
Claude finished @kyle-sexton's task in 2m 17s —— View job PR Review:
|
|
Claude finished @kyle-sexton's task in 2m 25s —— View job Security Review — PR #807
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 054b2e6118
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Two P2 findings from the PR #807 Codex review: 1. The above-threshold command resolved the default branch name but diffed against a bare local ref, which does not exist in a PR worktree that only fetched origin/main — git merge-base then errored. Qualify with the remote-tracking ref (origin/$d), and state that when origin is absent the threshold is unjudgeable and the skill offers nothing. 2. The action router advertised free-text invocation ("quiz me on this change") but defined only a no-args default and recall, leaving any non-recall argument unrouted. Make recall the only reserved first token; everything else — including empty — is the default action, with argument text taken as context describing the change to quiz on. Refs #799. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4RMR3G1rHAdNjxTqenVJL
|
Claude finished @kyle-sexton's task in 3m 2s —— View job Security Review — PR #807
|
|
Claude finished @kyle-sexton's task in 2m 5s —— View job PR Review:
|
| Finding | Source | Round | Status |
|---|---|---|---|
docs/topics/education-quiz-me/ not pruned |
Claude review | 1 | ✅ Fixed 0359bf8 |
<default-branch> placeholder in threshold command |
Claude review | 1 | ✅ Fixed 872f20b |
| Install command missing plugin target | Codex P2 / Claude | 1–2 | ✅ Fixed 872f20b |
| Model-initiated offer → should not auto-generate | Codex P2 | 2 | ✅ Fixed 872f20b |
Repo-tree guard for report_library_dir |
Codex P2 | 2 | ✅ Fixed 872f20b |
| Publisher-specific ID in reusable skill body | Codex P1 | 3 | ✅ Fixed 054b2e6 |
Threshold origin/$d missing in PR worktrees |
Codex P2 | 3 | ✅ Fixed bde4056 |
Non-recall arguments silently dropped |
Codex P2 | 4 | ✅ Fixed bde4056 |
| Protocol-agnostic SSH/HTTPS slug normalization | Claude | — | ✅ Fixed ac9c875 |
quiz-me naming exception in PLUGIN-PHILOSOPHY.md |
Claude | — | ✅ Fixed ac9c875 |
Policy evals (above-threshold, always+decline) |
Claude | — | ✅ Fixed ac9c875 |
Remaining findings (SUGGESTION / PLAUSIBLE)
Both were raised in multiple prior security review passes and remain unresolved at HEAD. Neither is a blocker — both are one-liner additions to SKILL.md.
Finding 1 — SUGGESTION (PLAUSIBLE): HTML-encoding of git-sourced content in reports
SKILL.md lines 66–87: The Report contract prohibits remote fetch and real secrets, but contains no instruction to HTML-encode strings drawn from git output (file paths, branch names, commit messages, PR titles) before embedding them in the report body.
A collaborator with write access could craft a commit message containing <script>…</script> that executes when the user opens the report via file://. file:// contexts have no shared cookie domain and Chrome/Firefox restrict cross-origin fetch() from local files, but the script could still exfiltrate the in-page content (the report body, architectural decisions, and embedded answer key). Attack surface is narrow — repo write access is required — but the fix is a single sentence in the Report contract.
Recommended addition (after the "Reference discipline" bullet, around line 87):
HTML-encode all git-sourced strings (file paths, branch names, commit messages, PR titles) before embedding them in report body and heading elements — never interpolate them verbatim into HTML tags.
Finding 2 — SUGGESTION (PLAUSIBLE): Repo-tree guard should canonicalize both sides
SKILL.md lines 119–122: The guard resolves report_library_dir to an absolute path before comparing against ${CLAUDE_PROJECT_DIR}, but does not require canonicalizing ${CLAUDE_PROJECT_DIR} itself. In a per-ticket worktree where CLAUDE_PROJECT_DIR is itself a symlink, report_library_dir resolves to the real path while CLAUDE_PROJECT_DIR stays as the symlink path — they compare unequal and the guard silently passes, allowing a report to land in the repo tree.
This only triggers if a user sets report_library_dir to the realpath of their symlinked worktree root — an uncommon coincidence. Consequence is a policy violation (report in the product tree), not a privilege escalation.
Recommended change to the repo-tree guard bullet: add "canonicalize ${CLAUDE_PROJECT_DIR} via realpath (or equivalent) before the comparison so symlinked worktree roots are caught correctly."
Strengths
SKILL.md is correct and internally consistent across all its sections (action router, report contract, retention mechanics, non-gating posture, recall coverage boundary, composition, boundary docs). At 196 lines and 825/1536 description chars it sits comfortably within both budget limits.
Slug derivation (lines 96–111) is sound: sha256sum/shasum -a 256 cross-platform fallback is present; SSH/HTTPS normalization strips scheme, userinfo, and scp-style colons before hashing; tr -cs 'a-z0-9' '-' reduces the base to safe alphanumeric-plus-hyphen; the quiz-me/ segment correctly fences artifacts from teach's path-keyed workspaces in the shared ${CLAUDE_PLUGIN_DATA}.
Evals (8 scenarios) give complete policy coverage: canonical trigger, default non-gating, off suppresses, retention to CLAUDE_PLUGIN_DATA, recall library-first with named coverage boundary, artifact-verification boundary handoff, above-threshold offers-not-generates, always+decline leaves nothing written. All expectations are specific and verifiable.
plugin.json is correctly structured: quiz_policy has type/title/description/default; report_library_dir has type directory with no default (preserving zero-config). Version bump 0.4.0 → 0.5.0 and CHANGELOG entry are present and consistent.
README correctly replaces the old "no userConfig" claim with a complete Configuration section documenting both keys, their defaults, and the #798 guardrail caveat.
PLUGIN-PHILOSOPHY.md correctly records quiz-me as a named exception with full rationale — the -me qualifier as a load-bearing object-pronoun distinguishing the human-test boundary. Consistent with the list's style and scope.
Security posture is clean: prompt-only skill, no hooks, no executable trust surface, no remote MCP, no new dependencies, all artifacts routed to ${CLAUDE_PLUGIN_DATA} or the userConfig override, never the product repo's tree.
Summary
All prior CRITICAL and MINOR findings across six review passes are resolved. Two SUGGESTION-level items remain open — HTML-encoding of git-sourced content in the report body, and canonicalizing both sides of the repo-tree guard comparison — neither is a blocker. Applying both would take two sentences in SKILL.md and close the remaining edge cases definitively. The skill, evals, manifest, and docs are otherwise complete and ready to merge.
| Branch
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bde4056e6f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| 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 |
There was a problem hiding this comment.
Include uncommitted work in threshold checks
When quiz_policy=above-threshold is evaluated before the completed change is committed, this range diff only compares the merge-base to HEAD, so staged/unstaged working-tree edits are invisible and a large post-work change can produce an empty stat and no quiz offer until after a commit. Include the index/working-tree diff in this threshold calculation, or state that above-threshold only applies after a committed checkpoint.
Useful? React with 👍 / 👎.
Adds the third education sibling:
/education:quiz-me, a post-work comprehension check. After a change completes it generates a self-contained HTML report (context, intuition, decisions) with a quiz at the bottom the user answers — verifying the HUMAN absorbed the work, never the artifact. Non-gating:quiz_policytunes offer cadence only and no report generates without user confirmation. Arecall <query>action answers "what did we do on " from the retained report library first (coverage bounded to quizzed work), git/tracker archaeology second. Reports are keyed on repo identity (worktree-safe), stored under${CLAUDE_PLUGIN_DATA}or the newreport_library_diruserConfig, never in the consuming repo's tree, with durable-pointer-only references and an embedded answer key so later sessions can grade.Source practice: Thariq Shihipar (Anthropic) — "Field Guide to Fable" + Peter Yang episode; canonical prompt honored verbatim.
Fresh-docs verified this session (repo CLAUDE.md mandate):
disable-model-invocationdefault false;user-invocabledefault true; name = directory.${CLAUDE_PLUGIN_DATA}persistence semantics.Gates (all exit 0): skill-quality check (0 err/0 warn, 176/500 lines, description 825/1536), changelog parity
--check-bump, evals schema (check-jsonschema), catalog parity, leaf-name check, portability lint, markdownlint, validate-plugins.Closes #799
Related
report_library_dirshares knowledgelibrary_dir's literal-path indirection problem class; adopts its scheme when it landsPLAN.md (approved plan — contract slice pruned before merge)
Education quiz-me — post-work comprehension verification
Tracker: #799 (contract locked in interview session
claude-loop-practices, 2026-07-21; retention seam added in issue follow-up comment).Brief
TLDR
educationplugin (teach = multi-session coach,explain = one-shot altitude drop): after Claude completes a change, generate an HTML
report of what was done (context, intuition, decisions) with an embedded quiz the user
answers — verifying the HUMAN absorbed the work, never the artifact.
quiz_policy: off | on-request | always | above-threshold.destination via userConfig, default a local disk library; a companion retrieval flow
answers "what did we do on ticket X" from that library first, git/ticket archaeology
second.
quiz-meis the working label, not locked — final name via the codified naming grammar.Goal
Failure mode (source: Thariq Shihipar, Anthropic — "Field Guide to Fable" talk + Peter Yang
episode): "people still glaze over the 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. Outcome: a post-work skill that quizzes the user on the
completed work (representation accountability, loop retention, and secondary late
intent-mismatch detection — a failed quiz surfaces "that's not what I intended" pre-merge),
with the report retained as a queryable record (retention-at-write replacing
retrieval-at-need).
Constraints
verification:confirmownsartifact verification; no overlap. Composition with gate sequences (e.g. session-flow
workflow) by pointer only — this skill never edits other plugins' gate lists.
CLAUDE.mddesign rules govern: fresh-docs mandate (WebFetch current skills/pluginsdoc pages before editing, cite URLs), repo-agnostic, plugin-form-safe
(
${CLAUDE_PLUGIN_ROOT}/${CLAUDE_PLUGIN_DATA}), configurable viauserConfigonly./skill-quality:checkgate passes for every touched skill (listing-budget cap,trigger-keyword preservation).
https://claude.com/blog/a-field-guide-to-claude-fable-finding-your-unknowns); markdown
fallback per project convention.
seam with a local disk library default. Same destination-config problem class as knowledge: library_dir seam accepts only literal paths — machine-specific config collides with hardcoded-path guardrail #798
(library_dir portability) — compose, don't fork a second convention.
userConfig; this changemakes that false — README configuration section must be updated in the same PR.
feat/799-education-quiz-me.Acceptance criteria
plugins/education/skills/<final-name>/whose SKILL.md triggers onpost-work comprehension requests and generates report + quiz per the canonical pattern.
quiz_policyuserConfig documentedwith the four values above;
above-thresholdkeyed to change size / blast radius.there, never in the product repo working tree.
(skill action vs documented query pattern) decided in the plan.
/planning:interview(pre-work, extracts theUSER's intent — user holds answers) and
verification:confirm(object = artifact) isstated where a consumer would look for it.
/skill-quality:checkpasses; education plugin version bump + CHANGELOG entry; READMEconfiguration section updated.
Captured assumptions
a week ago") — revisit if usage shows the quiz is skipped as ceremony.
retention default — revisit when the Obsidian-vault destination or knowledge: library_dir seam accepts only literal paths — machine-specific config collides with hardcoded-path guardrail #798's resolution
lands.
Out-of-scope
the extension point); do not design now.
verification:confirmowns it).Deferred questions
quiz-me) — arbiter: plan,before frontmatter is written.
above-thresholdmetric definition (diff size? files touched? blast-radius class?) —arbiter: plan.
arbiter: plan.
library_dirindirection — arbiter:plan, tracking knowledge: library_dir seam accepts only literal paths — machine-specific config collides with hardcoded-path guardrail #798's outcome.
Plan
Standards grounding: repo
CLAUDE.md(fresh-docs mandate, plugin design rules, branching/PR),docs/PLUGIN-PHILOSOPHY.md(naming grammar §Naming, composition-by-pointer, userConfigownership + native-first, version single-home),
docs/MIGRATION-PLAYBOOK.md(userConfig seam1, security-review trigger scope, changelog rule). Exploration evidence:
.work/education-quiz-me/EXPLORE.md(memory tier). Design sketch:design/design-resolution.md.Resolved decisions (Brief "Deferred questions")
quiz-me— grammar check (docs/PLUGIN-PHILOSOPHY.md§Naming):imperative verb phrase ✓;
quizcarries no fixed verb meaning (no collision withaudit/scan/checksemantics —check-understandingwas rejected precisely becausecheckmeans a deterministic pass/fail gate and this skill is non-gating); the-mequalifier is load-bearing — it names the object under test (the human), the skill's
defining boundary vs teach's
assessaction andverification:confirm. Composes:"/education:quiz-me on this change". Rejected: bare
quiz(under-specifies vs teach'sin-workspace quizzing),
debrief-me(loses the test/verification element).above-thresholdbar — the completed change meets ANY of: >5 files touched,recallaction in the same skill — action router with two actions:default (generate report + quiz for the change just completed) and
recall <query>(answer "what did we do on ticket X" from the retained library first, git/tracker
archaeology second, stating which source answered). Approval-round bound (2026-07-21):
recall's coverage is only work that WAS quizzed — retention-at-write, not a general
work-history engine; the skill states that boundary when answering and routes unquizzed
work to the archaeology fallback rather than implying library completeness.
${CLAUDE_PLUGIN_DATA}— artifacts land under${CLAUDE_PLUGIN_DATA}/<repo-slug>/quiz-me/reports/<date>-<change-slug>-<short-hash>.html.report_library_dir(typedirectory) userConfig overrides toward a corpus checkout;unset = plugin-data default. Guardrail-safe (no machine-specific literal needed),
zero-config, survives plugin updates; adopts knowledge: library_dir seam accepts only literal paths — machine-specific config collides with hardcoded-path guardrail #798's indirection scheme for literal-path
overrides when that lands.
<repo-slug>= repo basename +8-hex sha256 of the canonicalized
originremote URL (fallback: teach'sproject-path recipe when no remote). Deviates deliberately from teach's
path-keyed slug: this repo's own workflow runs per-ticket worktrees that are pruned
after merge, so path-keyed retention would strand every report under a dead slug and
recallfrom the main clone would find nothing — repo-identity keying makes reportsfrom all worktrees land in one library. Side benefit: quiz-me's tree never collides
with teach's path-keyed slugs in the shared per-plugin
${CLAUDE_PLUGIN_DATA}; thequiz-me/path segment adds a second fence.<details>in HTML; appendix section in markdown fallback). Grading isin-conversation same-session; a later or compacted session grades by reading the key
from the retained artifact, re-deriving from report + diff only when the key is
missing. Without this, the retention use case ("quiz me on ticket X from last week")
could not be graded at all.
zero-config behavior, below PLUGIN-PHILOSOPHY's "non-trivial" bar (knowledge's setup
skill exists for its external prerequisites, not merely for
library_dir). Recordedso the conformance audit doesn't read silence as a gap.
approval-round bound (2026-07-21): every
quiz_policyvalue governs offer cadenceonly (
offnever offers,on-requestonly when asked,alwayssuggests after eachcompleted change,
above-thresholdsuggests when the bar is met); no valueauto-generates — the report+quiz is produced only after the user accepts the offer,
and a direct invocation ("quiz me") is itself that acceptance. Offers are
model-initiated via description triggers + documented posture, explicitly best-effort
in SKILL.md. A Stop/skill-scoped hook was evaluated and rejected: it would add a code
trust surface (re-triggering the acceptance security review) and a kill-switch config
for a nicety; revisit only if usage shows offers reliably missed. Threshold judged
against
git diff --stat $(git merge-base HEAD <default-branch>)..HEADat offer time(well-defined even after commits).
quiz_policyunknown values fall back toon-request, documented in the userConfig description (claude-ops precedent).self-contained; external references restricted to durable, checkout-independent
pointers: PR/issue URLs, commit SHAs/permalinks, promoted docs reachable on the
default branch. Never memory-tier paths (
.work/…never leaves its checkout) andnever contract-slice paths (
docs/topics/…is pruned before merge) — both dangle.Ephemeral inputs (exploration/research artifacts, session context) are distilled
inline instead of linked. This is the topic-docs pointer discipline applied to the
report artifact; no new linking mechanism.
Phase 1: SKILL.md authoring [TODO]
Fresh-docs fetch first (repo
CLAUDE.mdmandate): skills page + plugins-reference page;cite URLs in the PR. Then author
plugins/education/skills/quiz-me/SKILL.md:name: quiz-me;user-invocable: true; NOdisable-model-invocation(policy
always/above-thresholdrequires model-initiated offers; description guardstriggers). Description ≤1536 chars with single-quoted 'Use when' triggers ('quiz me',
'do I understand this change', 'what did we do on', 'comprehension check', canonical
blog phrasing 'quiz at the bottom … that I must pass').
from issue); Effective configuration table (
quiz_policydefaulton-request,report_library_dirunset → plugin-data; surviving-placeholder-means-unset rule);Action router (default = generate,
recall <query>); Report contract (self-containedsingle-file HTML, inline CSS/JS, no remote fetch,
file://, synthetic/no secrets —teach
context/lessons.mdprecedent; markdown fallback; quiz at bottom per canonicalprompt; answer key embedded collapsed in the artifact per decision 6, grading
in-conversation same-session or key-read later; failed quiz surfaces possible intent
mismatch pre-merge; reference discipline per decision 9 — durable pointers only,
ephemeral inputs distilled inline); Retention mechanics (repo-identity slug per
decision 5, filename short-hash, never writes the consuming repo's tree); Non-gating
posture + policy semantics (offer-cadence-only policies, generation always
user-confirmed, best-effort offers, threshold base, unknown-value fallback per
decision 8);
recallcoverage boundary (decision 3); Boundaries ("What thisskill does NOT do": verification:confirm, planning:interview, teach
assess/exercisedisambiguation); Gotchas.
Sanity Check:
CHECK_SKILL_SKILLS_ROOT="$PWD/plugins/education/skills" bash plugins/skill-quality/scripts/check-skill.sh quiz-meexits 0 (the invocation shapescripts/check-changed-skills.shitself uses);grep -c '' plugins/education/skills/quiz-me/SKILL.md< 500.Phase 2: Manifest, config, plugin docs [TODO]
plugins/education/.claude-plugin/plugin.json: adduserConfig(quiz_policystringdefault
"on-request";report_library_dirdirectory, no default), bump version0.4.0→0.5.0, add keywords (quiz,comprehension).plugins/education/CHANGELOG.md: top-insert## [0.5.0]### Addedentry.plugins/education/README.md: skill list + sibling framing gains quiz-me; Configurationsection rewritten (drops "no userConfig", documents both keys + defaults); Requirements
unchanged (no new dependency — reuses declared Bash + coreutils).
lives in README — status quo pattern).
Sanity Check:
jq -e '.version=="0.5.0" and (.userConfig|has("quiz_policy") and has("report_library_dir"))' plugins/education/.claude-plugin/plugin.json;bash scripts/check-changelog-parity.sh --check-bump origin/mainexits 0 (--checkalone would pass without the 0.5.0 entry — it only asserts the file exists).Phase 3: Evals [TODO]
plugins/education/skills/quiz-me/evals/evals.json(skill_name: quiz-me, siblingschema shape), ~6 evals: canonical-prompt trigger produces report+quiz; default policy is
non-gating (no unsolicited block);
quiz_policy: offrespected; artifact never written torepo tree;
recallqueries library before git archaeology; boundary handoff (artifactverification request routes to verification:confirm, not quizzed).
Sanity Check: validate
plugins/education/skills/quiz-me/evals/evals.jsonagainstplugins/skill-quality/reference/evals.schema.jsonwith one of the validatorsskill-quality:checkitself accepts —check-jsonschema(Python/pipx),ajv, orpython -m jsonschema— exit 0 (check-jsonschema is NOT an npm package;npxcannot run it).Phase 4: Catalog + marketplace [TODO]
node scripts/generate-catalog.mjs— regenerate rootREADME.mdcatalog block..claude-plugin/marketplace.json: addquiz/comprehensionto the education entry'stags(discovery-tag change; the explain exemplar did the same).Sanity Check: after editing marketplace tags, run
node scripts/generate-catalog.mjsthengit diff --exit-code README.mdexits 0 (catalog is a committed regeneration, not drift);jq -e '.plugins[]|select(.name=="education").tags|index("quiz")' .claude-plugin/marketplace.jsonexits 0.Phase 5: Gate sweep [TODO]
Run the CI-mirroring local gates over the diff:
scripts/check-changed-skills.sh origin/main,scripts/check-skill-leaf-names.sh --check(registerquiz-meif a cross-plugincollision is reported — none known),
scripts/check-skill-portability.sh, markdownlint(
npx --no-install markdownlint-cli2),scripts/validate-plugins.sh.Sanity Check: every listed script exits 0.
Blast radius
LOW — additive prompt-only skill inside one plugin; no cross-plugin edits, no hooks, no
scripts, no network, no new trust surface (no security-review re-trigger per
docs/MIGRATION-PLAYBOOK.md); worst regression is a bad new skill that consumers simplydon't invoke, plus a README/manifest documentation error.
Stress-test summary
Fresh-context plan reviewer (Step 3): 1 CRITICAL + 6 IMPORTANT + 4 SUGGESTION, all
verified and folded in — corrected the check-skill.sh invocation (needs
CHECK_SKILL_SKILLS_ROOT), changelog gate (--check-bump origin/main), evals validator(check-jsonschema is Python, not npm); resolved the worktree-pruned-slug retention trap
(decision 5), later-session answer-key grading (decision 6), setup-skill silence
(decision 7), best-effort offer semantics + threshold diff base + unknown-value fallback
(decision 8), plugin-data coexistence with teach and filename collisions (decisions 4-5).
Formal
/devils-advocateskipped: blast radius LOW, no trigger matched.Execution shape
Fully sequential, single implementation lane — all five phases write inside
plugins/education/+ two root catalog files; file overlap and the version/CHANGELOGcoupling make parallel waves pointless. Routing: one scope-fenced implementation subagent
executes Phases 1–5 (orchestrator never edits source, per the work-items dispatch
posture); main session verifies returns and owns PLAN.md updates.
Open questions
None blocking; user-overridable decisions listed under "Decisions made" in the approval
presentation.
Handoff to implementation
User-approval gates
quiz-me— hard to reverse post-publish (rename sweep); flagged for explicitconfirmation at plan approval.
Execution shape ([EXEC-SHAPE] tagged)
plugins/education/**,README.md(regen only),.claude-plugin/marketplace.json(tags only). FORBIDDEN: everything else,
docs/topics/**(PLAN.md ismain-session-owned), other plugins.
disable-model-invocationon quiz-me (policy-driven offers needmodel invocation; explain precedent).
above-thresholdnumbers (>5 files / >200 LOC / blast ≥ HIGH).adopt it for
report_library_dirliteral-path overrides; otherwise document literalpaths as guardrail-constrained (same caveat knowledge README carries).
Mechanical work
Commit per phase or logical pair (Conventional Commits,
feat(education): …PR title);squash merge; PR body:
Closes #799,## Related(#798, knowledge-corpus digest PR 3),attribution trailer + session link; PLAN.md phase tags advance with each phase's commit;
close-out (PR time): paste PLAN.md into PR
<details>, prunedocs/topics/education-quiz-me/before merge per /planning:plan close-out.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Y4RMR3G1rHAdNjxTqenVJL