↑ ↓ to move · Enter to open · Esc to closeTap a result

Architecture · how the harness works

Between the model and the disk, a hook.

Claude Code brings the model and its tools. Nerviz adds five kinds of piece around them. Four of them guide the model. The fifth runs as a process on every event, and it can refuse the action.

§ one feature, through the hooks

A use case meets the harness

bank-app's UC-005, from the first prompt to the green build, in nine steps. The terminal shows which hook modes ran and what they printed: the refusals are the hooks' own text.

1 / 9

A prompt opens the turn

You type /new-feature. guard prompt closes the previous phase, records what is already on disk, and opens a phase for new-feature, class orchestrator. Its body enters the context and stays for the rest of the run.

§ five kinds of piece

Who loads, who calls, who can say no

  • CLAUDE.md

    Index and routing table. The norm itself lives in the rule or skill it points to.

    Enters context
    Always, at session start
    Invoked by
    Nobody: it loads
    Can block
    No
  • rule

    A norm: what must be true. A leaf: it never names a skill, an agent or a command.

    Enters context
    When a file matching its paths is touched
    Invoked by
    Nobody: it loads
    Can block
    No
  • skill

    A procedure. Its class fixes its model, the sections of its body and where it may write.

    Enters context
    On invocation, then stays for the turns that follow
    Invoked by
    You, with /name, or the model
    Can block
    No
  • agent

    Isolated execution: its own context, restricted tools and its own model.

    Enters context
    On delegation, in a fresh context
    Invoked by
    A skill, through the Agent tool
    Can block
    No
  • hookexit 2

    ArchHook.jar: Java, eleven modes, no shell. Wired in settings.json.

    Enters context
    Never as text: it runs as a process
    Invoked by
    The runtime, on the event
    Can block
    Yes, exit 2

An agent exists for one of three reasons only: to preserve context, to restrict tools, or to switch model. Anything that must always hold is a hook or a permissions.deny rule, never prose in markdown.

§ the catalog, counted

How many skills and agents

Every skill and agent in nerviz belongs to a class in extensions.json, and schema fails on any file no class lists. So the classes are the catalog: these numbers are read from them at the latest release, never typed by hand.

32skills

27 of them are exported into every generated project

  • design 13
  • orchestrator 3
  • build 7
  • observer 2
  • meta 1
  • ops 4
  • report 2
7agents

5 of them are exported into every generated project

  • driver 1
  • executor 1
  • installer 4
  • verifier 1
Count over time
327
2026-09-112026-10-27
Latest additions
  1. project-configskill · opsnew2026-10-24
  2. platform-upgradeskill · build2026-10-19
  3. new-projectskill · ops2026-10-15
  4. mapstruct-installeragent · installer2026-10-14
  5. ai-architectskill · design2026-10-13

Read from extensions.json in nerviz release v0.44.0, published 2026-10-27.

§ who calls whom

Three commands, three chains

Each command opens a chain of its own:

  • /init-project creates the project, once.
  • /new-feature designs one use case and delivers a spec, no code.
  • /new-feature-implement builds the code from the approved spec.

Dashed pieces run only when they apply.

  • skill
  • agent
  • only when it applies
§ territory as data

Every class writes only its own paths

Each skill belongs to one of seven classes, each agent to one of four. The class lists what it may write in extensions.json, and guard refuses everything else, deny by default. Pick a class:

bank-app/
  • .claude/
  • CLAUDE.md
  • docs/
  • use-cases/BACKLOG.md
  • use-cases/UC-005-record-kyc-verdict/20-persistencia.md
  • use-cases/UC-005-record-kyc-verdict/UC-005-spec.md
  • lessons-learned/
  • src/main/java/…/domain/model/Customer.java
  • src/test/java/…/domain/model/CustomerTest.java
  • pom.xml
  • docker-compose.yml
  • .env.example
who

write_allow

  • may write
  • refused: outside the territory
  • refused: frozen spec
  • status close and checklist only
§ rules load by path

Touch a file, get its norms

Every rule declares paths. When a matching file is touched, the rule enters the context on its own, and nobody has to remember to cite it. These are bank-app's 21 rules, with the globs export wrote for its blueprint. Pick a file:

  • architecture-ddd.md
  • value-objects.md
  • naming.md
  • code-quality.md
  • error-handling.md
  • logging.md
  • lombok.md
  • personal-data.md
  • secrets.md
  • api-rest.md
  • api-graphql.md
  • inbound-admission.md
  • authorization.md
  • observability.md
  • persistence.md
  • messaging.md
  • scheduling.md
  • http-client.md
  • ai.md
  • caching.md
  • search.md
  • testing.md
  • transport-security.md
  • spec-diagrams.md
  • 00-index.md
§ the hook map

Every event, every mode

What a generated project registers in .claude/settings.json. Every entry runs java -jar .claude/hooks/ArchHook.jar <mode>, in exec form, the same on Linux, macOS and Windows. Pick an event to see when it fires.

The events, in the order they fire in a turn

UserPromptSubmitaudit promptguard prompt

You press Enter. The hook runs before Claude reads a word.

You typed

youYoutype the command
hookUserPromptSubmitopens the run's record and takes a git baseline
claudeClaudereads the prompt

Modes on this event

  • audit prompt
    Opens the record of the run when the prompt is an audited skill, so tokens and files can be attributed to it.project can switch off
  • guard prompt
    Closes the previous phase and opens the one the prompt names, so the guards know which skill is running.
PreToolUseguard callguard writeguard bashschemaaudit callaudit ask

Claude is about to use a tool. The hook still has time to say no.

You typed

claudeClaudepersistence-architect wants to write docker-compose.yml
hookPreToolUseguard write checks the territory of its class
✓ the tool runs
✕ exit 2: the reason goes back to Claude and nothing is written

Modes on this event

  • guard call
    Keeps a design run from reaching a build skill.can block

    Blocks: A Skill call to a build-class skill while a design run is open.

  • guard write
    Every skill and agent writes only inside the territory of its class, and an approved spec stays frozen.can block

    Blocks: A write outside the territory of the running skill or agent, and any write into an approved use case except its status close and checklist toggles.

  • guard bash
    The same check, reached from the shell: a heredoc, sed -i or tee must not do what a Write may not.can block

    Blocks: A shell write into a place its class cannot write or into a frozen use case, and a force push in any spelling.

  • schema
    Keeps the instruction files of the harness valid, so a broken skill, agent or hook entry fails at once and not in a later run.can block

    Blocks: An invalid SKILL.md, agent, rule or settings.json hook entry, and a secret in .mcp.json.

  • audit call
    Records each Skill, Task or Agent call as a node of the chain.project can switch off
  • audit ask
    Notes that Claude is waiting for you, so the wait is not counted as work.project can switch off
SubagentStartcontext subagenttests agent-start

Claude hands work to a subagent. The hook can load context before the subagent's first turn.

You typed

claudeClaudedelegates the code of the use case
hookSubagentStartcontext subagent hands over the patterns catalog
subjava-spring-boot-developerstarts with the catalog in context

Modes on this event

  • context subagent
    Hands the design patterns catalog to every agent whose class asks for it, before its first turn. Other agents get nothing.
  • tests agent-start
    Notes that a writer subagent is running, so the tests wait for it.
PostToolUsecheckschemaformataudit fileaudit answer

A tool has just run. The change is made, and the hook looks at it.

You typed

subjava-spring-boot-developeredits Customer.java
hookPostToolUsecheck, format and the trail look at the file
✓ clean: the work goes on
✕ exit 2: the file is already written, and Claude is told to fix it now

Modes on this event

  • check
    Holds the architecture while the code is written: the boundary is judged on each edit, not at review.can block

    Blocks: A forbidden import or qualified name, or a failed incremental compile of the touched module. The file is already written, so Claude is told to fix it.

  • schema
    Keeps the instruction files of the harness valid, so a broken skill, agent or hook entry fails at once and not in a later run.can block

    Blocks: An invalid SKILL.md, agent, rule or settings.json hook entry, and a secret in .mcp.json.

  • format
    Runs spotless:apply on the touched module, so formatting never costs a turn.
  • audit file
    Records each file a run touched.project can switch off
  • audit answer
    Records each question and your answer, redacted, in the report.project can switch off
SubagentStopaudit agenttests agent-end

The subagent finishes and hands its result back.

You typed

subjava-spring-boot-developerfinishes
hookSubagentStopcloses its report and frees the tests
claudeClaudegets the result

Modes on this event

  • audit agent
    Closes the subagent's report. Its tokens come from its own transcript.project can switch off
  • tests agent-end
    Frees the tests when the writer subagent ends.
Stopschemaguard sweepcompose gatetestsaudit flush

Claude thinks it is done. This is the last gate before the turn ends.

You typed

claudeClaudesays it is done
hookStopschema, guard sweep, compose gate and tests look at what really changed
✓ every gate is green: the turn ends
✕ a gate fails: Claude keeps working and fixes it

Modes on this event

  • schema
    Keeps the instruction files of the harness valid, so a broken skill, agent or hook entry fails at once and not in a later run.can block

    Blocks: An invalid SKILL.md, agent, rule or settings.json hook entry, and a secret in .mcp.json.

  • guard sweep
    The backstop at the end of the turn: it compares the disk with the git baseline, so it catches the writes no tool-time guard could read.can block

    Blocks: Any change this turn made that its class may not write. It detects; it does not prevent.

  • compose gate
    A stack that does not start is caught when the turn ends, not later on someone's machine.can blockproject can tune

    Blocks: The end of the turn while a compose service is exited or dead.

  • tests
    A turn does not end on red tests.can block

    Blocks: The end of the turn while verify fails on the modules changed since HEAD. It waits while a writer subagent runs.

  • audit flush
    Renders the run's report from the log and writes the ledger line.project can switch off
PermissionRequest · PermissionDenied · PostToolUseFailure · PreCompact · StopFailure · SessionEndaudit permaudit failaudit compactaudit stopfailaudit close

Six quieter events. They only write to the audit trail, and none of them blocks.

You typed

claudeClauderuns git push, which stays on ask
youYouanswer no
hookPermissionDeniedwrites a line in the trail

Modes on this event

  • audit perm
    Records a permission asked or denied.project can switch off
  • audit fail
    Records a tool that failed, as rework.project can switch off
  • audit compact
    Records a context compaction.project can switch off
  • audit stopfail
    Records a failure while stopping.project can switch off
  • audit close
    Closes the open run when the session ends.project can switch off

mode can refuse with exit 2 · mode never blocks ·can block project can tune project can switch off

Why each mode exists, what it blocks, and what a project can change: all of them, in a table, with the detail.

Open the mode catalog →
§ the harness, tested

Every refusal above runs in CI

validate.yml runs on every push and pull request. The hook tests launch the same java -jar .claude/hooks/ArchHook.jar the registrations run, on Ubuntu, macOS and Windows. Another job checks the invariants of the prompt graph, and another exports every blueprint twice and requires identical trees.

Latest run on mainvalidate.yml status on main, live from GitHub
validate.yml · 9fb82ed · 2026-10-13 · 6m17s
  1. hooks · ubuntu-latest✓1m32s
  2. hooks · macos-latest✓1m37s
  3. hooks · windows-latest✓3m15s
  4. design invariants✓33s
  5. export is reproducible and self-contained✓55s
  6. exemplar imports resolve against the real classpath✓6m13s

The six jobs run in parallel. In each hook job, build --verify runs before the tests: the committed jar must be byte for byte what the source compiles to, so the tests exercise reviewed bytes.

Open this run →
245/ 260

runs of validate.yml passed since 2026-09-11. 11 failed and 4 were cancelled. Counted on 2026-10-13.

The red ones stay public in Actions. The badge above is live; the numbers are a snapshot.

All runs →

Every step of that run, replayed

All 58 named steps of validate.yml, with the times GitHub recorded for the run above, played 12× faster. The hooks job runs each step on three runners, one dot per runner: Ubuntu, macOS and Windows.

6m17s / 6m17s12×
hooks3m15s
  1. Set up job · checkout · setup-java
  2. doctor
  3. committed ArchHook.jar is what the source compiles to
  4. the jar runs what the hooks run
  5. hook blocks forbidden import, from the payload and from argv
  6. format, check and tests hand Maven the module the edited file belongs to
  7. tests defers while a writer subagent of the session runs, and only then
  8. hook blocks a cwd-relative frontmatter injection
  9. schema blocks a migrations entry no project would see
  10. hook reports a compose image tag that disagrees with src/test
  11. guard keeps each skill inside its class's territory
  12. guard keeps each agent inside its class's territory
  13. guard bash refuses force pushes and holds shell writes to the phase
  14. guard sweep reports this turn's writes, never pre-existing dirt
  15. guard status prints the open phase the cockpit draws
  16. schema blocks a mod that would load half-way
  17. compose gate blocks a published service no host client can reach
  18. doctor gate fails on every gated line and only on those
  19. schema blocks a settings file that re-enables a colliding plugin
  20. doctor names a personal skill that shadows a project skill, and never gates
  21. doctor checks the agent instruction files against the blueprint, and never gates
  22. doctor sweeps every source file for forbidden imports, with check's own match
  23. export notes only real overwrites, and never guesses the stamp's commit
  24. context subagent hands the catalog to pattern_catalog agents only
  25. audit renders where the run spent, redacts tool errors, skips what class or project turns off
  26. audit genesis fills GENESIS from the transcripts, once
design invariants33s
  1. Set up job · checkout · setup-java
  2. frontmatter schema (extensions.json is the single owner — invariant 10)
  3. decisions/ doesn't grow paths or enter 00-index.md
  4. no hardcoded Spring/Java version outside decisions/ (invariant 8)
  5. new blueprint doesn't touch prompts
  6. rules is a leaf of the graph
  7. norm contains no code boilerplate
  8. no new commands/
  9. exemplars have the .example suffix at the end
  10. exemplar bodies carry no comment but Javadoc
  11. XML exemplars are well-formed
  12. spec template diagrams name only what their text defines
  13. cache exemplars decide every library default, and only they cache
  14. AI exemplars decide every library default, and only they reach the library
  15. GraphQL exemplars decide every limit and batch every child list
  16. document-family exemplars name collection, alias and version, and run a replica set
  17. search exemplars keep the index a strict, versioned projection
  18. auditing exemplars keep the history whole, its actor named and its prune safe
  19. REST exemplars version every handler from one configuration
  20. admission exemplars keep one bounded limiter per caller and an inbound cap ahead of idempotency
  21. blueprint-declared templates exist on disk
  22. issue forms cited by skills exist
  23. compose service templates merge and parse
  24. each norm has a single owner
  25. no dependencies outside the Java ecosystem
  26. every norm `paths` matches some blueprint
export is reproducible and self-contained55s
  1. Set up job · checkout · setup-java
  2. export twice per blueprint, require identical trees
  3. the exported tree carries nothing that only exists here
  4. the exported tree passes its own schema
  5. re-export deletes what export.retired names
exemplar imports resolve against the real classpath6m13s
  1. Set up job · checkout · setup-java
  2. reference project from the Initializr
  3. build the classpath
  4. every exemplar import exists in a JAR
  5. exemplars don't use known deprecated symbols

Each step of the player, and the test that proves it

  1. step 4

    A design skill cannot write docker-compose.yml. The case that started it all is one of the cases.

    $ java .claude/.ci/SkillTerritoryTest.java✅ compose file outside the territory — blocked (exit 2)✅ compose allowed once docker-architect joined (exit 0)✅ the implement thread itself still may not write compose (exit 2)59 cases, all green

    The same territory holds for shell writes, and every force-push spelling is refused.

    $ java .claude/.ci/BashGuardTest.java✅ `-f` — blocked✅ executor: `sed -i -e` on a file outside its territory — blocked29 cases, all green
  2. step 6

    An agent is judged by its own class, whatever phase its caller left open.

    $ java .claude/.ci/AgentTerritoryTest.java✅ the executor writes main source (exit 0)✅ archunit-installer is refused docker-compose.yml (exit 2)37 cases, all green

    The design patterns catalog reaches only the agents whose class asks for it.

    $ java .claude/.ci/SubagentContextTest.java✅ general-purpose receives nothing✅ context subagent: catalog to pattern_catalog agents only, within max_chars.
  3. step 7

    A forbidden import exits 2, from the hook payload and from check <path>.

    $ java .claude/.ci/BoundaryTest.java  ✓ stdin payload blocks the forbidden import  ✓ check <path> blocks it with stdin left open✅ Boundary enforced: forbidden import blocked with exit 2, from the payload and from argv.
  4. step 9

    The Stop sweep names only what this turn wrote, and never blocks twice.

    $ java .claude/.ci/SweepTest.java✅ write outside the territory — reported, pre-existing dirt is not✅ stop_hook_active — does not block twice

    tests waits while a writer subagent runs, and only then.

    $ java .claude/.ci/TestsDeferTest.java✅ tests: deferred while a writer subagent runs, Maven called otherwise.

The prompt graph, checked like code

The design invariants job turns each rule of the harness's own architecture into a failing step. Some of them, as named in the workflow:

  • frontmatter schema (extensions.json is the single owner — invariant 10)
  • rules is a leaf of the graph
  • new blueprint doesn't touch prompts
  • no hardcoded Spring/Java version outside decisions/ (invariant 8)
  • each norm has a single owner
  • every norm `paths` matches some blueprint

And in every generated project

The project carries its own CI. Its architectural boundaries job runs doctor gate, which fails when the hook jar, the hooks, the schema or the boundaries are broken, or when any source file holds a forbidden import.

bank-app · build.yml · a3cc28a · 2026-10-15
  • architectural boundariesjava .claude/hooks/ArchHook.java doctor gate10s
  • build and tests3m37s
Open bank-app's run →