Claude Code harness setup: 3 skills, 30 minutes
Before this session: zero project-scoped permissions, recurring approval prompts on git and npm commands, no session-start visibility into the open-work backlog, and five allow rules in user-global settings that belonged to a completely different project. One session with three skills fixed most of it — and each skill surfaced something that wasn’t obvious going in. The skills: /fewer-permission-prompts scanned 15 transcripts and found one strict-survivor pattern from ~50 candidates, /update-config relocated the five misplaced allow rules and added a SessionStart hook that surfaces the open-work checklist as a systemMessage in the UI, and /schedule shipped two routines and rejected a third because remote agents can’t see gitignored directories. Total elapsed: 30 minutes. The post documents the receipts and three non-obvious edges that don’t live in any skill’s documentation.
/fewer-permission-prompts: 15 transcripts, ~50 patterns, 1 survivor
The skill scans session transcript JSONLs in ~/.claude/projects/<project>/, extracts Bash command frequencies, and filters them against Claude Code’s built-in auto-allow list. Commands that appear 3+ times and aren’t already covered are candidates for your project .claude/settings.json.
15 transcripts scanned. ~50 distinct command patterns extracted. 1 strict survivor.
That survivor was Bash(npm run build) — 14 hits, not auto-allowed, safe to add. I added Bash(npm run dev) and Bash(npm run preview) proactively even though neither had hit the threshold — both are documented npm scripts, no side effects, and local dev work hadn’t started yet but would.
The gap between expectation and reality: Claude Code’s built-in auto-allow list covers more than most people assume. git status, git diff, git log, cat, most read-only filesystem operations — all auto-allowed already. The commands I was actually being prompted on (gh api, git push, node scripts/*) either carry mutation risk or are flagged by the skill as arbitrary-code-execution candidates. Bash(gh api *) would authorize all HTTP methods, not just GETs. No good narrow pattern fits.
The honest deliverable from this skill on an active project: you’re probably already covered on what matters, and you’ll add 1–3 lines.
The edge case the skill missed: the skill says to write to .claude/settings.json (committed to the repo). I wanted .claude/settings.local.json (gitignored — no reason to commit three npm commands). But settings.local.json wasn’t in .gitignore yet. The skill doesn’t check for this; I had to add the entry manually before writing the file.
/update-config: 5 misplaced rules, 1 missing hook
Two problems going in:
-
User-global
~/.claude/settings.jsonhad 5 allow rules for a different project — gcloud deploy commands from an unrelated bot project that had been set globally instead of in that project’s local settings. They applied to every project on the machine. -
The open-work list in
CLAUDE.local.mdwasn’t visible at session start. Eight active items; none surfaced automatically when opening a session.
The skill’s core instruction — READ before WRITE, MERGE not REPLACE — earned its keep immediately. The existing ~/.claude/settings.json had 10 deny rules and 2 hooks. Writing blind would have lost all of them.
Fix 1 — stray rules: appended the 5 allow rules to the bot project’s .claude/settings.local.json (now 60 rules, all gcloud-related and scoped correctly). User-level allow: 5 → 0.
Fix 2 — session-start hook: created .claude/hooks/session-start-checklist.mjs — 18 lines. Reads CLAUDE.local.md, regex-extracts the ## Open work section, emits {"systemMessage": ...} JSON. Registered as a SessionStart hook in .claude/settings.json.
The systemMessage vs additionalContext distinction: both can carry content from a SessionStart hook. systemMessage shows it to the human in the UI — the checklist appears as a visible message when you open a session. additionalContext injects it into the model’s context silently. For a checklist you want to read yourself, systemMessage is right. The skill’s documentation mentions both; which to use isn’t obvious until you’ve seen them in practice.
One regex edge case during pipe-testing: the initial \n## pattern matched ambiguously at end-of-file. Tightened to (?=\n## |\n*$). Pipe-test to confirm valid JSON before registering — the skill’s instructions include this step and it caught a real bug.
/schedule: 2 routines created, 1 declined
Three routines requested:
- +7 days: check custom domain status and Search Console setup readiness
- +14 days: audit whether Haines and obra wiki entries still have TODO Receipts
- Weekly recurring: run
/skill-evolveagainstreceipts-drafts/
Two created, one declined with explanation.
Routine #1 (run once, +7 days): web-only checks on whether agentcookbooks.com has resolved and Search Console is ready to verify. No repo access needed.
Routine #2 (run once, +14 days): repo read — scan wiki entries for TODO markers in Receipts sections.
Routine #3 declined. The remote agent runs against the GitHub checkout of the repo. receipts-drafts/ is in .gitignore. The entire purpose of /skill-evolve on this project is to read that directory — real session notes captured locally as skills run. The remote agent would clone the repo, find no receipts-drafts/ directory, and have nothing to do.
Nowhere in the /schedule skill documentation does it say this. The constraint is only discoverable by cross-referencing .gitignore.
Remote agents = GitHub repo state. Not your local working tree, not gitignored directories, not uncommitted files. Any routine that depends on local-only data needs to be redesigned before it can be scheduled — either commit the data or restructure the workflow so the remote agent can source what it needs from somewhere public.
Both create calls batched in one turn to avoid a second cache-miss round-trip.
What each skill actually delivered
| Skill | Output | The non-obvious thing |
|---|---|---|
/fewer-permission-prompts | 1 survivor from ~50 patterns | Claude Code’s auto-allow list already covers most read-only commands |
/update-config | 5 rules relocated, 1 hook added | systemMessage shows to human; additionalContext injects silently |
/schedule | 2 routines shipped, 1 rejected | Remote agents can’t see gitignored directories |
All three took about 30 minutes. The harness is cleaner in each case. The useful output in each case was the thing the skill found that wasn’t obvious going in — not the mechanical output, but the edge case that the documentation doesn’t mention.
Subscribe to RSS for the rest.