TL;DR. Three primitives compose the Claude Code harness. Subagents give you isolation — fresh context, scoped tools, optional cheaper model [1]. Hooks give you determinism — shell/HTTP/MCP/prompt/agent callbacks fired at ~30 lifecycle events, with
PreToolUsethe only one that can block [2]. Settings, permissions, and skills are the substrate the model runs on [3][4]. Teach the audience to ask: is this a context problem, a determinism problem, or a defaults problem? — that’s how they pick the layer.
1. Subagents — isolation
A subagent runs in its own context window with its own system prompt, tool allowlist, and model [1]. Reach for one when a side task would flood the main context (long search, log scrape, doc fetch) or when you keep re-spawning the same kind of worker. Built-ins: Explore, Plan, general-purpose. Custom subagents live in .claude/agents/*.md and Claude picks them by their description field. Cost lever: route cheap work to Haiku via model: in frontmatter [1]. The community has ~100 ready-made specs at VoltAgent/awesome-claude-code-subagents ⭐ 20k (May 2026) [5] — good to crib from.
2. Hooks — determinism around the model
Hooks turn “best practice” into enforced rules. They are command/HTTP/MCP/prompt/agent callbacks fired on lifecycle events: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop, SubagentStop, PreCompact, FileChanged, plus ~20 more [2]. Only PreToolUse blocks — exit code 2 stops the tool call and shows stderr to Claude [2]. Configured in settings.json under hooks.<event> with a tool-name matcher and an optional if permission rule (e.g. Bash(rm *)). Canonical uses: block rm -rf, run a linter after every Edit, inject context at SessionStart.
3. The rest — settings, permissions, skills
settings.json cascades across five tiers (managed > CLI > local > project > user); permission allow/ask/deny arrays merge across tiers, other keys override [3]. Permission modes (default, acceptEdits, plan, auto, bypassPermissions) set the session-wide posture [3]. Skills are the model-invokable cousin of subagents: a SKILL.md with frontmatter that Claude loads when relevant or you call as /skill-name; custom commands are now skills under the hood [4]. Skills with context: fork run inside a subagent — that’s the bridge: skill = what to do, subagent = where it runs [4]. The community frame for the whole layered stack is “harness engineering” — turning Claude from a capable assistant into a team member that already knows the project’s conventions [6].
Suggested 60–90 min demo flow
| # | Demo | Reinforces |
|---|---|---|
| 1 | Create .claude/agents/db-explorer.md with tools: Read, Grep and model: claude-haiku-4-5; ask main session a delegable task. |
Subagent isolation + cost routing [1] |
| 2 | Add a PreToolUse hook on Bash with if: "Bash(rm -rf *)" that exits 2; try to delete a file. |
Hook as blocking gate [2] |
| 3 | Write .claude/skills/commit/SKILL.md with allowed-tools: Bash(git *) and disable-model-invocation: true; invoke /commit. |
Skills, pre-approved tools, user-only invocation [4] |
| 4 | Drop a project-scope deny rule into .claude/settings.json; show it overriding a user-scope allow. |
Settings cascade and permission merge [3] |
Talking points to land
- The three layers are orthogonal: a hook can fire on a subagent’s tool call; a skill can run inside a subagent via
context: fork[4]. - Hooks are the only harness layer that runs deterministically — everything else still routes through the model’s judgement [2].
- Permission merges are subtle:
denyfrom any tier wins;allowfrom a higher tier doesn’t override a lower-tierdeny[3]. - Subagents save tokens; skills save tokens and keystrokes; hooks save trust [1][2][4].