@xhulz/nina 0.28.20 → 0.28.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/releases/0.28.21/core/GAPS.md +125 -0
- package/releases/0.28.21/core/answered.json +29 -0
- package/releases/0.28.21/core/defaults/tree/.claude/agents/architect.md +1 -0
- package/releases/0.28.21/core/defaults/tree/.claude/agents/dba.md +3 -0
- package/releases/0.28.21/core/defaults/tree/.claude/agents/devops.md +1 -0
- package/releases/0.28.21/core/defaults/tree/.claude/agents/implementer.md +1 -0
- package/releases/0.28.21/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.28.21/core/defaults/tree/.claude/agents/qa.md +2 -0
- package/releases/0.28.21/core/defaults/tree/.claude/agents/reviewer.md +3 -0
- package/releases/0.28.21/core/defaults/tree/.claude/agents/secops.md +3 -0
- package/releases/0.28.21/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
- package/releases/0.28.21/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
- package/releases/0.28.21/core/tree/.claude/agents/architect.md +125 -0
- package/releases/0.28.21/core/tree/.claude/agents/dba.md +107 -0
- package/releases/0.28.21/core/tree/.claude/agents/devops.md +113 -0
- package/releases/0.28.21/core/tree/.claude/agents/implementer.md +155 -0
- package/releases/0.28.21/core/tree/.claude/agents/integration-tester.md +160 -0
- package/releases/0.28.21/core/tree/.claude/agents/planner.md +132 -0
- package/releases/0.28.21/core/tree/.claude/agents/qa.md +146 -0
- package/releases/0.28.21/core/tree/.claude/agents/reviewer.md +180 -0
- package/releases/0.28.21/core/tree/.claude/agents/secops.md +121 -0
- package/releases/0.28.21/core/tree/.claude/agents/solidity-auditor.md +126 -0
- package/releases/0.28.21/core/tree/.claude/agents/solidity-dev.md +123 -0
- package/releases/0.28.21/core/tree/.claude/agents-overview.md +64 -0
- package/releases/0.28.21/core/tree/.claude/graph.md +73 -0
- package/releases/0.28.21/core/tree/.claude/patterns.md +212 -0
- package/releases/0.28.21/core/tree/.claude/pills/README.md +145 -0
- package/releases/0.28.21/core/tree/.claude/pipeline.md +102 -0
- package/releases/0.28.21/core/tree/.claude/retrieval.md +104 -0
- package/releases/0.28.21/core/tree/.claude/router.md +201 -0
- package/releases/0.28.21/core/tree/.claude/templates/integration.md +45 -0
- package/releases/0.28.21/core/tree/CLAUDE.md +155 -0
- package/releases/0.28.21/core/tree/scripts/edit-guard.mjs +16 -0
- package/releases/0.28.21/core/tree/scripts/harness-check.mjs +71 -0
- package/releases/0.28.21/core/tree/scripts/loop-gate.mjs +19 -0
- package/releases/0.28.21/core/vocabulary.json +12 -0
- package/releases/0.28.21/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
- package/releases/0.28.21/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
- package/releases/0.28.21/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
- package/releases/0.28.21/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
- package/releases/0.28.21/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
- package/releases/0.28.21/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
- package/releases/0.28.21/surfaces/blockchain/tree/.claude/graph.md +11 -0
- package/releases/0.28.21/surfaces/blockchain/tree/.claude/router.md +9 -0
- package/releases/0.28.21/surfaces/blockchain/tree/CLAUDE.md +12 -0
- package/releases/0.28.21/surfaces/db/tree/.claude/agents/architect.md +8 -0
- package/releases/0.28.21/surfaces/db/tree/.claude/agents/dba.md +84 -0
- package/releases/0.28.21/surfaces/db/tree/.claude/agents/devops.md +14 -0
- package/releases/0.28.21/surfaces/db/tree/.claude/agents/implementer.md +5 -0
- package/releases/0.28.21/surfaces/db/tree/.claude/agents/integration-tester.md +13 -0
- package/releases/0.28.21/surfaces/db/tree/.claude/agents/planner.md +2 -0
- package/releases/0.28.21/surfaces/db/tree/.claude/agents/reviewer.md +25 -0
- package/releases/0.28.21/surfaces/db/tree/.claude/agents/secops.md +2 -0
- package/releases/0.28.21/surfaces/db/tree/.claude/agents-overview.md +5 -0
- package/releases/0.28.21/surfaces/db/tree/.claude/graph.md +8 -0
- package/releases/0.28.21/surfaces/db/tree/.claude/patterns.md +29 -0
- package/releases/0.28.21/surfaces/db/tree/.claude/pipeline.md +13 -0
- package/releases/0.28.21/surfaces/db/tree/.claude/retrieval.md +5 -0
- package/releases/0.28.21/surfaces/db/tree/.claude/router.md +10 -0
- package/releases/0.28.21/surfaces/db/tree/CLAUDE.md +23 -0
- package/releases/0.28.21/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
- package/releases/0.28.21/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
- package/releases/0.28.21/surfaces/edge-cf/tree/.claude/agents/devops.md +31 -0
- package/releases/0.28.21/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
- package/releases/0.28.21/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
- package/releases/0.28.21/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
- package/releases/0.28.21/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.28.21/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
- package/releases/0.28.21/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
- package/releases/0.28.21/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
- package/releases/0.28.21/surfaces/edge-cf/tree/.claude/router.md +21 -0
- package/releases/0.28.21/surfaces/edge-cf/tree/CLAUDE.md +27 -0
- package/releases/0.28.21/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
- package/releases/0.28.21/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
- package/releases/0.28.21/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
- package/releases/0.28.21/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
- package/releases/0.28.21/surfaces/frontend/tree/.claude/agents/reviewer.md +62 -0
- package/releases/0.28.21/surfaces/frontend/tree/.claude/patterns.md +15 -0
- package/releases/0.28.21/surfaces/frontend/tree/.claude/retrieval.md +5 -0
- package/releases/0.28.21/surfaces/frontend/tree/.claude/router.md +17 -0
- package/releases/0.28.21/surfaces/frontend/tree/CLAUDE.md +5 -0
- package/releases/0.28.21/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
- package/releases/0.28.21/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
- package/releases/0.28.21/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
- package/releases/0.28.21/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
- package/releases/0.28.21/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
- package/releases/0.28.21/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
- package/releases/0.28.21/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
- package/releases/0.28.21/surfaces/integrations/tree/.claude/graph.md +9 -0
- package/releases/0.28.21/surfaces/integrations/tree/.claude/patterns.md +38 -0
- package/releases/0.28.21/surfaces/integrations/tree/.claude/pipeline.md +16 -0
- package/releases/0.28.21/surfaces/integrations/tree/.claude/retrieval.md +16 -0
- package/releases/0.28.21/surfaces/integrations/tree/.claude/router.md +6 -0
- package/releases/0.28.21/surfaces/integrations/tree/CLAUDE.md +17 -0
- package/releases/0.28.21/surfaces/money/tree/.claude/agents/architect.md +12 -0
- package/releases/0.28.21/surfaces/money/tree/.claude/agents/dba.md +14 -0
- package/releases/0.28.21/surfaces/money/tree/.claude/agents/implementer.md +11 -0
- package/releases/0.28.21/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.28.21/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
- package/releases/0.28.21/surfaces/money/tree/.claude/agents/secops.md +5 -0
- package/releases/0.28.21/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
- package/releases/0.28.21/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
- package/releases/0.28.21/surfaces/money/tree/.claude/graph.md +2 -0
- package/releases/0.28.21/surfaces/money/tree/.claude/patterns.md +49 -0
- package/releases/0.28.21/surfaces/money/tree/.claude/pipeline.md +2 -0
- package/releases/0.28.21/surfaces/money/tree/.claude/retrieval.md +2 -0
- package/releases/0.28.21/surfaces/money/tree/CLAUDE.md +11 -0
- package/releases/0.28.21/surfaces/pii/tree/.claude/agents/dba.md +2 -0
- package/releases/0.28.21/surfaces/pii/tree/.claude/agents/devops.md +8 -0
- package/releases/0.28.21/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.28.21/surfaces/pii/tree/.claude/agents/secops.md +2 -0
- package/releases/0.28.21/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
- package/releases/0.28.21/surfaces/pii/tree/CLAUDE.md +5 -0
- package/src/commands/upgrade.mjs +19 -4
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# Agent Router
|
|
2
|
+
|
|
3
|
+
Decides which subagent (or chain) to dispatch for a given task. Subagent definitions live in `.claude/agents/`.
|
|
4
|
+
|
|
5
|
+
<!-- nina:slot project.1 track-summary -->
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Decision tree
|
|
10
|
+
|
|
11
|
+
The stages and edges are in `.claude/graph.md`. What follows is this project's own way of sizing a
|
|
12
|
+
task into a chain over them.
|
|
13
|
+
|
|
14
|
+
<!-- nina:slot project.2 decision-tree -->
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Track flow
|
|
19
|
+
|
|
20
|
+
<!-- nina:slot project.3 track-flow-diagram -->
|
|
21
|
+
|
|
22
|
+
The stages, and the edges between them, are in `.claude/graph.md` — the one place they are stated, composed for this project. Every gate the diff triggers runs in parallel after the implementer, and each must approve before the reviewer can. `qa` runs after the reviewer approves.
|
|
23
|
+
|
|
24
|
+
**Every loop has a cap.** Before dispatching a loop-back, count the rounds the SAME issue has already made on that edge. At the cap `.claude/graph.md` gives it, do not dispatch again: stop and hand {{OWNER}} the report from every round. A third attempt at a fix that failed twice is rarely different from the second, each round costs minutes to hours<!-- nina:why -->, and until this rule existed nothing in the pipeline could stop a loop at all<!-- /nina:why -->. A different issue on the same edge starts its own count. A stage that sends work back names each issue on the `ISSUES` line under its verdict; when you dispatch a round — the fix, and the check of the fix — copy that line into both dispatches, so the stage that checks can keep the id of an issue that is still open. That id is what your count is of.
|
|
25
|
+
|
|
26
|
+
Where the project wires the **loop gate** (`scripts/loop-gate.mjs`, run by hooks — `nina wire` puts them
|
|
27
|
+
in place), the cap is held for you. It counts a round when a dispatch acts on a loop-back a stage
|
|
28
|
+
declared on its `VERDICT` line — per issue, while every report the loop's rounds act on named its issues
|
|
29
|
+
on the `ISSUES` line, and per edge from the first round one did not until the loop closes; several
|
|
30
|
+
dispatches acting on the same verdicts are one round; a review that saw the fix and passed closes the
|
|
31
|
+
loop, while a sibling that approved alongside a rejection releases nothing; and {{OWNER}}'s next message
|
|
32
|
+
starts every count over. The dispatch past the cap goes to {{OWNER}} to confirm. If they refuse it, do
|
|
33
|
+
what the graph says — hand them each round's report and ask how to proceed — and do not route around the
|
|
34
|
+
refusal by resuming the fixer or making the fix yourself.
|
|
35
|
+
|
|
36
|
+
The gate trusts the ids it is given: an issue renamed between rounds starts its count over, so an edge
|
|
37
|
+
still goes to {{OWNER}} once it has gone round more than twice its cap with no approval between. It does
|
|
38
|
+
not see a fix you make without a subagent — so keep your own count as well, gate or no gate. When you
|
|
39
|
+
dispatch a second round on an edge, say "round 2 of max 2 on <edge>" in the dispatch itself, so the
|
|
40
|
+
count is in the transcript and the next reader of it — you after a compaction, or `nina stats` — can see
|
|
41
|
+
it.
|
|
42
|
+
|
|
43
|
+
### Milestone gate
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
… last sub-step: → reviewer → qa ─┬─▶ devops (preview deploy + smoke) ─┐
|
|
47
|
+
│ ├─▶ milestone done
|
|
48
|
+
(entire spec-SET like 6.* ) ───┴─▶ secops (whole-set audit) ────────┘
|
|
49
|
+
│
|
|
50
|
+
└─ BLOCKED (CRITICAL/HIGH) → architect / implementer → re-audit
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**secops** is a MILESTONE gate, not a per-sub-step stage. When the LAST sub-step of a numbered set (`6.*`) or phase (one package's build-out) passes qa, dispatch **secops** to audit the whole assembled surface for cross-cutting security/privacy gaps. The set is not "done" until secops returns `SECURE`. Do NOT run secops per sub-step — only at set boundaries.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Rules
|
|
58
|
+
<!-- nina:slot db.1 -->
|
|
59
|
+
<!-- nina:slot integrations.1 -->
|
|
60
|
+
<!-- nina:slot blockchain.1 -->
|
|
61
|
+
|
|
62
|
+
### Planner only for ambiguous, multi-step, or multi-package work
|
|
63
|
+
If the task fits in one head and lives in a single package, skip to architect (or implementer for trivial things). Don't dispatch planner for "change the button color" or "add a `label` field to a model."
|
|
64
|
+
One package is not one step, though: the architect still splits a long file list into steps (see *A spec's steps are passes of their own*).
|
|
65
|
+
|
|
66
|
+
### Architect output is a spec, not code
|
|
67
|
+
Architect produces a TS spec; implementer consumes the spec; they do not re-read the original user message.
|
|
68
|
+
|
|
69
|
+
### A spec is corrected in place
|
|
70
|
+
A spec sent back is corrected where it is wrong; new work comes as a new step file or spec. Never ask for
|
|
71
|
+
a `Revision N` section, and correct a spec yourself only in place, searching it and its steps for what you contradict. Point a dispatch at the sections and steps
|
|
72
|
+
that changed, by number: a history at a spec's head is read first by every stage and applies to
|
|
73
|
+
nothing.<!-- nina:why --> The architect was told to correct in place, and the
|
|
74
|
+
orchestrator went on asking for revisions. The first new project's second spike spec opened with four
|
|
75
|
+
revision sections, one of them written by the orchestrator, and every dispatch told the stage to read them
|
|
76
|
+
first. Its first spec reached an eighth revision, which had become the way new work was asked for.<!-- /nina:why -->
|
|
77
|
+
|
|
78
|
+
### Reviewer audits; QA runs tests
|
|
79
|
+
Reviewer runs `{{TYPECHECK_CMD}}` / `{{LINT_CMD}}` (and `{{BUILD_CMD}}` for frontend) and verifies clean, confirms guardrails ran, but **does not run vitest**. QA runs vitest once after approval.
|
|
80
|
+
|
|
81
|
+
### Pipeline is not sacred
|
|
82
|
+
If reviewer finds a design flaw, loop back to the architect. Don't paper over with implementation hacks.
|
|
83
|
+
|
|
84
|
+
### Plugin skills are part of the pipeline
|
|
85
|
+
<!-- nina:slot edge-cf.7 -->
|
|
86
|
+
Retrieval-first skills registered for this stack are inherited by every subagent — invoke via the `Skill` tool. Mandatory triggers (mirrors CLAUDE.md):
|
|
87
|
+
|
|
88
|
+
| Diff touches… | Skill |
|
|
89
|
+
|---|---|
|
|
90
|
+
<!-- nina:slot edge-cf.1 -->
|
|
91
|
+
<!-- nina:slot edge-cf.2 -->
|
|
92
|
+
<!-- nina:slot edge-cf.3 -->
|
|
93
|
+
<!-- nina:slot edge-cf.4 -->
|
|
94
|
+
<!-- nina:slot frontend.1 -->
|
|
95
|
+
| **secops** auditing any milestone | **`security-audit`** (guidance mode — full-audit mode only on an explicit whole-tree request) |
|
|
96
|
+
<!-- nina:slot edge-cf.5 -->
|
|
97
|
+
<!-- nina:slot db.2 -->
|
|
98
|
+
<!-- nina:slot db.3 -->
|
|
99
|
+
<!-- nina:slot blockchain.2 -->
|
|
100
|
+
|
|
101
|
+
The architect cites which skill informed the spec. The reviewer rejects a spec touching a surface with a mandatory skill that doesn't cite one OR justify why it wasn't needed. Same rigor as the `node_modules:<line>` premise rule.
|
|
102
|
+
<!-- nina:slot edge-cf.6 -->
|
|
103
|
+
|
|
104
|
+
### Devops owns the deploy
|
|
105
|
+
Invoke **devops** after **qa PASS** on any step that changes a deployed surface (API, frontend, schema, deploy config, secrets, platform bindings). It is the stage that executes Hard Rule #14 — the reviewer only checks that the spec *has* a preview-deploy plan. Skip it for steps that touch only tests, docs, or the harness. **Preview and staging it deploys on its own; production needs an explicit go from {{OWNER}} for that specific change.**
|
|
106
|
+
<!-- nina:slot frontend.2 -->
|
|
107
|
+
|
|
108
|
+
### Look at every loop-back for a lesson
|
|
109
|
+
|
|
110
|
+
When a stage loops back (qa → implementer on a test failure, reviewer rejects a diff, a gate blocks) **or** the user corrects something, the orchestrator asks one question: **would this happen again?** If it would, the lesson goes into a pill under `.claude/pills/<role>/` (or `shared/` if it spans roles) so the responsible agent does not repeat it. If it would not — a typo, a flake, a one-off — say so in one line and move on. The question is not optional; the pill is its answer when the answer is yes. <!-- nina:why -->The rule used to be "a pill on every loop-back", and it was followed 3% of the time. <!-- /nina:why -->A rule that demands a lesson from a typo trains everyone to skip the ones that were lessons. `harness:check` watches the outcome<!-- nina:why --> instead of the ritual<!-- /nina:why --> — a role sent back three times since its newest lesson is reported every turn until one is written (`nina learn`). Skip it only if the lesson is really a code convention (→ `patterns.md`/`CLAUDE.md`) or a library premise (→ `integrations/<lib>.md`) — those surfaces own it, and a recurring pill should eventually **graduate** there and be marked `retired`. If the lesson already has a pill, do **not** write a second one — increment that pill's `occurrences` and set `last_seen` to today — the counter that decides when a correction has recurred often enough to graduate into a rule. At three, `harness:check` sends it to the harness on its own; commit the request it writes together with the pill. Each subagent already reads its own pills before acting, and `nina pills` checks that what was written is well formed and filed where its audience will actually read it (see `.claude/pills/README.md`).
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Parallelization
|
|
115
|
+
|
|
116
|
+
**The rule that decides everything here: a stage that only reads can always run beside another
|
|
117
|
+
stage that only reads. A stage that writes files owns those files alone.** Most of this pipeline is
|
|
118
|
+
read-only, so most of it can overlap — the default of running every stage nose-to-tail is a
|
|
119
|
+
habit, not a constraint.
|
|
120
|
+
|
|
121
|
+
Dispatch concurrent agents **in a single message with multiple Agent tool calls**. Separate messages
|
|
122
|
+
run them one after another and buy nothing.
|
|
123
|
+
|
|
124
|
+
### Run these in parallel
|
|
125
|
+
|
|
126
|
+
| Together | Why it is safe | What it buys |
|
|
127
|
+
|---|---|---|
|
|
128
|
+
| **`reviewer` ∥ every gate the diff triggered** after the implementer | all read-only + Bash | the gates stop being a serial prefix to the review |
|
|
129
|
+
| **`reviewer` fanned out by dimension** — one per axis of risk the diff carries, such as tenant isolation, patterns and spec-scope | read-only; they never touch the same output | **the biggest single win.** One reviewer carrying ~15 checklists over a 500-line diff misses things; three narrow ones do not. Faster *and* better |
|
|
130
|
+
| **`architect` across the sibling specs of one milestone** (`<feature>-spec1..N`) | each writes its own file under `.claude/plans/specs/` | the specs share context, so designing them together is more coherent than one-at-a-time, and the whole milestone is specced in one pass |
|
|
131
|
+
| **`secops` ∥ `qa`** at the end of a milestone | secops is read-only by definition | removes the audit from the critical path |
|
|
132
|
+
| **`devops` ∥ `secops`** at the end of a milestone | devops only reads code; what it writes is a deploy target, not the tree | the audit and the preview deploy stop being sequential |
|
|
133
|
+
| **`Explore` fan-out** for "where does X live" | read-only | one search instead of every stage re-grepping the tree |
|
|
134
|
+
|
|
135
|
+
### Implementers side by side
|
|
136
|
+
|
|
137
|
+
Two implementers write at once only on work that shares no file and builds on nothing the other writes:
|
|
138
|
+
the planner's `PARALLEL-SAFE`, a step's `Builds on` and the binding file lists say which. Where decides how:
|
|
139
|
+
|
|
140
|
+
- **In different packages, each checked on its own** (typecheck, lint and build scoped to it, as
|
|
141
|
+
`{{API_DIR}}` and `{{APP_DIR}}` are): side by side in this checkout.
|
|
142
|
+
- **In one package, or where a check spans packages:** each with **`isolation: "worktree"`**. A worktree
|
|
143
|
+
holds only what is committed, so the work they build on is committed first (else ask {{OWNER}} for the
|
|
144
|
+
commit, or run them in turn), and lacks what git does not track, such as dependencies or a virtual
|
|
145
|
+
environment, until its implementer sets it up.
|
|
146
|
+
- You merge a worktree's work, in step order. Never let two agents merge.<!-- nina:why --> The rule
|
|
147
|
+
used to be one implementer per package, always in a worktree. The first new project had no commit for a
|
|
148
|
+
worktree to hold, so it ran two implementers side by side in one checkout, one per spike directory, and
|
|
149
|
+
neither disturbed the other. It also ran two steps of one spec one after the other that built on the
|
|
150
|
+
same two steps and nothing else, only because they shared a package and both edited its README.<!-- /nina:why -->
|
|
151
|
+
|
|
152
|
+
### A spec's steps are passes of their own
|
|
153
|
+
|
|
154
|
+
When the architect splits a spec into steps, each step is its own pass from the implementer on: an
|
|
155
|
+
implementer for that step alone, then the reviewer and every gate its diff triggers, then qa. A step's
|
|
156
|
+
implementer goes out only once every step it builds on has closed its pass, so no step is built on one
|
|
157
|
+
that was sent back. Steps whose turn comes together and share no file go out together, as § *Implementers side by side*
|
|
158
|
+
says; their reviews go out together too, and their qa runs one after the other. Parallel steps save
|
|
159
|
+
time, not tokens: each run builds its own context, and a step that builds on another is never one of
|
|
160
|
+
them. Never hand one implementer several steps, or a spec that lists more than {{STEP_FILES}} files with no
|
|
161
|
+
steps: that spec goes back to the architect to be split. `ONE-SPEC` and `ONE-REVIEW` are the planner's
|
|
162
|
+
groupings of sibling steps, and never merge an architect's steps into one run.
|
|
163
|
+
|
|
164
|
+
### Keep these serial
|
|
165
|
+
|
|
166
|
+
- **`qa`.** Vitest is ~2–3 GB per worker; concurrent invocations take the machine down. One run, at
|
|
167
|
+
the end of each pass. This is not negotiable and is not a speed problem — the suites are seconds, except the
|
|
168
|
+
`{{API_DIR}}` integration suite, which is slow for its own reasons (real DB).
|
|
169
|
+
- **The merge**, always.
|
|
170
|
+
- **A loop-back.** When a stage rejects, fix and re-run that stage; do not fan out around a failure.
|
|
171
|
+
|
|
172
|
+
### Right-sizing beats parallelism
|
|
173
|
+
|
|
174
|
+
Before parallelizing a chain, ask whether the chain should be that long at all. The
|
|
175
|
+
proportional-dispatch table above is worth more than any fan-out: a display change that takes
|
|
176
|
+
`implementer → reviewer` is already faster than the same change parallelized across five stages.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## What NOT to use subagents for
|
|
181
|
+
|
|
182
|
+
- Trivial edits (rename variable, fix typo, add a log line).
|
|
183
|
+
- Reading code to answer a user question.
|
|
184
|
+
- Running a single command.
|
|
185
|
+
- Pure research / exploration.
|
|
186
|
+
|
|
187
|
+
The subagent overhead isn't worth it for sub-5-minute tasks. Do it directly.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Examples
|
|
192
|
+
|
|
193
|
+
| User request | Pipeline |
|
|
194
|
+
|---|---|
|
|
195
|
+
<!-- nina:slot project.5 dispatch-examples -->
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Quick triage
|
|
200
|
+
|
|
201
|
+
When dispatch is ambiguous, escalate one notch (heavier chain). The cost of an extra stage is low; the cost of skipping a gate on a critical path is a production incident.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<!-- nina:requires integrations -->
|
|
2
|
+
# <Name> — premises
|
|
3
|
+
|
|
4
|
+
> One integration, one doc. The **integration-tester** creates it on first contact with the
|
|
5
|
+
> dependency and appends to it whenever it observes behavior this file does not yet carry.
|
|
6
|
+
> Everything below is evidence. A claim you cannot cite belongs in *Open questions*, not in a
|
|
7
|
+
> premise — a premise that turns out to be a guess is how an integration regression ships.
|
|
8
|
+
|
|
9
|
+
- **Kind:** `installed-library` · `live-api` · `platform-binding` — pick one; it decides what counts as evidence.
|
|
10
|
+
- **Boundary:** the one module that speaks to this dependency. Nothing else may.
|
|
11
|
+
- **Skill:** the installed skill covering it, or *none* — do not invent one.
|
|
12
|
+
|
|
13
|
+
## Premise index
|
|
14
|
+
|
|
15
|
+
Read this table and the sections your task actually cites. Not the whole file.
|
|
16
|
+
|
|
17
|
+
| Premise | Evidence | Claim |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| P1 | `<citation>` | one sentence |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## P1 — <the claim, as a sentence>
|
|
24
|
+
|
|
25
|
+
**Claim.** What the dependency does, in one sentence, stated specifically enough to be wrong.
|
|
26
|
+
|
|
27
|
+
**Evidence.** The form depends on the kind:
|
|
28
|
+
|
|
29
|
+
- `installed-library` — `node_modules/.pnpm/<lib>@<version>/.../<file>:<line>`, with the lines that
|
|
30
|
+
prove it quoted. From the version actually installed here, not the latest release.
|
|
31
|
+
- `live-api` — the request sent and the response received, verbatim, or the contract-test case that
|
|
32
|
+
pins it. The vendor's documentation is not evidence: it describes what they intend, and you are
|
|
33
|
+
depending on what they do.
|
|
34
|
+
- `platform-binding` — what the local emulator did when driven, plus the platform's installed types.
|
|
35
|
+
|
|
36
|
+
**Why it matters here.** Which code depends on this being true, and what breaks if it is not.
|
|
37
|
+
|
|
38
|
+
**Checked.** `<date>` against `<version / environment>`.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Open questions
|
|
43
|
+
|
|
44
|
+
Behavior this doc does not settle yet, and what would settle it. A question here is honest work;
|
|
45
|
+
the same uncertainty written as a premise is a defect waiting for a deploy.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<!-- nina:slot project.1 title -->
|
|
2
|
+
|
|
3
|
+
<!-- nina:slot project.2 mission -->
|
|
4
|
+
|
|
5
|
+
<!-- nina:slot project.3 stack -->
|
|
6
|
+
<!-- nina:slot edge-cf.1 -->
|
|
7
|
+
|
|
8
|
+
> **A subagent can only invoke a skill if `Skill` is in its `tools:` list.** That field is an allowlist, not a hint.<!-- nina:why --> An earlier version of this harness omitted `Skill` on every role, so every mandatory-skill rule was unenforceable, and the skills were measured at 2 invocations in 743 runs — both from built-in agents that carry the full tool set.<!-- /nina:why --> Any new role that binds a skill must grant `Skill`. The `tools:` field is read when the agent is registered, so a change to it needs a new session; the spec body is re-read on every dispatch.
|
|
9
|
+
|
|
10
|
+
Mandatory triggers:
|
|
11
|
+
|
|
12
|
+
| When you are about to… | Invoke skill | Why |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
<!-- nina:slot edge-cf.2 -->
|
|
15
|
+
<!-- nina:slot edge-cf.3 -->
|
|
16
|
+
<!-- nina:slot edge-cf.4 -->
|
|
17
|
+
<!-- nina:slot edge-cf.5 -->
|
|
18
|
+
<!-- nina:slot frontend.1 -->
|
|
19
|
+
| Run the **secops** milestone gate | `security-audit` | the attack-class corpus; guidance mode — its full six-phase workflow is for an explicit whole-tree audit only |
|
|
20
|
+
<!-- nina:slot edge-cf.6 -->
|
|
21
|
+
<!-- nina:slot db.1 -->
|
|
22
|
+
<!-- nina:slot db.2 -->
|
|
23
|
+
|
|
24
|
+
**Each agent spec now carries its own binding table** (`.claude/agents/<role>.md` § *Skills you MUST consult*), because a rule that lives only here was invoked 2 times in 743 subagent runs. `qa` and `planner` have no skill bound — none of the installed skills covers vitest or decomposition, and an invented binding is worse than none.
|
|
25
|
+
<!-- nina:slot edge-cf.7 -->
|
|
26
|
+
|
|
27
|
+
Reviewer rejects any architect spec that touches the platform runtime or its deploy config without citing which skill it consulted, OR explaining why the skill was not needed. Same standard as the premise rule below: retrieval beats recall.
|
|
28
|
+
|
|
29
|
+
## Where things live
|
|
30
|
+
- Full architecture → `.claude/architecture.md`
|
|
31
|
+
- Code conventions → `.claude/patterns.md`
|
|
32
|
+
- Pipeline graph — stages, edges, loop caps → `.claude/graph.md`
|
|
33
|
+
- Agent dispatch rules → `.claude/router.md`
|
|
34
|
+
- Which docs to load per task → `.claude/retrieval.md`
|
|
35
|
+
- Agent pipeline overview → `.claude/agents-overview.md`
|
|
36
|
+
- Stage contracts + examples → `.claude/pipeline.md`
|
|
37
|
+
- Subagent role specs → `.claude/agents/` (dispatchable via Agent tool)
|
|
38
|
+
<!-- nina:slot project.4 product-docs -->
|
|
39
|
+
|
|
40
|
+
**Generated files — edit the layer, not the output.** A file that opens with a
|
|
41
|
+
`<!-- nina:generated -->` comment was composed by `nina compose` from the harness core, the
|
|
42
|
+
surfaces this project declares in `.nina/profile.json`, and this project's own fragments in
|
|
43
|
+
`.nina/project/tree/`. Most of the docs above open with one, and so does this file. The
|
|
44
|
+
comment names the layer to edit instead — or says the text belongs to the harness and cannot
|
|
45
|
+
be changed from this project, in which case **report it rather than editing it**. A hand edit
|
|
46
|
+
is reported as drift by `pnpm harness:check` and overwritten by the next compose, which is
|
|
47
|
+
how a correction written in the wrong place disappears without anyone being told.
|
|
48
|
+
|
|
49
|
+
**The rule is the marker, not the directory.** Most of `.claude/` is yours to write and is
|
|
50
|
+
never composed: `architecture.md`, the code map, `integrations/*`, everything under
|
|
51
|
+
`plans/**`, and the pills under `pills/<role>/`. Do not stop writing those.
|
|
52
|
+
|
|
53
|
+
**A generated line you cannot fix here is a change made upstream.** The harness lives in its own
|
|
54
|
+
repository; this project composes a *frozen* version of it, pinned as `core` in `.nina/profile.json`.
|
|
55
|
+
So a wrong composed line has one of two addresses, and saying which is part of reporting it. **This
|
|
56
|
+
project's own fact** — a path, a provider, a duration, a package name — belongs in a fragment under
|
|
57
|
+
`.nina/project/tree/`, and you can fix it now. **The harness's rule** — what a role must do, what a
|
|
58
|
+
stage hands off, what a report must contain — cannot be changed from this project at all: report it as
|
|
59
|
+
a request against the pinned version, quoting the composed file and line, and it arrives back through
|
|
60
|
+
`nina upgrade`. A correction that names no layer is not actionable by anyone, however right it is.
|
|
61
|
+
|
|
62
|
+
## ⚠️ Agent pipeline — MANDATORY
|
|
63
|
+
|
|
64
|
+
> **Dispatch the subagent chain that fits the task's size and blast radius — before executing anything non-trivial.**
|
|
65
|
+
> The pipeline is **proportional, not one-size-fits-all.** A change on a **critical path** (below) and a substantial multi-file feature get the full chain. Small read-only / display / UI-copy changes get a light chain — or a direct edit. **Under-gating a change on a critical path is a protocol violation. Over-gating a one-file label or list change wastes hours — that is also a failure.**
|
|
66
|
+
|
|
67
|
+
**Critical paths.** A change that touches one is gated in full, and when it is unclear whether a change touches one, it does:
|
|
68
|
+
|
|
69
|
+
- **auth**: how a credential is issued, checked or revoked, and who may do what
|
|
70
|
+
<!-- nina:slot db.8 -->
|
|
71
|
+
<!-- nina:slot integrations.7 -->
|
|
72
|
+
<!-- nina:slot money.4 -->
|
|
73
|
+
<!-- nina:slot pii.2 -->
|
|
74
|
+
<!-- nina:slot blockchain.4 -->
|
|
75
|
+
|
|
76
|
+
### Procedure — follow this every time
|
|
77
|
+
|
|
78
|
+
1. **Classify the task** using the table below *before* touching any file.
|
|
79
|
+
2. **Dispatch the first subagent** in the required chain via the Agent tool.
|
|
80
|
+
3. Each stage hands off a written artifact (spec / diff / review) to the next.
|
|
81
|
+
4. **Run the read-only stages concurrently.** A stage that only reads can run beside any other stage that only reads, so the `reviewer` and every gate the diff triggers go out together after the implementer — in ONE message with multiple Agent calls — and `secops` runs alongside `qa` at a milestone's end. For a diff over ~200 lines, fan the reviewer out by dimension — one reviewer per axis of risk the diff actually carries — instead of asking one agent to carry every checklist. Concurrent **implementers** are the exception: they write, so they run together only on work that shares no file and builds on nothing the other writes — side by side in different packages, each in `isolation: "worktree"` within one. `qa` is always alone (vitest memory). Full rules: `.claude/router.md` § *Parallelization*.
|
|
82
|
+
5. The **reviewer** must verify every required stage ran — including the database gate if the schema or a query was touched — before approving.
|
|
83
|
+
6. **Honor the planner's `ONE-SPEC` / `ONE-REVIEW` grouping.** Sibling steps that change no observable behavior on their own get **one** architect spec and **one** reviewer pass over the combined diff, while still being implemented one step at a time. Small steps are good; eight full pipelines to ship one feature are not. This never relaxes a gate — the gates in `.claude/graph.md` fire on the surface touched, however the specs were grouped.
|
|
84
|
+
7. **Right-size before dispatching.** Match the chain to blast radius (see table). A change that touches no critical path and ≤2 files takes the light chain — or a direct edit when a subagent adds nothing (a label, a copy tweak, a list render). Do NOT run planner/architect/secops on that. The gates on a critical path are never optional.
|
|
85
|
+
8. **At the END of a spec-SET / milestone** (e.g. all of `6.*`), after the last sub-step's qa, dispatch **secops**. The milestone is not done until secops returns `SECURE`. This is a milestone gate, NOT a per-sub-step stage. **Scope secops to the milestone's diff by default.** A whole-tree sweep surfaces pre-existing debt mid-task and causes scope-creep — run it ONLY when explicitly requested, as a deliberate choice, never as the default.
|
|
86
|
+
|
|
87
|
+
### Required chain by task shape
|
|
88
|
+
|
|
89
|
+
| Task shape | Required dispatch |
|
|
90
|
+
|---|---|
|
|
91
|
+
| Question / exploration / Q&A | none — answer directly |
|
|
92
|
+
| Trivial edit (rename, typo, 1-line log fix) | none — edit directly |
|
|
93
|
+
| **Small read-only / display / UI-copy change** (≤2 files, no critical path) | **implementer → reviewer** — or a direct edit if a subagent adds no value; qa runs ONLY the affected test file. No planner, no secops. |
|
|
94
|
+
| Single-file bug fix (TS) | **implementer → reviewer → qa** |
|
|
95
|
+
| Refactor TS (no new behavior) | **architect → implementer → reviewer → qa** |
|
|
96
|
+
| New TS feature / multi-file change (a critical path, or ≥3 files with logic) | **planner → architect → implementer → reviewer → qa** |
|
|
97
|
+
<!-- nina:slot db.3 -->
|
|
98
|
+
<!-- nina:slot integrations.1 -->
|
|
99
|
+
<!-- nina:slot blockchain.2 -->
|
|
100
|
+
| **ANY step that changes a deployed surface** (API, frontend, schema, deploy config, secrets, bindings) | **+ devops after qa** — preview deploy + smoke; prod only on an explicit go |
|
|
101
|
+
<!-- nina:slot frontend.2 -->
|
|
102
|
+
| **END of a spec-SET / milestone** (a numbered set like `6.*`, or a phase like one package's build-out) | **+ secops after the last sub-step's qa** — milestone security gate; `SECURE` required before the milestone is declared done |
|
|
103
|
+
|
|
104
|
+
**Test execution policy:** vitest is **only** invoked by the **qa** stage, which runs once at the end of the pipeline (after reviewer approves). Implementer writes tests but does NOT execute them. Reviewer does NOT run tests. This prevents the workspace-machine memory blowups from concurrent vitest invocations (vitest is ~2–3 GB per worker). Configs enforce single-fork; the root `pnpm test` uses `turbo run test --concurrency=1`.
|
|
105
|
+
|
|
106
|
+
**Test scoping (speed — this is the single biggest time sink).** qa runs ONLY the test files / packages the diff actually touches during the build→fix loop — never the full suite per cycle. Run it targeted per file while iterating; run the **full** package suite exactly ONCE, right before opening the PR / merging, as the final gate. Vitest stays single-fork and non-concurrent.
|
|
107
|
+
|
|
108
|
+
<!-- nina:slot project.8 slow-suites -->
|
|
109
|
+
|
|
110
|
+
### If you are uncertain which chain applies
|
|
111
|
+
→ If the doubt is whether a **critical path** is touched → treat it as the heavier shape and gate it. If the task clearly touches no critical path and the only question is "how much ceremony" → take the **lighter** chain; an extra planner/secops pass on a display change costs hours, not minutes.
|
|
112
|
+
|
|
113
|
+
<!-- nina:slot project.5 roles-heading -->
|
|
114
|
+
|
|
115
|
+
- **planner** → decompose ambiguous or multi-step tasks into ordered subtasks
|
|
116
|
+
- **architect** → design the TS technical approach AND cite external-library premises (`node_modules/.pnpm/<lib>/.../<file>:<line>`); write a spec before code exists
|
|
117
|
+
- **implementer** → write TS code AND test files strictly to spec; runs typecheck/lint/build only (NOT vitest)
|
|
118
|
+
<!-- nina:slot db.4 -->
|
|
119
|
+
<!-- nina:slot integrations.3 -->
|
|
120
|
+
<!-- nina:slot blockchain.3 -->
|
|
121
|
+
- **reviewer** → verify diff matches spec, run typecheck/lint/build (NOT vitest), confirm every gate the diff triggered ran, confirm spec has a preview-deploy plan
|
|
122
|
+
- **qa** → runs vitest once at the END of the pipeline (after reviewer approves) for the affected packages; loops back to implementer on failures
|
|
123
|
+
- **devops** → **deploy owner.** Runs after **qa PASS** on any step that changes a deployed surface. Deploys to preview or staging first; production needs an explicit go. Smokes the deployed preview for a blank page and a clean console — judging whether the screen is *right* is the reviewer's job, not his. Executes Hard Rule #14: clean build, every deploy target, build-time env, migrations in order against the right DB, secret parity, **smoke against preview**, named rollback. Deploys preview/staging on its own; **a production deploy requires an explicit go from {{OWNER}} for that change.** Read-only on code — a failed deploy caused by bad code loops back, it does not get patched here.
|
|
124
|
+
- **secops** → **milestone security gate.** Runs once at the END of a completed spec-SET (a numbered milestone like `6.*`, or a phase like one package's build-out), after the last sub-step's qa — NOT per sub-step. Audits as an attacker and a privacy auditor (auth/session, tenant isolation, secrets and config exposure, PII leakage, abuse of any irreversible operation and its idempotency seams, injection/SSRF, dependency and binding posture). **Scoped to the milestone diff by default; a whole-tree sweep runs only on explicit request** (it surfaces pre-existing debt mid-task → scope-creep). Read-only; CRITICAL/HIGH findings in the milestone's own surface BLOCK it and loop back until re-audited.
|
|
125
|
+
|
|
126
|
+
<!-- nina:slot project.6 doc-pointers -->
|
|
127
|
+
|
|
128
|
+
## Hard rules (non-negotiable)
|
|
129
|
+
|
|
130
|
+
A rule keeps its number in every project, because other documents cite it as `Hard Rule #N`. A rule this project does not have leaves a gap in the numbers — it was not lost, and the list is not renumbered.
|
|
131
|
+
|
|
132
|
+
1. **Run the subagent chain proportional to the task (see the pipeline table).** Under-gating a change on a critical path is a protocol violation — those gates are never skipped, and when uncertain whether one is touched, gate it. But small read-only / display / UI-copy changes (≤2 files, no critical path) take the light chain or a direct edit — over-gating them wastes hours and is also a failure.
|
|
133
|
+
<!-- nina:slot db.5 -->
|
|
134
|
+
<!-- nina:slot money.1 -->
|
|
135
|
+
<!-- nina:slot money.2 -->
|
|
136
|
+
<!-- nina:slot money.3 -->
|
|
137
|
+
<!-- nina:slot db.6 -->
|
|
138
|
+
<!-- nina:slot db.7 -->
|
|
139
|
+
<!-- nina:slot pii.1 -->
|
|
140
|
+
9. **TSDoc is mandatory on every TS declaration** (function, type, interface, class, method, enum) — exported or not, and members are declarations: interface and type fields, class members, constructors and object-literal methods. Enforced in review, by a sweep of every file touched, not a sample.
|
|
141
|
+
<!-- nina:slot edge-cf.8 -->
|
|
142
|
+
<!-- nina:slot integrations.4 -->
|
|
143
|
+
<!-- nina:slot integrations.5 -->
|
|
144
|
+
<!-- nina:slot integrations.6 -->
|
|
145
|
+
14. **Preview-first deploy. Smoke runs against preview, NEVER prod first.** The **devops** stage owns executing this; the reviewer only verifies the plan exists. Any change that culminates in a production deploy must name a preview URL (a preview deployment for `{{APP_DIR}}`; a staging route for the API) in the architect's spec. Smoke runs against preview FIRST. A spec without a preview-deploy plan is rejected by the reviewer.
|
|
146
|
+
15. **Relative imports use the `.js` extension in `.ts`/`.tsx` source — this is intentional ESM, NOT a mistake.** `import { x } from './client.js'` (never `'./client'` or `'./client.ts'`). TypeScript does not rewrite specifiers; emitting packages ({{EMITTING_PKGS}}) ship real `dist/*.js`, so the `.js` form is the only one that resolves at runtime, and the bundlers map it back to `.ts` source transparently. Stripping or `.ts`-ifying the extension breaks the build — do not "fix" it. Full rationale: `.claude/patterns.md` § *Relative imports carry a `.js` extension*.
|
|
147
|
+
<!-- nina:slot blockchain.1 -->
|
|
148
|
+
17. **Every artifact you are handed is a hypothesis; the tree is the truth.** A file list, a line range, a test filter, a cited premise — each was derived by an earlier stage from a tree that may have moved since, so **the part you are about to rely on** is checked against the tree before you act on it. Where it diverges, **report the divergence, naming the stage that produced it**: a stage that quietly works around a wrong input hides the defect from the only stage that can fix it, and the next task inherits it intact. Acting on what the tree says **never widens what you were authorized to touch** — a wrong range inside a file you own is corrected and reported; a divergence that would take you outside your authorized files stops the work and goes back to the stage that drew the boundary. This is not diligence for its own sake. On one milestone three consecutive stages each found the artifact above them wrong — a test filter that covered two of six blocks gated on a live credential, a line range that missed a second block of orphaned mocks, a spec that named the wrong test as the one blocking a removal — and every one of the three found it only by going to look.
|
|
149
|
+
|
|
150
|
+
## Common commands
|
|
151
|
+
|
|
152
|
+
<!-- nina:slot project.7 common-commands -->
|
|
153
|
+
|
|
154
|
+
## When in doubt
|
|
155
|
+
Consult `.claude/retrieval.md` to decide which docs to load before acting.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The edit guard, run by this project's `PreToolUse` hook on the edit tools.
|
|
4
|
+
*
|
|
5
|
+
* A file this harness composes is changed in its layer, never in place: the next compose would
|
|
6
|
+
* overwrite the edit. This refuses an edit to a file carrying the `nina:generated` notice, and the
|
|
7
|
+
* refusal quotes the notice, which names where the change goes. The mechanism lives in the NINA
|
|
8
|
+
* package; this file only says which project it guards. It lets every call through if anything goes
|
|
9
|
+
* wrong.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { dirname, resolve } from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import { runGuard } from '@xhulz/nina/guard';
|
|
15
|
+
|
|
16
|
+
process.exitCode = await runGuard({ root: resolve(dirname(dirname(fileURLToPath(import.meta.url)))) });
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Runs this project's harness drift detectors.
|
|
4
|
+
*
|
|
5
|
+
* The mechanism lives in the NINA package — what counts as drift, what counts as a check
|
|
6
|
+
* that could not run, and how either reaches a Claude Code hook. Only the LIST is here,
|
|
7
|
+
* because only this project knows which detectors it has and what to do when one fires.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node scripts/harness-check.mjs human-readable; exit 1 on drift, 2 on error
|
|
11
|
+
* node scripts/harness-check.mjs --hook JSON for a Stop hook — shown to the person; always exit 0
|
|
12
|
+
* node scripts/harness-check.mjs --context JSON for a UserPromptSubmit hook — read by the model
|
|
13
|
+
* before it answers; always exit 0
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { dirname, resolve } from 'node:path';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
import { runDetectors } from '@xhulz/nina/detectors';
|
|
19
|
+
|
|
20
|
+
const ROOT = resolve(dirname(dirname(fileURLToPath(import.meta.url))));
|
|
21
|
+
|
|
22
|
+
process.exit(
|
|
23
|
+
runDetectors(
|
|
24
|
+
[
|
|
25
|
+
// Every composed project has this one, so the core carries it rather than asking each
|
|
26
|
+
// project to write the same nine lines. It disables itself where the project does not
|
|
27
|
+
// declare the npm script, like any other detector.
|
|
28
|
+
{
|
|
29
|
+
name: 'agent specs',
|
|
30
|
+
declaredBy: 'harness:compose:check',
|
|
31
|
+
bin: 'nina',
|
|
32
|
+
args: ['compose', '--check', '--drift', '--quiet'],
|
|
33
|
+
hint: 'these files are generated — run `nina where <file>` to see which layer owns it, then `nina compose`',
|
|
34
|
+
ignore: /^compose: current$/,
|
|
35
|
+
},
|
|
36
|
+
// What the project has not declared yet: vocabulary, its own layer, the documents the layers
|
|
37
|
+
// read, the wiring. On a new project this is the whole to-do list, and it reaches the model
|
|
38
|
+
// before its first answer — so the first conversation starts by filling it, unasked.
|
|
39
|
+
{
|
|
40
|
+
name: 'declaration',
|
|
41
|
+
bin: 'nina',
|
|
42
|
+
args: ['check', '--detector', '--quiet'],
|
|
43
|
+
hint: 'each line says what is missing — `.nina/TODO.md` lists the project’s own items, and `.nina/BRIEF.md`, where the interview wrote one, says what the project is; fill them, then `nina compose`',
|
|
44
|
+
ignore: /^check: declaration is sound/,
|
|
45
|
+
},
|
|
46
|
+
// The one link of the learning cycle that fails in silence: a role keeps being sent back
|
|
47
|
+
// and nothing is written down. It fires on the event — three loop-backs since that role's
|
|
48
|
+
// newest lesson — not on a rate, so it says something new when it speaks. A lesson learned
|
|
49
|
+
// a third time it sends to the harness itself, and says so once.
|
|
50
|
+
{
|
|
51
|
+
name: 'lessons',
|
|
52
|
+
bin: 'nina',
|
|
53
|
+
args: ['learn', '--check', '--quiet'],
|
|
54
|
+
hint: 'each line above says what it needs — `nina learn` shows the whole cycle',
|
|
55
|
+
ignore: /^learn: current$/,
|
|
56
|
+
},
|
|
57
|
+
// The loop gate holds the caps in .claude/graph.md from this project's hooks, and it lets every
|
|
58
|
+
// call through when it fails — so a gate that stopped working looks exactly like a pipeline with
|
|
59
|
+
// no loops. This asks it, every turn, whether it is wired and would still hold one.
|
|
60
|
+
{
|
|
61
|
+
name: 'loop gate',
|
|
62
|
+
bin: 'nina',
|
|
63
|
+
args: ['gate', '--selftest', '--quiet'],
|
|
64
|
+
hint: 'the caps in .claude/graph.md are not being held — `nina wire --apply` wires the gate; `nina gate --selftest` says what else is wrong',
|
|
65
|
+
ignore: /^gate: current$/,
|
|
66
|
+
},
|
|
67
|
+
<!-- nina:slot project.1 detectors -->
|
|
68
|
+
],
|
|
69
|
+
{ root: ROOT, hook: process.argv.includes('--hook'), context: process.argv.includes('--context') },
|
|
70
|
+
),
|
|
71
|
+
);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The loop gate, run by this project's Claude Code hooks.
|
|
4
|
+
*
|
|
5
|
+
* `.claude/graph.md` caps every loop-back edge. This holds the cap: it records the verdict each stage
|
|
6
|
+
* reports, each dispatch that goes out and each time the owner speaks, and sends the dispatch that would
|
|
7
|
+
* make a round past its edge's cap to the owner to confirm — the graph's `human`. The owner's next
|
|
8
|
+
* message starts every count over. It keeps metadata only, under ~/.nina/gate/.
|
|
9
|
+
*
|
|
10
|
+
* The mechanism lives in the NINA package; this file only says which project it guards. It prints
|
|
11
|
+
* nothing unless it has a decision to report, and it lets every call through if anything goes wrong —
|
|
12
|
+
* `nina gate --selftest`, run by `pnpm harness:check`, is what says so.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { dirname, resolve } from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
import { runGate } from '@xhulz/nina/gate';
|
|
18
|
+
|
|
19
|
+
process.exitCode = await runGate({ root: resolve(dirname(dirname(fileURLToPath(import.meta.url)))) });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ARCHITECT_EFFORT": "xhigh",
|
|
3
|
+
"BUILD_CMD": "pnpm build",
|
|
4
|
+
"DEEP_EFFORT": "high",
|
|
5
|
+
"DEEP_MODEL": "claude-opus-5-5",
|
|
6
|
+
"LINT_CMD": "pnpm lint",
|
|
7
|
+
"STEP_FILES": "15",
|
|
8
|
+
"TEST_CMD": "pnpm test",
|
|
9
|
+
"TYPECHECK_CMD": "pnpm typecheck",
|
|
10
|
+
"WORK_EFFORT": "xhigh",
|
|
11
|
+
"WORK_MODEL": "claude-sonnet-5"
|
|
12
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!-- nina:slot blockchain.1 -->
|
|
2
|
+
| a contract, a proxy, or a contracts library component | **`develop-secure-contracts`** · **`upgrade-solidity-contracts`** when storage moves |
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot blockchain.2 -->
|
|
5
|
+
- **Contract surface (MANDATORY when the spec reaches `{{CONTRACTS_DIR}}`).** Name which contracts are touched and whether each is deployed; for a deployed one, say whether this is an upgrade and what preserves the storage layout. State the access-control model in roles, not in adjectives: who may call each state-changing function, and how that role is granted and revoked. List the adversarial tests the implementation owes — re-entry, the unauthorized caller, the boundary amount, the second call that must fail. A spec that reaches a contract and leaves any of these to the implementer is rejected.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!-- nina:slot blockchain.1 -->
|
|
2
|
+
- **`upgrade-solidity-contracts`** — before deploying a contract or executing a proxy upgrade, to check the storage layout against what is live.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot blockchain.2 -->
|
|
5
|
+
- **A contract deploy needs `solidity-auditor` `APPROVED` for this exact diff**, and its rollback named before it runs — which for a non-upgradeable contract is a redeploy plus a migration of state, not a revert. Say so *before* deploying. A deploy to a network carrying real value needs an explicit go from {{OWNER}} for that change, the same as production.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!-- nina:slot blockchain.1 -->
|
|
2
|
+
- **The solidity-auditor gate ran and returned `APPROVED`** for any diff touching `{{CONTRACTS_DIR}}`, a deploy script, or a contracts library version. Its absence is a blocking finding on its own, whatever the diff looks like to you.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot blockchain.2 -->
|
|
5
|
+
- A contract change written by the implementer rather than **solidity-dev** is rejected on routing alone. The two roles carry different rules about what a defect costs, and the cheaper set is the wrong one here.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<!-- nina:slot blockchain.1 -->
|
|
2
|
+
| **`develop-secure-contracts`** | any library component in the diff — to check the usage against the library's own guidance, not against memory |
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot blockchain.2 -->
|
|
5
|
+
| **`upgrade-solidity-contracts`** | a proxy, an initializer, or a storage layout change |
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot blockchain.3 -->
|
|
8
|
+
| **`security-audit`** | the off-chain half — deploy scripts, key handling, CI, RPC endpoints (guidance mode) |
|
|
9
|
+
|
|
10
|
+
<!-- nina:slot blockchain.4 -->
|
|
11
|
+
- The deployed addresses and role holders in play, where the change targets a live system.
|
|
12
|
+
|
|
13
|
+
<!-- nina:slot blockchain.5 -->
|
|
14
|
+
- **An event for every state change**, and that what it emits matches what was written. Off-chain
|
|
15
|
+
reconstruction is the only history there is, and a wrong event is worse than a missing one.
|
|
16
|
+
|
|
17
|
+
<!-- nina:slot blockchain.6 -->
|
|
18
|
+
- Approve a change to a live contract without stating the upgrade path and naming who can execute it.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!-- nina:slot blockchain.1 -->
|
|
2
|
+
| **`setup-solidity-contracts`** | a new contract project, a dependency install, or import/remapping configuration |
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot blockchain.2 -->
|
|
5
|
+
| **`develop-secure-contracts`** | any library component — token standards, access control, pausing, reentrancy protection |
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot blockchain.3 -->
|
|
8
|
+
| **`upgrade-solidity-contracts`** | a proxy, an initializer, or anything that moves storage |
|
|
9
|
+
|
|
10
|
+
<!-- nina:slot blockchain.4 -->
|
|
11
|
+
- The contract's deployed state, where one exists. A change to a live contract is an upgrade with a
|
|
12
|
+
storage layout to preserve, not an edit.
|
|
13
|
+
|
|
14
|
+
<!-- nina:slot blockchain.5 -->
|
|
15
|
+
- Widen a function's visibility, or add an address to a role, to make a test pass. The test is
|
|
16
|
+
describing the access control; fix the test or reject the spec.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<!-- nina:slot blockchain.1 -->
|
|
2
|
+
- **Contract guardrail** → deployed code is immutable and permanently callable by anyone, so a defect is not a patch away and "nothing calls this" is not a property of it. Reentrancy, a state-changing function with no access control, a storage layout broken by an upgrade, and an audited component used against its own guidance are caught here.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot blockchain.2 -->
|
|
5
|
+
| **solidity-dev** | Write contract sources and their adversarial tests to spec; never deploys | `.claude/agents/solidity-dev.md` |
|
|
6
|
+
| **solidity-auditor** | Mandatory gate on any contract diff; blocks reviewer and devops | `.claude/agents/solidity-auditor.md` |
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!-- nina:slot blockchain.1 -->
|
|
2
|
+
- `solidity-dev` — writes contract code, which cannot be changed once it is live
|
|
3
|
+
- `solidity-auditor` — gate: audits contract code as an attacker before anything deploys
|
|
4
|
+
|
|
5
|
+
<!-- nina:slot blockchain.2 -->
|
|
6
|
+
- `architect` → `solidity-dev` on `SPEC-READY` — the spec changes contract code
|
|
7
|
+
- `solidity-dev` → `solidity-auditor` on `DIFF-READY`
|
|
8
|
+
- `solidity-dev` → `architect` on `BLOCKED` — the change cannot be made safely as specified · max 2
|
|
9
|
+
- `solidity-auditor` → `reviewer` on `APPROVED`
|
|
10
|
+
- `solidity-auditor` → `solidity-dev` on `REJECTED` — a flaw in the contract code · max 2
|
|
11
|
+
- `solidity-auditor` → `architect` on `REJECTED` — a flaw in the design · max 2
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<!-- nina:slot blockchain.1 -->
|
|
2
|
+
|
|
3
|
+
### Solidity-auditor is a guardrail for contract changes
|
|
4
|
+
Invoke **solidity-auditor** on any diff that touches `{{CONTRACTS_DIR}}`, a deploy script, or the version of a contracts library — regardless of where you are in the pipeline. No contract change merges without its approval, and none deploys without it. Reviewer verifies it ran; devops verifies it approved.
|
|
5
|
+
|
|
6
|
+
Contract sources are written by **solidity-dev**, not the implementer. Route the work there when the spec's file list reaches `{{CONTRACTS_DIR}}`, and keep the implementer on the off-chain side of the same feature.
|
|
7
|
+
|
|
8
|
+
<!-- nina:slot blockchain.2 -->
|
|
9
|
+
| a contract source, a proxy, or a contracts library component | **`develop-secure-contracts`**, plus **`upgrade-solidity-contracts`** when storage moves |
|