Current inventory
Skills: !ls .claude/skills
Agents: !ls .claude/agents
Rules: !ls .claude/rules
Decision records (the latest is the most recent): !ls .claude/decisions
Scenario
$ARGUMENTS
Claude Code Architect Designer
Decides which of the six forms of extension resolves the scenario, and writes it. The six:
| # | 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 |
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.
A legitimate seventh 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 the problem is a compliance issue and belongs in a hook/permissions.deny —
which this skill does not write, see § Out of scope.
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 | Out of scope (hook) |
| 8 | Destination — this repo only, also the generated project, or both | Steps 6.6/6.7/7 of project-bootstrap |
| 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 |
| 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 6 |
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.
Phase 2 · Classify
Apply the decision table in references/decision-matrix.md (§ 2, and § 2.1 whenever
axis 2 named an external system). 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), 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:
- Title — proposed filename, in kebab-case
- Motivator — the interview axis that justifies it
- Pros
- Cons — includes the invariant it strains, if any
- Score 0-10 — rubric in
references/decision-matrix.md, § Rubric - 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.jsonand inproject-bootstrap's template. The duplication is deliberate (invariant 9), and without a record nobody six months from now can tell it apart from drift.
None of these → don't save any file. The justification lives in the `## Why this is
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
Generate from the
templates/exemplar matching the approved form. For Form 6a, the shape reference is this skill's owntemplates/mcp.json.example: merge the new server into the target.mcp.json— this repo's root, and/orproject-bootstrap/templates/mcp.json.example, per axis 13 — never overwrite existing servers already declared there. Write a companiontemplates/mcp-setup.md.example-shaped doc (this repo's ownMCP-SETUP.md, or the project one, matching axis 13) listing the environment variables the new server needs, if any.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 isreferences/mcp-fields.md— same discipline,.claude/schemas/extensions.json'smcpblock is the actual owner.No
metadata:in frontmatter. Ownership,reads, and handoff go in the## Contractsection of the body.Code boilerplate goes to
templates/<name>.exampleinside the skill that emits it, never pasted in the body — invariant 3.No literal secret, ever, in a
.mcp.jsonthis skill writes or edits — invariant${VAR}/${VAR:-default},oauth, orheadersHelperonly. 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."
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.jsonitself (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.Propagate. A new file that nobody routes to isn't found:
You created Also update Skill @CLAUDE.mdrouting tableDevelopment skill (valid inside the generated project) Table in step 6.7 of project-bootstrap/SKILL.mdand the## Skill contractlistRule @.claude/rules/00-index.md(written-rules table; remove from planned) and the table in step 6.6 ofproject-bootstrap/SKILL.mdAgent @CLAUDE.mdrouting table, if it's invocable by nameCLAUDE.mdsectionNothing else — but confirm the total stays under ~200 lines MCP server, this repo only (axis 13 = "meta-repo") .mcp.jsonat the root; the companion setup doc;@CLAUDE.mdrouting table row, if none already covers itMCP server, also the generated project (axis 13 = "both") Everything above, plus project-bootstrap/templates/mcp.json.example, its own companion setup doc, and the copy table in step 7.5 ofproject-bootstrap/SKILL.mdA creation skill (only useful before the project exists) stays outside step 6.7, like
project-bootstrapandinit-project. State this explicitly in the report.Delegation, and only in this case. If axis 8 or axis 13 answered "both," propagation grows — steps 6.6/6.7/7/7.5 of
project-bootstrap, plus itstemplates/. There, delegate this step 7 and no other to the generic agent withmodel: 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.Steps 1 through 6 never get delegated. Writing the
descriptiondecides whether the skill fires, and the## Contractdecides 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.jsonentry into a second file is the only mechanical part.If you saved a draft in Phase 3.5, promote it: fill in
Decision,State(approved by whom, on what date), and thePropagationtable with the files step 7 touched.Run
claude plugin validate .claude/skillsand report the output without rewriting it. Doesn't cover.mcp.json— runjava .claude/hooks/ArchHook.java schematoo, whenever step 1 touched a.mcp.json.
Phase 5 · Report
Files created, files changed, the decision record path (or the sentence explaining why
there wasn't one), the validate output, and the restart warning if you touched
settings.json — it's only read at session startup.
Out of scope
Hooks and permissions.deny. If the interview concludes on axis 7 that the rule must
always hold, the answer is a hook — and this skill's correct response is to say so and
stop, not to write the hook. Reason: this repo's enforcement is concentrated in
ArchHook.java, and adding a subcommand to it is infrastructure change, with its own
test and its own commit. Propose, don't execute.
.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, or a specific tool that must never be callable.
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 run is permissions.deny on
mcp__<server>__<tool> — same as any other forbidden action, propose and stop, don't
write the rule.
~/.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
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, and .mcp.json at this repo's root (Form 6a). 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.json, .claude/hooks/**, .claude/blueprints/**,
~/.claude.json, 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.
Delegates at most step 7 of Phase 4 (propagation), and only when axis 8 or axis 13 is "both" and a decision record has been saved. 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.