---
name: claude-code-architect-designer
description: >
  Decides which Claude Code extension resolves a scenario — auto-invocable skill,
  manually-invoked skill, subagent, rule in `rules/`, `CLAUDE.md` section, MCP server
  connecting to an external system, lifecycle hook, or `permissions` rule — and writes
  the files only after approval. Interviews first, proposes scored options with sources,
  writes afterward. Explicit invocation only.
argument-hint: "[scenario, use case, or problem in one sentence]"
disable-model-invocation: true
allowed-tools: Read, Write, Edit, Glob, Grep, AskUserQuestion, Bash(find:*), Bash(ls:*), Bash(claude plugin validate:*), Bash(java:*)
---

## Current inventory

Skills: !`ls "${CLAUDE_PROJECT_DIR:-.}/.claude/skills"`

Agents: !`ls "${CLAUDE_PROJECT_DIR:-.}/.claude/agents"`

Rules: !`ls "${CLAUDE_PROJECT_DIR:-.}/.claude/rules"`

Decision records (the latest is the most recent): !`ls "${CLAUDE_PROJECT_DIR:-.}/.claude/decisions"`

## Scenario

$ARGUMENTS

---

# Claude Code Architect Designer

Decides **which of the eight forms** of extension resolves the scenario, and writes it.
The eight:

| # | Form | File |
|---|---|---|
| 1 | Auto-invocable skill | `.claude/skills/<name>/SKILL.md` |
| 2 | Manually-invoked skill (`/name`) | same, with `disable-model-invocation: true` |
| 3 | Subagent | `.claude/agents/<name>.md` |
| 4 | Rule | `.claude/rules/<name>.md` |
| 5 | `CLAUDE.md` section | root `CLAUDE.md` |
| 6a | MCP server, shared | `.mcp.json` |
| 6b | MCP server, one agent only | `mcpServers:` in that agent's frontmatter |
| 7a | Hook for the whole session | `hooks` block of `.claude/settings.json` |
| 7b | Hook only while one skill or agent runs | `hooks:` in that file's frontmatter |
| 7c | The executable behind 7a/7b | new mode in `.claude/hooks/ArchHook.java` |
| 8 | Hard prohibition or standing permission | `permissions.deny` / `permissions.allow` |

6b isn't a fourth reason to write an agent — it's reason 2 of § 5 of the decision matrix
(restrict tools) applied to an external connection instead of a built-in one. An agent
only exists in the first place per the three reasons already in `references/decision-matrix.md`
§ 5; 6b just answers which tools it gets once that's settled.

7c is not a trigger — it's what 7a or 7b invokes, and it exists **only** when no mode the
hook already has covers the check. Same relationship 6b has with the agent that carries
it: the registration decides when it runs, the mode decides what it does. A 7a that
reuses `check`, `format`, `schema`, `tests`, `guard`, `audit`, or `compose` is the common
case and writes no Java at all.

Forms 7 and 8 are the **guarantee** side of § 1 of the decision matrix: they execute
regardless of what the model decides. That makes them the answer whenever the rule must
always hold — invariant 6 of `@CLAUDE.md` — and it makes them the most expensive thing on
this list to get wrong, because a hook that blocks fires on every matching event for
everyone, including when it's mistaken.

A legitimate ninth answer, and the cheapest one: **create nothing**. A piece that already
covers the scenario exists, a CLI already solves it (§ 2.1 of the decision matrix), or an
existing hook mode already runs the check and only needs a registration.

**Entry rule: no proposal without an interview.** Classifying from one sentence produces
the wrong piece, and the wrong piece costs more than no piece at all — it stays in
context every session, or it never fires.

## Why this is a skill and not a rule

This is a multi-step procedure — interview, classify, propose, write — hence a skill. As
a rule it would break invariant 1 of `@CLAUDE.md`: a rule about when to create skills and
agents would have to mention skills and agents, and `rules/` is a leaf.

## Procedure

### Phase 1 · Interview

Read `references/decision-matrix.md` before asking. Use `AskUserQuestion` — at most 4
questions per call, so 2 to 3 calls. **Don't proceed with a missing answer**: each axis
below eliminates candidate forms, and an unanswered axis leaves the decision guessing.

| # | Axis | What it decides |
|---|---|---|
| 1 | Concrete symptom — what error repeats, what prompt gets pasted again | Whether there's a case, or it's anticipation |
| 2 | Trigger — `/command`, model decision, touching a file, runtime event, or reaching an external system | Forms 1 · 2 · 4 · 6 · out of scope |
| 3 | Frequency — every session, weekly, rare | Always loaded vs on demand |
| 4 | Territory — which file globs, or none | `paths` in form 4; `paths` in form 1 |
| 5 | Nature — declarative fact or sequence of steps | Forms 4/5 vs 1/2/3 |
| 6 | Isolation — verbose output, tools to restrict, different model | Form 3, and only it |
| 7 | Mandatoriness — can it fail sometimes, or is it build/security/compliance | Forms 7 · 8 vs everything above |
| 8 | Destination — this repo only, also the generated project, or both | The `export` block of `@.claude/schemas/extensions.json` |
| 9 | Integration — what it reads, what it writes, which existing piece it collides with | Ownership conflict |
| 10 | Cost of getting it wrong | minutes or days | Weight in the score |
| 11 | Does a CLI already solve it (`gh`, `psql`, `aws`, `kubectl`, `sentry-cli`)? | Eliminates Form 6 before it's even considered — decision matrix § 2.1 |
| 12 | Credential shape — OAuth, static token, dynamic header script, or none; read-only or read/write | Form 6a vs 6b vs `permissions.deny`; whether `oauth`/`headersHelper` is needed |
| 13 | Destination — this repo's `.mcp.json` only, the generated project's template only, or both | Which file(s) Form 6a writes; propagation in Phase 4 step 8 |
| 14 | Lifecycle event — what exactly has to have just happened: a tool call, a prompt, the end of a turn, session start | The event key of Form 7, and whether a `matcher` is even read there — `references/hook-events.md` |
| 15 | Reaction — observe and report, add context, or block | Exit code, and Form 7 vs Form 8: a call that must **never** happen is `permissions.deny`, cheaper than a hook that spawns a process to refuse it |
| 16 | Existing mode — does `ArchHook.java` already run this check | Form 7a alone vs 7a + 7c. `java .claude/hooks/ArchHook.java doctor` lists the modes in use |

Axis 9 is checked against the inventory injected at the top, not from memory. Two pieces
writing to the same paths is an ownership bug, not a style decision.

Axes 11-13 only apply when axis 2 (trigger) names an external system — Jira, a database,
GitHub, Figma, anything reachable only through its own API. Skip them otherwise; asking
about credentials for a scenario that isn't MCP-shaped just burns a question.

Axes 14-16 only apply when axis 7 (mandatoriness) answered that the rule cannot be
allowed to fail. Skip them otherwise — everything above the line in § 1 of the decision
matrix is persuasion, and asking which lifecycle event a skill fires on is a category
error. Axis 8 (destination) covers Form 7 unchanged: the generated project's hooks live
in `project-bootstrap/templates/settings.json.example`, and a mode in `ArchHook.java`
travels there on its own, since the `export` mode copies the whole file.

### Phase 2 · Classify

Apply the decision table in `references/decision-matrix.md` (§ 2; § 2.1 whenever axis 2
named an external system; § 2.2 whenever axis 7 answered that the rule cannot fail). Then
run the eleven invariants of `@CLAUDE.md` as a veto — the most commonly violated are 1 (a
rule that mentions a skill), 5 (an agent without one of the three reasons), 6 (prose
where a guarantee was available, and its mirror: a hook for something the model gets
right on its own), and, for MCP, 11 (a literal secret in `.mcp.json`). A proposal that
fails an invariant **is not presented as viable**: it appears with the score it deserves
and the reason for rejection.

### Phase 3 · Propose — write nothing

Two to four options, ordered by score, always including the "create nothing" hypothesis
when it is defensible. Each in this form, in this order:

1. **Title** — proposed filename, in kebab-case
2. **Motivator** — the interview axis that justifies it
3. **Pros**
4. **Cons** — includes the invariant it strains, if any
5. **Score 0-10** — rubric in `references/decision-matrix.md`, § Rubric
6. **Visual** — file tree or ASCII graph of who calls whom

And at the end, a **references** table: for each decision, the concrete source
(`@claude-help.md` § N, `@CLAUDE.md` invariant N, or the repo file that serves as
precedent). A claim about the runtime without a source is decoration — cut it.

### Phase 3.5 · Save the decision draft

What Phase 3 produced — options, scores, rejected alternatives, the references table —
evaporates at the end of the session. Six months from now nobody knows why the piece is
a skill and not an agent, and the thirteen-axis interview starts over from scratch.

**When to save a file.** Only if at least one of these is true:

- Two or more options scored ≥ 5 — there was a real choice.
- The top-scoring option strains an invariant of `@CLAUDE.md`.
- Axis 8 answered "both" — the piece also goes to the generated project.
- Axis 13 answered "both" — the MCP server is declared in `.mcp.json` and in
  `project-bootstrap`'s template. The duplication is deliberate (invariant 9), and
  without a record nobody six months from now can tell it apart from drift.
- The approved option is Form 7c or Form 8 — **always**, with no exception. A new mode in
  `ArchHook.java` is infrastructure: it has no markdown body to carry a `## Why this is
  <form>` section, it changes what every session enforces, and a later reader who can't
  reconstruct why it blocks will delete it the first time it gets in the way. A
  `permissions.deny` line is one line of JSON with the same problem and nowhere at all to
  explain itself.

None of these → don't save any file. The justification lives in the `## Why this is
<form>` section of the file Phase 4 creates, and that's enough. A record for a trivial
decision is ceremony, not memory.

**How.** Generate from `templates/decision.md.example` to
`.claude/decisions/NNNN-<slug>.md`, where `NNNN` is the highest existing plus one — read
it from the inventory injected at the top, not from memory. Save with **all** options and
without the `State` line: the decision hasn't been made yet. Directory rules in
`@.claude/decisions/README.md`.

**Stop here.** Wait for explicit approval. "Looks good" is not approval of which option.

If the user rejects everything, close the draft with
`State: rejected — no option approved` and stop. Don't delete it: its value is avoiding
the same interview again.

### Phase 4 · Write — only after approval

1. Generate from the `templates/` exemplar matching the approved form. For Form 6a, the
   shape reference is this skill's own `templates/mcp.json.example`: merge the new
   server into the target `.mcp.json` — this repo's root, and/or
   `project-bootstrap/templates/mcp.json.example`, per axis 13 — never overwrite
   existing servers already declared there. Write a companion
   `templates/mcp-setup.md.example`-shaped doc (this repo's own `MCP-SETUP.md`, or the
   project one, matching axis 13) listing the environment variables the new server
   needs, if any. For Form 7a/7b the shape reference is
   `templates/hook-entry.json.example`, and for 7c it is
   `templates/hook-mode.java.example` — merge into the `hooks` block that already exists,
   never replace it.
2. Frontmatter: native fields only, list in `references/frontmatter-fields.md`. An
   invented field is silently ignored by the runtime — it looks like behavior, it's
   decoration. For a Form 6a server's fields, the equivalent list is
   `references/mcp-fields.md`; for a Form 7 event, entry field, and whether that event
   reads a `matcher`, it is `references/hook-events.md` — same discipline,
   `.claude/schemas/extensions.json` is the actual owner of all three.
3. No `metadata:` in frontmatter. Ownership, `reads`, and handoff go in the `## Contract`
   section of the body.
4. Code boilerplate goes to `templates/<name>.example` inside the skill that emits it,
   never pasted in the body — invariant 3.
5. **No literal secret, ever, in a `.mcp.json` this skill writes or edits** — invariant
   11. `${VAR}` / `${VAR:-default}`, `oauth`, or `headersHelper` only. If the interview's
   axis 12 named a static token, write the `${VAR}` placeholder and hand the variable
   name to the companion setup doc from step 1 — never the value itself, not even
   "temporarily."
6. **Forms 7 and 8 — five rules, none of them optional.** They execute; a mistake here
   doesn't degrade, it blocks.

   - **Exec form, always.** `"command"` is the bare executable and `"args"` the
     arguments. A pipe, a `&&`, or a redirect inside `"command"` is a shell string in the
     executable's slot: `ArchHook.java schema` rejects it, and the runtime would have
     accepted it as a filename.
   - **Narrow before spawning.** Every entry carries an `if` (a permission rule, e.g.
     `Edit(.claude/**/*.md)`) or sits under a `matcher`, unless the check genuinely has to
     run on every occurrence of the event. Without one, a `PostToolUse` hook pays a JVM
     startup on every edit to every file in the repo — that is criterion 9 of the rubric,
     and it is the most common reason a hook gets deleted a month later.
   - **`matcher` only where the event reads one.** `references/hook-events.md` says which.
     On the others it is dead configuration that looks like a filter.
   - **Form 7c reuses `ArchHook.java`, and reads its lists from
     `.claude/schemas/extensions.json`.** A new mode is a `case` in the dispatch plus its
     own method — never a second hook file, and never a list of names, paths, or patterns
     hardcoded in the Java: invariant 10. Add the data to `extensions.json` and read it
     there.
   - **A blocking hook states the way out.** Exit `2` writes to stderr, and that text is
     the only thing the person who just got blocked will read. Name the rule, the file,
     and what to do instead.

   For Form 8, prefer `permissions.deny` over a hook whenever the answer is a flat
   refusal of a tool call: it costs no process and it is read before the call, not after.

7. **A `## Why this is <form>` section in the body of the created file, always.** Three
   sentences: the form chosen, the interview axis that motivated it, and the closest
   rejected form with the reason. It's the only record that travels with the file — it
   survives whoever never read `.claude/decisions/`, and the copy into the generated
   project. Precedent: `.claude/agents/project-initializer.md`, section "Why this is an
   agent and not a skill". For Form 5, and for Form 6a's `.mcp.json` itself (plain JSON,
   no room for prose), there's no body to put it in: the justification lives only in the
   decision record, and if there's no record, in the commit message. Form 6b does have a
   body — it's the same agent file whose own "Why this is an agent" section already
   covers it; add one line naming which server and why it's scoped to that agent alone.
   Forms 7a and 8 are JSON in the same position, and Phase 3.5 already makes their record
   mandatory. Form 7c does have a body — the Javadoc of the new mode's method — and
   carries the same three sentences there, naming the event that invokes it. Form 7b sits
   in the frontmatter of a skill or agent whose body already has a `## Why this is
   <form>` section: add one line saying which event, and why the hook is scoped to that
   invocation instead of the whole session.
8. **Propagate.** A new file that nobody routes to isn't found:

   | You created | Also update |
   |---|---|
   | Skill | `@CLAUDE.md` routing table, **and** a class in `skill_classes` of `@.claude/schemas/extensions.json` — its `skills` list, an `overrides.<skill>.write_allow` when the class default is wrong for it, and the `**Class:** <c>` line in the body. `ArchHook.java schema` fails by name on a skill in no class, and the body must carry every section that class requires |
   | Development skill (valid inside the generated project) | `export.skills.include` in `@.claude/schemas/extensions.json` — `ArchHook.java schema` fails on a skill listed in neither `include` nor `exclude` |
   | Rule | `@.claude/rules/00-index.md` (written-rules table; remove from planned). Every rule travels by default; a rule with a package territory also needs an `export.derived_paths` entry, and `schema` fails without it |
   | Agent | `@CLAUDE.md` routing table, if it's invocable by name, **and** a class in `agent_classes` of `@.claude/schemas/extensions.json` — its `agents` list, an `overrides.<agent>.write_allow` when the class default is wrong for it, the `**Class:** <c>` line in the body, and `**Executor:** yes` when the class grants `executor: true` (`schema` cross-checks both directions). The body must carry every section the class requires and the frontmatter every field it requires — `model` and `tools` always, which is also how invariant 5's reasons stop being implicit |
   | `CLAUDE.md` section | Nothing else — but confirm the total stays under ~200 lines |
   | MCP server, this repo only (axis 13 = "meta-repo") | `.mcp.json` at the root; the companion setup doc; `@CLAUDE.md` routing table row, if none already covers it |
   | MCP server, also the generated project (axis 13 = "both") | Everything above, **plus** `project-bootstrap/templates/mcp.json.example` and its own companion setup doc — both already named in `export.optional_copy`, so nothing else to wire |
   | Hook, this repo only (axis 8 = "meta-repo") | `.claude/settings.json`; `@CLAUDE.md` § Known pitfalls, if the hook blocks something a reader would otherwise call a bug |
   | Hook, also the generated project (axis 8 = "both") | Everything above, **plus** `project-bootstrap/templates/settings.json.example`. A Form 7c mode needs nothing further: the `export` mode copies `ArchHook.java` and `schemas/extensions.json` whole |
   | Hook mode (Form 7c) | The mode table in `@CLAUDE.md` § Commands, and the `doctor` report if the mode has state worth reporting |
   | `permissions` rule (Form 8) | `.claude/settings.json`; the generated project's template when axis 8 = "both". A `deny` also goes into `@CLAUDE.md` § Known pitfalls — a tool that silently refuses reads as a broken tool |

   A creation skill (only useful before the project exists) goes in `export.skills.exclude`,
   like `project-bootstrap` and `init-project`. State this explicitly in the report.

   **Delegation, and only in this case.** If axis 8 or axis 13 answered "both,"
   propagation grows — the `export` block plus `project-bootstrap`'s `templates/`.
   There, delegate **this step 8 and no other** to the generic agent with `model:
   sonnet`, passing the path of the Phase 3.5 record and the exact list of files to
   touch. These are mechanical table edits with a destination fixed in writing. Without a
   saved record, don't delegate: the subagent doesn't see the conversation, and the
   interview is what justifies each line. **Never delegate a Form 7 or Form 8
   propagation** even when axis 8 is "both": the second file is a hook registration that
   executes, and a wrong `if` or `matcher` copied into the generated project's template
   ships to every project made afterwards.

   Steps 1 through 7 **never** get delegated. Writing the `description` decides whether
   the skill fires, and the `## Contract` decides ownership — that's design, not
   transcription. For MCP the same split holds: which server to add, its credential
   shape, and its destination are design; copying an already-approved `.mcp.json` entry
   into a second file is the only mechanical part.

9. If you saved a draft in Phase 3.5, promote it: fill in `Decision`, `State` (approved
   by whom, on what date), and the `Propagation` table with the files step 8 touched.

10. Run `claude plugin validate .claude/skills` and report the output without rewriting
    it. **It does not look at `.mcp.json`, `.claude/settings.json`, or
    `.claude/agents/`** — run `java .claude/hooks/ArchHook.java schema` too, always when
    step 1 touched a `.mcp.json` or any `settings.json`. For Form 7, add
    `java .claude/hooks/ArchHook.java doctor` and read its `Hooks` line: it is the only
    check that the registration count is what you intended. For Form 7c, run the new
    mode by hand once — `java .claude/hooks/ArchHook.java <mode>` — before reporting it
    as done. A mode that throws exits 0 through the top-level catch and looks like it
    passed.

### Phase 5 · Report

Files created, files changed, the decision record path (or the sentence explaining why
there wasn't one), the `validate` and `schema` output, and — whenever a hook or a
`permissions` rule was written — the restart warning, in its own line: `.claude/settings.json`
is read only at session startup, so nothing written in Forms 7a or 8 takes effect until
`claude` is restarted. Say it even if the user already knows; a hook believed to be
active and silently absent is worse than no hook.

## Out of scope

**`~/.claude/settings.json` and `.claude/settings.local.json`.** Personal and
machine-local configuration, same exclusion as `local`-scope MCP below: not versioned,
not shared, not this skill's concern. A hook the team doesn't get isn't enforcement, it's
one person's habit. `settings.local.json` in particular is where a permission granted
mid-session lands — moving one of those into the versioned file is a decision the user
makes, and this skill only writes it when asked outright.

**A second hook file.** Form 7c is a mode inside `ArchHook.java`, never
`.claude/hooks/<Other>.java`. Enforcement stays in one executable that the generated
project receives whole from the `export` mode; a second file would have to be
copied, registered, and kept in sync separately, and the first one to fall out of sync
fails silently.

**`.claude/commands/`.** Never. Invariant 4: write a skill and control invocation with
`disable-model-invocation`.

**Blueprints.** Architecture is data, not extension — `@.claude/blueprints/_schema.md`.

**MCP where a CLI already solves it.** Decision matrix § 2.1: `gh`/`psql`/`aws`/etc. wins
over a new server, and this skill says so and proposes nothing. A tool that must never be
callable is no longer out of scope — it is Form 8, `permissions.deny` on
`mcp__<server>__<tool>`, and this skill writes it after approval like any other form.

**`~/.claude.json`, `local`/`user`-scope MCP.** Personal or experimental servers
(`claude mcp add` without `--scope project`) aren't versioned and aren't this skill's
concern — it only writes what the team shares.

**The `skill-creator` plugin** is disabled in this project (`enabledPlugins` in
`.claude/settings.json`) on purpose: it creates generic skills, with no knowledge of this
repo's invariants. Don't reintroduce it to work around this skill.

## Contract

**Class:** meta — the territory is `skill_classes.meta` in
`@.claude/schemas/extensions.json`: the `.claude/` tree, the root `CLAUDE.md`, `.mcp.json`,
the MCP setup doc, and the two derived places a new piece propagates into — `docs/**` (the
reference pages that describe a hook or a class) and `.github/**` (the workflow step that
runs a new check). `ArchHook.java guard` enforces it, which is the reason the guard is
registered in this meta-repository at all. A new skill is **incomplete** until a class in
`skill_classes` lists it — `schema` fails by name otherwise (Phase 4, step 8).

**Reads** `@claude-help.md`, `@CLAUDE.md` (the eleven invariants),
`@.claude/rules/00-index.md`, `@.claude/blueprints/_schema.md` when the decision touches
blueprints, and the inventory injected at the top. Reads this skill's `references/`
before classifying — the matrix is deliberately not in the body.

**Writes** `.claude/skills/**`, `.claude/agents/**`, `.claude/rules/**`, the root
`CLAUDE.md` of **this repository**, `.mcp.json` at this repo's root (Form 6a), the `hooks`
and `permissions` blocks of `.claude/settings.json` (Forms 7a and 8), and
`.claude/hooks/ArchHook.java` plus the lists it reads in `.claude/schemas/extensions.json`
(Form 7c). Only after explicit approval.

**Also writes** `.claude/decisions/NNNN-<slug>.md` — and this is the only path it touches
*before* approval, as the Phase 3.5 draft. It is the exclusive owner of the directory: no
other piece writes there, and nothing inside it is a rule.

**Does not write** `.claude/settings.local.json`, `~/.claude/settings.json`,
`~/.claude.json`, `.claude/blueprints/**`, a hook file other than `ArchHook.java`, nor
project Java code. Does not create `.claude/commands/`. **Never writes a literal secret**
into `.mcp.json` — invariant 11; a static credential from axis 12 becomes a `${VAR}`
placeholder plus a line in the companion setup doc, never a value.

**Does not** reproduce rules. A new rule is a file in `rules/` with a single owner — never
prose inside a `SKILL.md`. Nor does it hardcode a list into `ArchHook.java`: every name,
path, and pattern a Form 7c mode reads lives in `.claude/schemas/extensions.json`,
invariant 10.

**Delegates** at most step 8 of Phase 4 (propagation), and only when axis 8 or axis 13 is
"both", a decision record has been saved, and the approved form is **not** 7 or 8.
Classifying, proposing, and writing the body always stay in this thread — the subagent
doesn't receive the conversation, and the interview is the heart of the task.

**Stays out of the generated project.** It's a creation skill, like `project-bootstrap`
and `init-project`: whoever clones an already-generated project has no extensions to
design. The same applies to `.claude/decisions/` — it records decisions about this
meta-repository.
