Claude Code skills and subagents for architecture

A skill that scaffolds modules inside your rules and a read-only subagent that reviews the drift lint misses. File formats, examples, when to use which.

MC
Martin Coll

Use a skill to scaffold new modules inside your rules and a read-only subagent to review the drift that lint can't see. Lint and hooks catch the architecture mistakes you can express as a rule: "domain/ never imports infrastructure/". Two problems remain:

Code for this guide: https://github.com/KonixDev/claude-code-guardrails — the open-source (MIT) claude-code-guardrails kit.

  1. Starting right. A new module created from scratch by an agent tends to copy whatever the nearest file looks like, which may not be the pattern you want.
  2. Drift a linter can't see. Business logic in a controller, a port declared in the wrong layer, a // eslint-disable added to get green — all of it passes lint.

Claude Code has a tool for each: skills for repeatable procedures and subagents for focused, isolated work like review.

Skills vs subagents in one table

SkillSubagent
What it isInstructions (and optional files) Claude loads when relevant or when you type /nameA separate Claude instance with its own prompt, tools and context window
Lives in.claude/skills/<name>/SKILL.md.claude/agents/<name>.md
Runs inYour current conversation (or a subagent, with context: fork)Its own context; returns a summary
Good forProcedures: "scaffold a module", "cut a release"Focused tasks that would flood your context: review, research

A skill that scaffolds a module inside the rules

.claude/skills/new-module/SKILL.md:

---
name: new-module
description: Scaffold a new bounded context under src/modules/<name> with domain, application, infrastructure and interfaces layers that the architecture lint already guards. Use when a feature needs its own module.
argument-hint: "[module-name]"
---

Create a new bounded context named `$ARGUMENTS` (kebab-case; ask if missing).

1. Read AGENTS.md for the layer rules.
2. Create under src/modules/$ARGUMENTS/:
   - domain/<entity>.ts — the entity with invariants in the constructor,
     plus its repository interface. No imports from outside domain/.
   - application/<use-case>.ts — one use case that receives the repository
     interface as a parameter, never a concrete class.
   - infrastructure/<entity>-repository.ts — the adapter (in-memory is fine
     until the real store exists).
   - interfaces/http.ts — wires the adapter into the use case.
3. If the module needs another module, declare a port in its own
   application/ and `import type` the other module's shapes.
4. Run `npm run -s guardrails` and fix anything it reports.
5. Summarize what you created as a short tree.

Details from the Claude Code docs that matter here:

  • description is what Claude uses to decide when to load the skill on its own, so write it as a trigger ("Use when a feature needs its own module").
  • $ARGUMENTS expands to whatever you typed after /new-module.
  • Add disable-model-invocation: true if you only ever want to run it by hand.
  • Step 4 tells Claude to run the same checks the hooks and CI run before it reports the module as created.

A subagent that reviews what lint can't see

.claude/agents/architecture-reviewer.md:

---
name: architecture-reviewer
description: Reviews a diff against the layered architecture in AGENTS.md (inward dependencies, framework-free core, type-only cross-module imports). Use before reporting a non-trivial change as done.
tools: Read, Grep, Glob, Bash
---

You review code changes for architecture drift. You do not write features.

1. Read AGENTS.md for the rules.
2. Get the change set: `git diff --name-only main...HEAD` (this branch),
   `git diff --name-only` (uncommitted) and untracked files.
3. Run `npm run -s guardrails`. Report any failure verbatim.
4. Then look for what lint cannot see:
   - business rules in interfaces/ or infrastructure/ instead of
     domain/ or application/;
   - a port declared in infrastructure/ instead of application/;
   - one module reaching into another's data store directly;
   - eslint-disable, @ts-ignore or `any` added to get past a guardrail.
5. Report: file:line — rule — why it matters — the fix. Do not edit files.

Why a subagent and not a paragraph in CLAUDE.md:

  • Fresh eyes. It starts from the diff and the rules, not from the reasoning that produced the change, so it doesn't inherit the main session's blind spots.
  • Context isolation. Reading a large diff and the rules happens in its own context window; your session only gets the findings.
  • Narrow tools. tools: Read, Grep, Glob, Bash and "Do not edit files" keep a reviewer a reviewer. (Bash is there to run the guardrails; drop it if you prefer a strictly read-only reviewer.)

Ask for it explicitly ("have the architecture-reviewer check this"), or tell Claude in CLAUDE.md to use it before reporting a non-trivial change.

Where they fit

The skill and the subagent sit on top of lint and two hooks: lint rejects the wrong import, the hooks put that error in front of the agent while it works (Claude Code hooks to enforce your architecture), the skill starts new code in the right shape, and the subagent catches what none of them can express as a rule. The rules file itself is covered in a CLAUDE.md example for a SaaS codebase.

Get the files

The skill, the subagent, both hooks, the ESLint config and AGENTS.md are in claude-code-guardrails (MIT). Copy templates/.claude/ into your repo and adjust the paths.

FAQ

Can a subagent call another subagent? Yes: by default up to three layers below the main conversation (CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH changes it). A reviewer rarely needs to — one subagent, one job, with the main session orchestrating, is easier to reason about.

Will Claude use the skill automatically? It can, based on the skill's description. If you want it only on demand, set disable-model-invocation: true and run it as /new-module billing.

Project or user scope? Project (.claude/skills/, .claude/agents/), committed to the repo. These encode your codebase's rules, so the whole team — and every agent session — should get the same ones.


useDeploy ships AGENTS.md, the hooks and the ESLint boundaries on a full SaaS base: auth, organizations, billing and eleven modules built to these rules. See the modules.