Claude Code skills

The verify gate's PASS/FAIL table from this page's own run: git state, typecheck, tests, screenshots

Role

Design engineer

Since

2026

Source

studiokyoung/claude-skills

Claude Code skills

The toolbox

Claude Code skills I run on my own work, and the router that fires them. Each skill is a working method written out far enough that an agent can run it; the router is what makes them fire without being called. Everything here is public at github.com/studiokyoung/claude-skills, and every field claim below comes from the run records the system keeps about itself.

/explain-diff — why does this hunk exist?

The approval gate

An approval gate for AI-written diffs: every change traced to the evidence that asked for it, every part given a verdict, nothing edited until you say which rows to apply. It has its own write-up on this site.

In the field: the rule that earns its keep is no evidence found. A change nothing in the session asked for is usually the change I would have regretted.

/verify — the gate that will not nod

The pre-commit gate

Four checks in one keystroke: git state, typecheck, tests, and viewport tiles at 390, 768 and 1440. It never says verified for a step that did not run, and on a mobile app it drives the repo's own maestro flows on a booted simulator instead of taking browser screenshots.

In the field: its first week of run records promoted two of its own upgrades. A real session improvised a maestro gate, the records showed it caught what static shots cannot, and it became part of the skill the same day. Then its own annotation confessed the fixed tiles had missed a changed section, and the next day it gained an anchored tile that pins one.

THE GATE, SAYING NO AND THEN YES
$ git commit -m "feat(archive): the toolbox page"
verify gate: no passing /verify for this exact tree
(tree changed since 2026-09-04T14:58:31-04:00).
Run the verify skill first, then commit.

$ /verify /archive/claude-skills
gate         status   detail
git state    ✅PASS   migration/nextjs-react
typecheck    ✅PASS   tsc --noEmit clean
tests        ⏭️SKIP   no runner in this repo
screenshots  ✅PASS   39 tiles · anchors hit

$ git commit -m "feat(archive): the toolbox page"
[migration/nextjs-react d169b4c] the gate lets it through

/reuse-scout — before building anything

The pre-flight scan

A scan of what the repo already has for each capability a plan needs: which twin is canonical, what to reuse, where new code is actually justified.

In the field: scanning a two-week-old feature surface it found two hand-rolled sheets that a canonical component already covered, and a cost formula duplicated across screens. The sheets were replaced and the formula extracted the same afternoon.

THE REMINDER, AS IT LANDS
> build a settings page with a new modal component

[skill-router] This prompt asks to build a component/hook/util/feature.
Run /reuse-scout first: scan what the repo already has, decide what to
reuse, then build against the canonical twin.

/skill-router — the operator console

The console

Says whether the router is installed, which repos the commit gate covers, what the log decided and why a commit was denied, and installs or removes the whole thing behind an explicit yes. Read-only otherwise: it reports what a command printed in this run, never what it remembers.

/skill-review — the Friday ritual

The loop

Once a week the run records turn into edits: which reminder converted, which rule fired for nothing, which gate stood in the way, what a finished run missed. It proves the router still fires, aggregates the week into one deterministic report, and proposes changes to the skills; it never edits them on its own.

In the field: the first two reviews filed the weeks into my knowledge graph and queued the upgrades the records argued for.

THE FRIDAY REPORT, CONDENSED
# skill router · weekly review
window 2.8 days · 28 records

verify      run 3 · safe 2 · not-safe 1 · gate allow 2 / deny 0
annotation  missed: default tile sampling landed on unchanged
            regions; none of the 9 tiles showed the edited section

Candidates
- pattern-unused · reuse-scout-prompt #1 · 0 matches in 2 windows

The router underneath

Four hooks

Four hooks in Claude Code settings: a reminder when a prompt looks like building, a commit gate on web repos that only passes a tree /verify actually verified, a run record for every invocation, and a self-check when a session starts or resumes. Every hook fails open: if the router breaks, the session works as if the router never existed, and the daily alarm says so instead of me noticing weeks later.

WHAT THE LOG REMEMBERS
2026-09-04 18:38  commit  gate  portfolio  deny   tree changed since 14:34
2026-09-04 18:39  commit  gate  portfolio  allow  verified 14:39
2026-09-04 19:14  commit  gate  portfolio  allow  verified 15:14

The gate is not ceremonial. In its first field week it allowed seven commits it had proof for and denied three it did not, including two of my own.

ONE RUN, ONE RECORD
{"type":"run","skill":"verify","version":"1.2.2",
 "repo":"portfolio","verdict":"safe",
 "gates":{"git":"PASS","typecheck":"PASS",
          "tests":"SKIP","screenshots":"PASS"},
 "tiles":39,"caught":[]}

How each one works

The deep layer

The cards above say what each tool is for. This is how they actually do it, in plain terms, with the moving parts named.

verify, under the hood

The fingerprint

Four steps, in order. Detect the stack from the repo root before running anything: cheap checks, no guessing. Run every gate that detection marked applicable, each with a hard timeout so nothing hangs the session. Print one PASS/FAIL table, one row per gate. Then mark the tree and record the run, always, last. A gate whose signal is absent is a skip carrying its reason, not a failure; a gate whose signal is present must run. Those last two commands are what the router and the run ledger see, and skipping them is the same failure as printing a pass the run did not earn.

A fingerprint is a photo of the exact tree that passed: the commit it sits on, every tracked change hashed, big and untracked files stamped by size and time. The marker holds that photo; the gate takes a new one at commit time and compares. One more edit means a different photo, which means one more /verify.

So the fingerprint is content-based for tracked files, mode aware, a chmod on an already dirty file moves it, and staging neutral: running /verify, then git add, then committing stays valid, while any actual edit in between does not. It fails closed. If any git call fails, the fingerprint is unavailable and the gate denies rather than passing on a hash computed over less than the whole tree.

On a mobile app the UI gate is maestro rather than browser tiles, and the condition for it is mechanical: react-native or expo in the dependencies, and a .maestro directory with flows in it. Both present means the gate must run, and an app with no flows gets a skip that says out loud the app shipped with no UI gate at all.

Node 22 ESM, raw git plumbing for the fingerprint, the repo's own playwright for tiles, maestro on a booted simulator for native apps. No daemon: every check is a fresh process.

THE COMMIT GATE, DECIDING
git commitcommand parser · cd, -C, heredoc aware
repo in a gated group?noallow · out of scope
yes
SKIP_VERIFY=1?yesallow · logged override
no
docs only?yesallow · docs only
no
marker fingerprint matches the tree?yesallow · verified
no
deny · run /verify

the router, under the hood

The rule table

Four hooks on four events. At UserPromptSubmit the prompt rules are matched against the first 4000 characters of the prompt, and a hit injects one line into the model's context: you do not see it, the model does. At PreToolUse the gate reads the Bash command, recomputes the fingerprint and compares, and a difference is denied with the reason going back to the model. At PostToolUse a Skill call is classified by how it was triggered, typed by you, suggested by the router, or picked by the model, and written down. At SessionStart the router probes itself against temp directories and says nothing if it still works.

The scripts hardcode no skill name, no repo name and no regex. Policy is one JSON rule table, and the scripts read it and execute: a row names the skill, the event, the repositories it covers as a group or a list, the mode, block or remind, and the patterns that match. Putting one more skill under the router is one more row in that table. It ships with its repository groups empty, so the gate covers nothing until someone fills them in, and a gitignored local file carries the machine's own repo names; the two are merged at load.

Every hook exits 0 no matter what happens inside it, and a failure inside one produces no output at all, so the worst a router bug can do is switch the router off. A broken policy file logs one line and lets the call through: fail open, but not fail silent.

Every failure direction is the same direction: off. A broken rule table, a missing buffer, a git that will not answer: the session keeps working as if the router was never installed, and the health check says so out loud at the next session start.

FOUR HOOKS, ONE TABLE
a promptUserPromptSubmit · on-prompt
a Bash or Write callPreToolUse · pre-tool
a Skill runPostToolUse · post-skill
a session startSessionStart · selfcheck
all four read
the rule table · one JSON file
remind · one line of context
allow or deny
run record
health record
records · one jsonl file per skill

Why a skill went quiet is a log question: six columns of ts, event, rule, repo, decision and why, rotated to a second file at 1 MB, so a window that reaches back far enough needs both.

the records and the Friday loop

The ratchet

Five record types, plus the router's own health line: one JSON object per line, appended and never edited in place.

remind
One line per reminder actually delivered, into the buffer of the skill it asked for, with which pattern matched and 160 characters of the prompt.
invoke
A tracked skill ran, and how it was triggered: typed by you, suggested by the router, or picked by the model.
gate
One commit decision: allow or deny, the reason, how many paths the commit carried, and which passing verify was accepted.
run
What the skill appends when it finishes, carrying its version. No hook writes one, so an invoke with no run beside it is what a skill that quit before finishing looks like.
annotation
What a later debrief says that run missed, pointing at it by id. Nothing is rewritten.
health
One line per session start or resume, so a week with no records can be told apart from a week the router was off.

remind, invoke, gate and run all carry the session and the prompt id, which is what makes separate buffers joinable: a remind and a later invoke of the same skill in the same session is a reminder that converted. An annotation joins to its run by reference instead, and a health line belongs to the router rather than to a session.

Detection is automatic; reinforcement is a decision, made once a week while looking at numbers. The aggregation is the deterministic half: the window runs from the last timestamp in the review watermark, else the last seven days, and the watermark moves only once the review is actually finished. Its candidates are threshold crossings, not opinions: a rule that reminded three times and never converted, a session denied three times with no run behind it, a pattern that matched nothing all window.

The loop is a ratchet: records this week, a deterministic report on Friday, a human yes, an edit to the skill, and next week's records measure whether the edit earned its keep. Twice already the records argued for an upgrade and won.

THE REINFORCEMENT LOOP
skills runrecords · jsonlthe Friday report
human judgmentproposed editsversioned skills

back to skills run

reuse-scout and explain-diff, briefly

The rest of the toolbox

reuse-scout breaks the ask into capabilities, one reusable behavior each, then scans the roots that exist, components, hooks, lib, utils, modules, app, plus the design tokens, by name and by behavior. Every file and line it prints has to be a line a grep actually printed in that run: a cited location that does not exist is worse than a missed match. Where a capability has two or more real implementations it names which one is canonical, which is what stops the builder cloning the wrong twin, or minting a new one beside them.

The manifest is the product, one row per capability with a verdict of reuse, partial, or genuinely new, and its run record carries the counts: capabilities, reuse, partial, new, twins found, and one clause naming each reuse the scan surfaced. A scan that found nothing and a scan that never finished do not look the same.

explain-diff is the approval gate for a diff: two axes in one table, why each change exists, traced to the evidence that asked for it rather than guessed, and whether it has earned its place. The mechanism has its own write-up, so it is not repeated here.

/plugin marketplace add studiokyoung/claude-skills/plugin install kyoung@studiokyoung

or symlink skills/explain-diff into ~/.claude/skills to keep the bare /explain-diff

node ~/claude-skills/router/install.mjs

adds the four hooks to settings.json, with a backup first; removable with --uninstall

Works