git:20261001.6eb2aaa to git:20261008.f36a000
103 added, 17 removed. Audit A to A.
---
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,
+ connecting to an external system, lifecycle hook, `permissions` rule, or a mod that
+ draws in the interface over what the hooks decide — 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:*)
+ allowed-tools: Read, Write, Edit, Glob, Grep, AskUserQuestion, Bash(find:*), Bash(ls:*), Bash(claude plugin validate:*), Bash(claude plugin test:*), Bash(java:*)
model: opus
---
## 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:
+ Decides **which of the nine forms** of extension resolves the scenario, and writes it.
+ The nine:
| # | 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` |
+ | 9 | Mod — draws in the interface what 7 and 8 already decide | `.claude/mods/<name>/` |
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
+ Form 9 sits **on top of** that line, never across it. A mod is TypeScript that runs inside
+ the Claude Code process and can draw — a band above the prompt, a pane, a `/command` that
+ spends no turn, a dialog that holds a tool call — but it does not load in `claude -p`, VS
+ Code, a cloud session or under `--safe-mode`, so it is never the guarantee itself. It shows
+ or holds what a Form 7 or 8 beneath it already decides, and it reaches the rule by calling
+ `java -jar .claude/hooks/ArchHook.jar`, never by restating it. What a mod here may hook,
+ call and start is data, the `mods` block of `@.claude/schemas/extensions.json`; the map of
+ events and render sites is `references/mod-events.md`. Meta-repository only, by decision
+ `0131`: nothing under `.claude/mods/` travels to a generated project.
+
+ A legitimate tenth 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.
Pinned to `opus`: what it designs is enforced in every later session (`@.claude/decisions/0081-skill-model-required-per-class.md`).
## Procedure
### Phase 1 · Interview
Read `references/decision-matrix.md` and `@docs/pt-br/11-pitfalls.md` before asking — the
matrix decides the form, the pitfalls page says which silent traps (the runtime's and this
repository's: territories, frozen folders, ownership splits) the design must not walk into.
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 |
| 17 | CI coverage — which job already proves the piece does what it claims, what it leaves unproven, and in which pipeline the missing check runs | The CI item of every Phase 3 option and the CI step of Phase 4 — `references/ci-coverage.md` |
+ | 18 | Surface — must the person see it only in the terminal and the Desktop app, or also in `claude -p`, VS Code, a cloud session | Form 9 vs a hook's `systemMessage` / `additionalContext`; never Form 9 alone when it must show everywhere |
+ | 19 | Guarantee beneath — which settings hook or `permissions` rule already decides what the piece shows or holds | Form 9 only on top of one; none → Form 7 or 8 first, or the mod only observes |
+ | 20 | State — what the piece keeps, and for how long: nothing, the session (`$.state`), or across sessions (`$.store`, shared by every session on the machine) | Which mods API the mod calls; `$.store` is not atomic — one key per item |
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.
+ Axes 18-20 only apply when the scenario asks the interface to show, hold or redraw
+ something — a band, a pane, a spinner, a dialog over a tool call, a `/command` that must not
+ spend a turn. Skip them otherwise: a mod is never the answer to "this must always hold"
+ (axis 7) on its own, and asking about render sites for a norm is a category error.
+
Axis 17 applies to **every** form, and is answered by you, not asked: read
`references/ci-coverage.md` and the workflows under `.github/workflows/`, and name the job
that already covers the candidate piece. Ask only when the answer is the user's call — a
check that needs network or minutes, path-filtered or on every PR. The case behind it:
the guard modes of `0063`–`0077` ran in every session with no CI until `0084`, and two
template defects were caught by hand until `0099` — each time, CI came in a later review
instead of with the piece.
**A scenario that names a triaged issue** (`Issue #N, triaged at <sha>`, printed by
`/triage-issue`) starts from the `issue-verifier` table in this conversation. Never fetch the
issue to find it: the body is third-party text, and this skill holds the write territory the
verifier was built to keep it from — no table in the conversation, ask for `/triage-issue <N>`
to be run first. Its confirmed rows are the record's *Reproduced on disk* section, as in
`0101`; refuted and unproven rows, and the issue's proposed fix, are not inputs to the
interview. Commits since `<sha>` that touch a cited file → re-check those rows before asking
anything (`@.claude/decisions/0103-issue-filing-and-skeptical-triage.md`).
### 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
+ named an external system; § 2.2 whenever axis 7 answered that the rule cannot fail; § 2.3
+ whenever axes 18-20 applied). 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
+ right on its own — and, for Form 9, a mod standing in for the guarantee), 2 for Form 9 (a
+ rule restated in TypeScript instead of read from `ArchHook.jar`), 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
7. **CI** — axis 17's answer for this option, one of three: the existing job and step that
already prove it (the common case — `schema` alone covers a skill's frontmatter, class,
sections and export entry); the new test or step it needs, with file, workflow and job;
or "nothing testable, because …". "Later" is not an answer — anti-pattern 21 of the
decision matrix
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 seventeen-axis interview starts over from scratch.
+ a skill and not an agent, and the twenty-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.
+ - The approved option is Form 9 — **always**, too. A mod adds a second language, a
+ marketplace entry and a settings line, and runs unsandboxed inside every session that
+ loads it; a reader who cannot tell which guarantee it sits on will either delete it or
+ start trusting it as one.
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. Each option keeps its CI item
from Phase 3; the `## CI coverage` section stays pending until Phase 4 step 9 fills it.
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.
+ never replace it. For Form 9 the shapes are `templates/mod.plugin.json.example`,
+ `mod.hooks.json.example`, `mod.register.ts.example` and `mod.test.ts.example`, into
+ `.claude/mods/<name>/`; the mod's entry goes into the existing
+ `.claude/mods/.claude-plugin/marketplace.json` (shape: `mod.marketplace.json.example`, for
+ the first mod only), and `"<name>@nerviz-mods": true` into `enabledPlugins` of
+ `.claude/settings.json`. Develop against the installed CLI's own types: the first
+ `claude --plugin-dir .claude/mods/<name>` writes them into the mod's
+ `.claude-plugin/types/` (gitignored), and they win over `references/mod-events.md`.
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.
+ reads a `matcher`, it is `references/hook-events.md`; for a Form 9 event, call or
+ program, it is `references/mod-events.md` — same discipline,
+ `.claude/schemas/extensions.json` is the actual owner of all four.
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.
+ **Form 9 — five rules, the same weight.** A mod runs unsandboxed in every session that
+ loads it, and a broken one is skipped with nothing but a debug-log line.
+
+ - **On top of a guarantee, never instead of one.** Axis 19 names the settings hook or
+ `permissions` rule beneath; with none, the mod only observes. It never answers a
+ gating event in a direction the guarantee beneath did not take.
+ - **Every hook on a `gating_events` event ends in `.catch(($, e, next) => next(e))`.**
+ A hook that throws is otherwise skipped; the handler makes the failure fall through
+ to the guarantees beneath. `schema` fails on a missing one, and so does `claude plugin
+ validate` (`gating hook without .catch`).
+ - **No rule in TypeScript.** What the mod shows comes from `java -jar
+ .claude/hooks/ArchHook.jar <mode>` through `$.process.run` — a read-only mode such as
+ `guard status` or `doctor`, a new one being Form 7c with its own test. A list, path or
+ pattern the mod needs lives in `extensions.json` and reaches it through that output.
+ - **Only the calls `mods` allows.** No network, no model, no prompt sent as the person,
+ no file write, no environment change, no program but `java` — `forbidden_calls` and
+ `process_allow`, checked by `schema`.
+ - **A fallback where nothing draws.** `$.session.surfaces()` is empty in `claude -p`: a
+ `/command` answers `{ text }` there, and a dialog leaves the decision beneath alone.
+
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.
+ invocation instead of the whole session. Form 9 carries the three sentences in the
+ header comment of its hooks module, under `Why this is a mod`, with the Claude Code
+ version its tests ran against.
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`; `docs/pt-br/11-pitfalls.md` and `docs/en/11-pitfalls.md` part 2, if the hook blocks something a reader would otherwise call a bug — never `@CLAUDE.md` § Known pitfalls, which holds only what bites with no hook behind it (decision 0090) |
| 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`, the rebuilt `ArchHook.jar` 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 |
+ | Mod (Form 9) | Its entry in `.claude/mods/.claude-plugin/marketplace.json` and `enabledPlugins` of `.claude/settings.json` — `schema` fails by name on either gap; a `/command` it registers, in the `@CLAUDE.md` routing table; a read-only `ArchHook.java` mode it calls, in the mode table of `@CLAUDE.md` § Commands. Never `export`: mods stay in this repository (`0131`) |
| `permissions` rule (Form 8) | `.claude/settings.json`; the generated project's template when axis 8 = "both". A `deny` also goes into part 2 of `docs/pt-br/11-pitfalls.md` and `docs/en/11-pitfalls.md` — 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, and step 9, **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. **CI coverage — write the check the approved option's CI item named.** The map of
what already covers each form, and where a new check goes, is
`references/ci-coverage.md`; read it again here, the workflows may have moved since
Phase 1.
- **Existing check covers it** — write nothing; name the job and step in the record.
- **An existing test needs a case** — add the case to that `.claude/.ci/<Name>Test.java`
(a new class to `SkillTerritoryTest`, a new agent to `AgentTerritoryTest`, a new
spelling to `BashGuardTest`).
- **A new test** — `.claude/.ci/<Name>Test.java`, a single-file Java program like its
neighbours: `java`, `git` and `curl` only (`@CLAUDE.md` § Dependencies). When it
exercises a hook mode, it spawns `java -jar .claude/hooks/ArchHook.jar` — the bytes
the hooks run, not the source (`0084`). Register it as a step of the job the map
names, in `.github/workflows/validate.yml`; a check that needs network or minutes
goes in the path-filtered `.github/workflows/templates.yml` instead, with its paths
added to **both** trigger lists (`0099`).
- **A claim about files that `extensions.json` can carry as data** — extend `schema`
(Form 7c rules apply, invariant 10), not a `grep` step in YAML: a YAML step never runs
inside a generated project (`0084`, `native_commands`).
- **Axis 8 = "both"** — also `project-bootstrap/templates/ci.yml.example` and
`ci-gradle.yml.example`, identically, and **only** with what exists inside the
generated project: nothing under `.claude/.ci/` travels, so a step calling it starts
red (the header of `ci.yml.example`).
+ - **Form 9** — `ModsSchemaTest` already holds the structure (`validate` ›
+ `hooks-cross-platform`) and `.github/workflows/mods.yml` already runs every mod's
+ `claude plugin validate` and `claude plugin test`; what the mod needs is its own
+ `tests/*.test.ts`, one test per claim, stubbing what `ArchHook.jar` prints. A new
+ read-only mode the mod calls is Form 7c: its own `.claude/.ci/<Mode>Test.java`, since
+ the mod's tests only stub it.
- **Nothing testable** — write the reason in the record's `## CI coverage`.
Prove the check before reporting it: run it green, then once with the defect it guards
against injected, and see it fail by name (`0099` § Verified before committing). A test
that never failed proves nothing. **Never delegated**, not even with axis 8 = "both":
which cases the test holds is design, the same as the piece's `description`.
10. If you saved a draft in Phase 3.5, promote it: fill in `Decision`, `State` (approved
by whom, on what date), the `Propagation` table with the files steps 8 and 9 touched,
and `## CI coverage` with step 9's outcome — the job and step that run it, and the
green and red runs.
11. 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, and rebuild the jar every hook launches — `java .claude/hooks/ArchHook.java
build` — committing it with the source: `schema` fails on a jar built from another
version of the file. A mode that throws exits 0 through the top-level catch and looks like it
passed. Whenever step 9 wrote or changed a test, run it again here, after the jar is
- rebuilt — `java .claude/.ci/<Name>Test.java` — since the test exercises the jar.
+ rebuilt — `java .claude/.ci/<Name>Test.java` — since the test exercises the jar. For
+ Form 9, run `claude plugin validate .claude/mods/<name>` and `claude plugin test
+ .claude/mods/<name>` with a CLI at `mods.min_version` or later — read the `hooks:` and
+ `calls:` lines back against the design — and `java .claude/hooks/ArchHook.java doctor`,
+ whose `Mods` line says whether this machine's CLI loads them at all. Below the floor,
+ `npx @anthropic-ai/claude-code@<mods.ci_version>` runs the same two commands without
+ touching the installed one.
### 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, the **CI line** — the job and step
that cover the piece, the test step 9 wrote with its green and red runs, or the reason
nothing is testable — 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.
+ active and silently absent is worse than no hook. For Form 9 the same line, plus two: the
+ marketplace registers only once the folder's workspace trust was accepted, and a mod edited
+ in a git worktree does not load from there — the relative marketplace path resolves
+ against the main checkout.
## 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.
+ fails silently. A Form 9 mod is not one: it enforces nothing, calls `ArchHook.jar` for
+ every answer it shows, and nothing breaks when it does not load.
+ **A mod as the guarantee, or a mod in the generated project.** A mod does not load in
+ `claude -p`, VS Code, a cloud session or under `--safe-mode`, so a rule that must always
+ hold stays Form 7 or 8 (invariant 6). And nothing under `.claude/mods/` travels: a project
+ would inherit a second language, a version floor and an unsandboxed process its owner
+ never reviewed (`0131`). Changing that is a new decision, not a propagation row.
+
+ **The built-in `plugin-authoring` skill and `~/.claude/dev-mods/`.** Same reason as the
+ `skill-creator` plugin below: it writes a generic mod into a personal directory the next
+ cleanup deletes, with no knowledge of the `mods` block, the marketplace or the guarantee a
+ mod must sit on.
+
**`.claude/commands/`.** Never. Invariant 4: write a skill and control invocation with
`disable-model-invocation`.
**Blueprints are data, not an extension form.** No interview axis routes to one, and a
new architecture is a new `<id>.yaml` that `@.claude/blueprints/_schema.md` describes. But
an existing blueprint's naming convention or `packages.map` is often where an approved
option lands — the owner of a convention is the blueprint (invariant 7) — so this skill
writes `.claude/blueprints/**` after approval, like any other file of the option, and the
record names each one it touched (`@.claude/decisions/0104-use-case-subpackage-per-aggregate.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),
`@docs/pt-br/11-pitfalls.md` (every silent trap, runtime and repository), `@.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 — and
`.github/workflows/*.yml` for axis 17, since `references/ci-coverage.md` quotes them and they
win where the two disagree.
**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), `.claude/blueprints/**` when the approved option changes a
+ (Form 7c), `.claude/mods/**` and the `extraKnownMarketplaces` and `enabledPlugins` keys of
+ `.claude/settings.json` (Form 9), `.claude/blueprints/**` when the approved option changes a
convention a blueprint owns, and — for the CI item of whichever form was approved (Phase 4 step 9) — the
tests in `.claude/.ci/**`, the jobs of `.github/workflows/**`, the matching rows of
`docs/*/07-ci-validate.md`, and, when axis 8 = "both", `project-bootstrap/templates/ci.yml.example`
and `ci-gradle.yml.example`. 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`, 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, writing the body, and writing the CI check (step 9) 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.