create-agent-rig 0.4.0 → 0.5.0
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/CHANGELOG.md +207 -8
- package/README.md +50 -17
- package/package.json +2 -1
- package/packages/cli/dist/commands/create.js +8 -3
- package/packages/cli/dist/commands/init.js +82 -34
- package/packages/cli/dist/commands/upgrade.js +112 -30
- package/packages/cli/dist/index.js +38 -14
- package/packages/cli/dist/lib/copy-tree.js +35 -6
- package/packages/cli/dist/lib/init-settings.js +12 -0
- package/packages/cli/dist/lib/install-set.js +6 -8
- package/packages/cli/dist/lib/manifest.js +21 -9
- package/packages/cli/dist/lib/safe-path.js +30 -0
- package/templates/agent-os/init/AGENTS.md +191 -0
- package/templates/agent-os/init/CLAUDE.md +61 -9
- package/templates/agent-os/stack/aws-cdk/.agents/skills/post-deploy-verify/SKILL.md +98 -0
- package/templates/agent-os/stack/aws-cdk/.agents/skills/ro-debug/SKILL.md +117 -0
- package/templates/agent-os/stack/aws-cdk/.claude/agents/cdk-diff-reviewer.md +34 -3
- package/templates/agent-os/stack/aws-cdk/.claude/rules/aws-cdk.md +1 -1
- package/templates/agent-os/stack/aws-cdk/.claude/skills/post-deploy-verify/SKILL.md +34 -0
- package/templates/agent-os/stack/aws-cdk/.codex/agents/cdk-diff-reviewer.toml +4 -0
- package/templates/agent-os/universal/.agents/skills/check-premises/SKILL.md +233 -0
- package/templates/agent-os/universal/.agents/skills/loop/SKILL.md +676 -0
- package/templates/agent-os/universal/.agents/skills/new-invariant/SKILL.md +102 -0
- package/templates/agent-os/universal/.agents/skills/new-invariant/guard-invariant.example.mjs +78 -0
- package/templates/agent-os/universal/.agents/skills/new-invariant/guard-invariant.example.test.mjs +89 -0
- package/templates/agent-os/universal/.agents/skills/pr-ship/SKILL.md +338 -0
- package/templates/agent-os/universal/.agents/skills/worktree-task/SKILL.md +73 -0
- package/templates/agent-os/universal/.claude/agents/code-reviewer.md +33 -1
- package/templates/agent-os/universal/.claude/agents/prose-reviewer.md +64 -1
- package/templates/agent-os/universal/.claude/agents/security-scanner.md +33 -0
- package/templates/agent-os/universal/.claude/hooks/gate-stop-dod.mjs +274 -24
- package/templates/agent-os/universal/.claude/hooks/guard-bash.mjs +43 -5
- package/templates/agent-os/universal/.claude/hooks/guard-core-purity.mjs +25 -11
- package/templates/agent-os/universal/.claude/hooks/guard-secret-file.mjs +180 -0
- package/templates/agent-os/universal/.claude/hooks/guard-web-boundary.mjs +32 -13
- package/templates/agent-os/universal/.claude/hooks/inject-rules.mjs +175 -7
- package/templates/agent-os/universal/.claude/hooks/lib/edit-input.mjs +480 -0
- package/templates/agent-os/universal/.claude/rules/autonomy.md +61 -1
- package/templates/agent-os/universal/.claude/rules/invariants.md +71 -16
- package/templates/agent-os/universal/.claude/rules/workflow.md +55 -5
- package/templates/agent-os/universal/.claude/scripts/decision-router.mjs +1242 -0
- package/templates/agent-os/universal/.claude/scripts/detect-missed-gate.mjs +65 -2
- package/templates/agent-os/universal/.claude/scripts/git-env.mjs +49 -0
- package/templates/agent-os/universal/.claude/scripts/lib/secrets.mjs +490 -0
- package/templates/agent-os/universal/.claude/scripts/lib/verdict.mjs +433 -0
- package/templates/agent-os/universal/.claude/scripts/preflight.mjs +6 -16
- package/templates/agent-os/universal/.claude/scripts/queue/checkout.mjs +89 -0
- package/templates/agent-os/universal/.claude/scripts/queue/core.mjs +334 -23
- package/templates/agent-os/universal/.claude/scripts/queue/gate-rounds.mjs +160 -0
- package/templates/agent-os/universal/.claude/scripts/queue/github-issues.mjs +7 -2
- package/templates/agent-os/universal/.claude/scripts/queue/index.mjs +413 -8
- package/templates/agent-os/universal/.claude/scripts/queue/jira.mjs +75 -13
- package/templates/agent-os/universal/.claude/scripts/queue/plan-md.mjs +222 -21
- package/templates/agent-os/universal/.claude/scripts/queue/state.mjs +197 -0
- package/templates/agent-os/universal/.claude/scripts/run-journal.mjs +435 -0
- package/templates/agent-os/universal/.claude/scripts/run-state.mjs +441 -0
- package/templates/agent-os/universal/.claude/scripts/verdict.mjs +101 -0
- package/templates/agent-os/universal/.claude/settings.json +7 -2
- package/templates/agent-os/universal/.claude/skills/check-premises/SKILL.md +115 -7
- package/templates/agent-os/universal/.claude/skills/loop/SKILL.md +378 -31
- package/templates/agent-os/universal/.claude/skills/pr-ship/SKILL.md +300 -25
- package/templates/agent-os/universal/.codex/agents/code-reviewer.toml +4 -0
- package/templates/agent-os/universal/.codex/agents/prose-reviewer.toml +4 -0
- package/templates/agent-os/universal/.codex/agents/security-scanner.toml +4 -0
- package/templates/agent-os/universal/.codex/agents/test-writer.toml +4 -0
- package/templates/agent-os/universal/.codex/hooks.json +65 -0
- package/templates/agent-os/universal/AGENTS.md +164 -0
- package/templates/agent-os/universal/CLAUDE.md +47 -14
- package/templates/agent-os/universal/PLAN.md +7 -40
- package/templates/agent-os/universal/docs/decisions/closing-a-task.md +98 -0
- package/templates/agent-os/universal/docs/decisions/codex-adapter.md +108 -0
- package/templates/agent-os/universal/docs/decisions/fail-open-guards.md +43 -0
- package/templates/agent-os/universal/docs/decisions/review-lanes.md +72 -0
- package/templates/agent-os/universal/docs/decisions/run-directory.md +44 -0
- package/templates/agent-os/universal/docs/decisions/spacing-rations-mechanisms.md +125 -0
- package/templates/agent-os/universal/docs/decisions/stop-conditions-in-a-file.md +46 -0
- package/templates/agent-os/universal/docs/decisions/two-empty-endings.md +62 -0
- package/templates/agent-os/universal/journal/README.md +101 -0
- package/templates/agent-os/universal/layers.json +36 -2
- package/templates/hash-history.json +2 -1
- package/templates/skeleton/aws-serverless/.github/workflows/deploy.yml +34 -1
- package/templates/skeleton/aws-serverless/README.md +91 -9
- package/templates/skeleton/aws-serverless/apps/web/src/lib/api.ts +9 -2
- package/templates/skeleton/aws-serverless/gitignore +37 -0
- package/templates/skeleton/aws-serverless/infra/bin/app.ts +73 -13
- package/templates/skeleton/aws-serverless/infra/lib/app-stack.ts +136 -4
- package/templates/skeleton/aws-serverless/infra/lib/web-stack.ts +14 -1
- package/templates/skeleton/aws-serverless/infra/test/allowed-origins.test.ts +301 -0
- package/templates/skeleton/aws-serverless/infra/test/app-composition.test.ts +137 -0
- package/templates/skeleton/aws-serverless/infra/test/app-stack.test.ts +36 -20
- package/templates/skeleton/aws-serverless/infra/test/web-stack.test.ts +15 -12
- package/templates/skeleton/aws-serverless/services/api/src/handlers/create-note.ts +33 -6
- package/templates/skeleton/aws-serverless/services/api/src/handlers/list-notes.ts +18 -5
- package/templates/skeleton/aws-serverless/services/api/src/usecases/create-note.ts +11 -0
- package/templates/skeleton/aws-serverless/services/api/test/create-note.handler.test.ts +120 -1
- package/templates/skeleton/aws-serverless/services/api/test/list-notes.test.ts +72 -1
- package/templates/skeleton/node-service/README.md +11 -1
- package/templates/skeleton/node-service/gitignore +34 -0
- package/templates/skeleton/node-service/packages/db/src/note-store.ts +47 -10
- package/templates/skeleton/node-service/packages/db/test/note-store.test.ts +20 -0
- package/templates/skeleton/node-service/services/api/src/main.ts +2 -9
- package/templates/skeleton/node-service/services/api/src/server.ts +93 -10
- package/templates/skeleton/node-service/services/api/src/static-dir.ts +20 -0
- package/templates/skeleton/node-service/services/api/test/server.test.ts +98 -13
- package/templates/skeleton/node-service/services/api/test/static-dir.test.ts +28 -0
|
@@ -63,9 +63,38 @@ That is enough, because it stops the normal path cold and the layers behind it
|
|
|
63
63
|
enforcement has to describe its enforcement precisely, or the first surprise
|
|
64
64
|
costs it all its credibility.
|
|
65
65
|
|
|
66
|
-
**Fail closed on a match, fail open on an error.** If the hook itself throws or
|
|
67
|
-
|
|
68
|
-
that blocks everything gets deleted within the hour.
|
|
66
|
+
**Fail closed on a match, fail open on an error.** If the hook itself throws, or
|
|
67
|
+
is handed a payload it cannot even tell apart from noise, it must allow the edit.
|
|
68
|
+
A crashed guard that blocks everything gets deleted within the hour.
|
|
69
|
+
|
|
70
|
+
**Refusing to inspect is a third outcome, not a match and not an error** — and it
|
|
71
|
+
has two members, which the first version of this rule collapsed into one.
|
|
72
|
+
|
|
73
|
+
- **A bound was crossed.** The payload is readable and inspecting it safely would
|
|
74
|
+
cost more than the guard is allowed to spend. It blocks, names the limit, and
|
|
75
|
+
tells the caller to split the change and retry — which is advice they can act
|
|
76
|
+
on, because a smaller edit really does fit.
|
|
77
|
+
- **The container is unreadable.** The guard can see *what* it was handed and
|
|
78
|
+
cannot read it: an `apply_patch` command that is neither a string nor a list of
|
|
79
|
+
strings, a `tool_input` that is not an object at all. It blocks, names the
|
|
80
|
+
shape it expected, and tells the caller to resend in that shape. It must **not**
|
|
81
|
+
say "split and retry": nothing about splitting changes a container, and a
|
|
82
|
+
remedy the caller cannot act on turns a refusal into a loop.
|
|
83
|
+
|
|
84
|
+
🔴 **The line between "unreadable" and "not understood" is whether the guard can
|
|
85
|
+
detect it**, and getting it backwards costs a credential either way. A field that
|
|
86
|
+
is simply ABSENT is the fail-open case — the guard has nothing to judge, exactly
|
|
87
|
+
as it has nothing to judge when a `Write` carries no content. A field that is
|
|
88
|
+
PRESENT in a shape the guard does not accept is the refusal case: it was handed
|
|
89
|
+
something, it could tell that it could not read it, and reporting that is the
|
|
90
|
+
one thing it is for. This distinction was written after a guard reported out
|
|
91
|
+
loud that it had not looked and returned the value meaning "there was nothing to
|
|
92
|
+
look at" — the edit landed, with a credential in it.
|
|
93
|
+
|
|
94
|
+
**The remedy belongs to the refusal, not to the guard that prints it.** Carry it
|
|
95
|
+
as a field beside the reason, decided where the reason is decided. Choosing it
|
|
96
|
+
by pattern-matching the reason's wording works until somebody rewords the reason,
|
|
97
|
+
and then every copy of the guard silently prints the wrong advice.
|
|
69
98
|
|
|
70
99
|
**A guard that fails open must do provably bounded work — and this is the rule
|
|
71
100
|
that cost the most to learn.**
|
|
@@ -75,15 +104,6 @@ means **every line of work the guard does is a potential total bypass**. Any
|
|
|
75
104
|
exception, any timeout, any stack overflow inside it resolves to *allow* — not
|
|
76
105
|
for the rule that broke, for **all** of them.
|
|
77
106
|
|
|
78
|
-
Three review rounds on one hook produced three separate total bypasses, and all
|
|
79
|
-
three were the same shape: an input made the guard's own code throw, and the
|
|
80
|
-
fail-open catch turned that into permission.
|
|
81
|
-
|
|
82
|
-
- an unbounded `spread` over an input-derived array → `RangeError` → allow;
|
|
83
|
-
- a recursive expansion whose bound was per-group, not total → stack overflow →
|
|
84
|
-
allow;
|
|
85
|
-
- a quadratic loop → killed by the hook timeout → allow.
|
|
86
|
-
|
|
87
107
|
So the test is not "is it fast enough on realistic input" but **"can any input
|
|
88
108
|
make it do unbounded work at all"**. In practice:
|
|
89
109
|
|
|
@@ -92,12 +112,16 @@ make it do unbounded work at all"**. In practice:
|
|
|
92
112
|
then spread;
|
|
93
113
|
- one forward pass; no rescanning, no loop that re-copies the whole string;
|
|
94
114
|
- when a bound is hit, fail **closed** or keep the input intact — never silently
|
|
95
|
-
drop part of it, which is how one
|
|
115
|
+
drop part of it, which is how one past bypass hid whole commands from the
|
|
116
|
+
guard while it reported that it had looked.
|
|
96
117
|
|
|
97
118
|
And the corollary that follows from all of it: **prefer deleting a rule to adding
|
|
98
|
-
one.**
|
|
99
|
-
|
|
100
|
-
|
|
119
|
+
one.** Subtraction cannot introduce this class of defect; addition routinely
|
|
120
|
+
does.
|
|
121
|
+
|
|
122
|
+
The three bypasses this rule was paid for — what each one was, and why the
|
|
123
|
+
corollary is subtraction rather than more care — are in
|
|
124
|
+
`docs/decisions/fail-open-guards.md`.
|
|
101
125
|
|
|
102
126
|
## State the limits — and test them
|
|
103
127
|
|
|
@@ -112,6 +136,31 @@ rely on cover that is not there) or into staleness (limits listed that were
|
|
|
112
136
|
fixed long ago, understating the guard). Both have happened here, in the same
|
|
113
137
|
file, within one review cycle.
|
|
114
138
|
|
|
139
|
+
**A sentence describing what a mechanism does or does not do is either generated
|
|
140
|
+
from the thing it describes, or a pointer to the test that proves it** — the form is
|
|
141
|
+
`see guard-invariant.example.test.mjs › "blocks the violation, and the reason names
|
|
142
|
+
what to do instead"` — the test's whole name, so one grep lands on it, in a file the
|
|
143
|
+
reader has. Free-standing prose about a mechanism's behaviour is a
|
|
144
|
+
`prose-reviewer` blocker **by rule** (its checklist item 5), which means the finding
|
|
145
|
+
is the absence of backing; nobody has to prove the claim wrong first.
|
|
146
|
+
|
|
147
|
+
Why by rule rather than by discovery: it moves the check earlier. `check-premises`
|
|
148
|
+
has a second entry point on the run's own prose, and it returns `UNMEASURED` for a
|
|
149
|
+
claim with nothing behind it — before the gate, instead of after a cold reader has
|
|
150
|
+
read the whole diff.
|
|
151
|
+
|
|
152
|
+
Two exits from `UNMEASURED`, and rewording is not one of them: **delete the
|
|
153
|
+
sentence, or make it a pointer.** Softening the wording leaves an unbacked claim in
|
|
154
|
+
a document agents follow literally, which is the failure this whole section is
|
|
155
|
+
about. A pointer also cannot quietly drift, because a renamed test makes it a dead
|
|
156
|
+
reference — item 2 of the same checklist.
|
|
157
|
+
|
|
158
|
+
⚠ **This norm has parts 1 and 3 of the pattern above, and not part 2.** No hook
|
|
159
|
+
refuses an unbacked sentence; the check is a skill a session is told to run and the
|
|
160
|
+
backstop is a reviewer nothing launches. By this file's own standard that makes it a
|
|
161
|
+
rule with a reader behind it rather than a mechanism — stated here so the next
|
|
162
|
+
reader does not infer cover that is not there.
|
|
163
|
+
|
|
115
164
|
Two rules that follow from it:
|
|
116
165
|
|
|
117
166
|
- **Match a rule's precision to the cost of a false positive.** Where a false
|
|
@@ -153,6 +202,12 @@ in this repository — so by the rule above, as they sit here, they are checks
|
|
|
153
202
|
without tests. That is deliberate and it has a boundary: it holds only while they
|
|
154
203
|
are untouched.
|
|
155
204
|
|
|
205
|
+
This is the one narrow exception for a generator-authored hook: it may cite the
|
|
206
|
+
generator's upstream tests **only while unchanged downstream**, and its hook
|
|
207
|
+
header must identify the upstream generator tests as absent locally. That pointer
|
|
208
|
+
records the evidence used to author the inherited snapshot; it does not turn the
|
|
209
|
+
absent test into a local check.
|
|
210
|
+
|
|
156
211
|
**The moment you edit one, its test is yours.** A guard whose behaviour has
|
|
157
212
|
changed and whose test lives somewhere else is precisely the "quietly stopped
|
|
158
213
|
matching" case this rule names, and nothing here would catch it. The same applies
|
|
@@ -47,19 +47,69 @@ travels one path to merge, in this order:
|
|
|
47
47
|
|
|
48
48
|
1. **Local checks** — the full suite, lint, typecheck, all green locally first.
|
|
49
49
|
A red check is information, never something to retry until green (`autonomy.md`).
|
|
50
|
-
2. **Reviewer fan-out**, by what the change touches
|
|
51
|
-
|
|
50
|
+
2. **Reviewer fan-out**, by what the change touches — and *how much* fan-out is
|
|
51
|
+
decided first, in ascending order of cost:
|
|
52
|
+
|
|
53
|
+
| lane | what reaches it | the floor it sets |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| `deterministic` | every changed file is a derived artifact git reports as modified or removed, none of them under a declared elevated path | the checks alone; no reviewer |
|
|
56
|
+
| `fast-path` | documentation outside the rulebook, and derived files under those same two rules | `prose-reviewer` |
|
|
57
|
+
| `model` | everything else, including anything unclassifiable | `code-reviewer`, **always** |
|
|
58
|
+
|
|
59
|
+
`.claude/scripts/decision-router.mjs` decides this from the **committed**
|
|
60
|
+
diff's paths — an uncommitted edit is not routed — and **risk flags escalate
|
|
61
|
+
ahead of all three**: a file under a declared elevated path, a dependency
|
|
62
|
+
manifest, a path naming auth or secrets or sessions, a deleted test —
|
|
63
|
+
including the deletion half of a rename. Any one of them means `model`,
|
|
64
|
+
however cheap the change otherwise looked. A rulebook document is code here,
|
|
65
|
+
so it never reaches the prose lane; `.md`/`.mdx` files and test paths that
|
|
66
|
+
provision nothing are inert, so a README inside an elevated directory does
|
|
67
|
+
not escalate on that ground alone. **Rulebook paths are exempt from that
|
|
68
|
+
carve-out** — `CLAUDE.md`, anything under `.claude/`, and the decision
|
|
69
|
+
records under `docs/decisions/`, which are extracted rationale and reviewed
|
|
70
|
+
like the rules they explain. The inert set is otherwise those two extensions
|
|
71
|
+
and test paths exactly — **not** the router's own notion of prose, which is
|
|
72
|
+
`.md`/`.txt`. Neither set contains the other, and reconciling them breaks a
|
|
73
|
+
gate in either direction: `docs/decisions/review-lanes.md`. The router
|
|
74
|
+
**refuses** rather than routing when it cannot decide, and a refusal is read
|
|
75
|
+
as `model`, never as a reason to skip the gate.
|
|
76
|
+
|
|
77
|
+
🔴 **The cheap lanes give something up, and the rule says what.** Dropping
|
|
78
|
+
`code-reviewer` drops two of its checks that are not about code — contract
|
|
79
|
+
drift, and "contradicts the item it claims to implement". So every lane
|
|
80
|
+
passes the queue item's text to whatever cold reader it launches. The
|
|
81
|
+
`deterministic` lane launches none, which rests on the file being generator
|
|
82
|
+
output that a check regenerates — so an added, copied, renamed or
|
|
83
|
+
status-less entry is refused it, and a test snapshot is not a derived
|
|
84
|
+
artifact at all: it *is* the behaviour claim.
|
|
85
|
+
|
|
86
|
+
The lane is a **floor, not a ceiling**. It reads paths, while the triggers
|
|
87
|
+
below read what the code *does*, and a path cannot say that a module parses
|
|
88
|
+
untrusted input. **These triggers are lane-independent and may only add** — a
|
|
89
|
+
documentation-only diff still reaches `security-scanner` when it trips one:
|
|
52
90
|
- `security-scanner` when it touches auth, secrets/configuration, input
|
|
53
91
|
parsing, file handling, or outbound calls;
|
|
54
92
|
- `prose-reviewer` when it touches the documents that instruct agents — a
|
|
55
|
-
rule file, a skill, an agent spec,
|
|
56
|
-
|
|
57
|
-
|
|
93
|
+
rule file, a skill, an agent spec, a decision record under
|
|
94
|
+
`docs/decisions/`, `CLAUDE.md`, the README. In this layer the prose *is*
|
|
95
|
+
the implementation, and it fails the same way code does: silently, in the
|
|
96
|
+
direction of false confidence;
|
|
58
97
|
- an infrastructure review when it touches infrastructure (the stack layer
|
|
59
98
|
names the reviewing agent for the target).
|
|
60
99
|
|
|
61
100
|
The `pr-ship` skill drives this fan-out and returns a SHIP / HOLD verdict
|
|
62
101
|
with named blockers; blocking findings are resolved, not argued with.
|
|
102
|
+
|
|
103
|
+
**A verdict is a block, not a sentence.** Every gate ends its report with one
|
|
104
|
+
fenced `json` block of the shape `.claude/scripts/lib/verdict.mjs` defines,
|
|
105
|
+
and `pr-ship` runs `node .claude/scripts/verdict.mjs check` on each answer
|
|
106
|
+
**before** it decides anything from it. A report that does not parse — no
|
|
107
|
+
block, a word no gate returns, a blocker naming no rule, a stop naming no
|
|
108
|
+
blocker — is `incomplete`: the reviewer did not answer, which is neither a
|
|
109
|
+
pass nor a stop. Reading it as a pass is the failure the check exists to
|
|
110
|
+
prevent. What the check cannot decide for a gate the shared vocabulary does
|
|
111
|
+
not name is stated as that module's first limit; the gate reads such a word
|
|
112
|
+
against the reviewer's own spec.
|
|
63
113
|
3. **Merge — on an explicit, non-lazy criterion.** Do not trust a watcher
|
|
64
114
|
command that can exit before the checks have even registered. Confirm that
|
|
65
115
|
the **required** check completed successfully **for this commit** — a list
|