create-agent-rig 0.4.0 → 0.6.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.
Files changed (118) hide show
  1. package/CHANGELOG.md +316 -11
  2. package/README.md +58 -19
  3. package/package.json +2 -1
  4. package/packages/cli/dist/commands/create.js +8 -3
  5. package/packages/cli/dist/commands/init.js +85 -34
  6. package/packages/cli/dist/commands/upgrade.js +112 -30
  7. package/packages/cli/dist/index.js +81 -19
  8. package/packages/cli/dist/lib/copy-tree.js +35 -6
  9. package/packages/cli/dist/lib/init-settings.js +12 -0
  10. package/packages/cli/dist/lib/install-set.js +6 -8
  11. package/packages/cli/dist/lib/manifest.js +31 -9
  12. package/packages/cli/dist/lib/safe-path.js +30 -0
  13. package/scripts/prepare.mjs +1 -1
  14. package/templates/agent-os/init/AGENTS.md +199 -0
  15. package/templates/agent-os/init/CLAUDE.md +69 -9
  16. package/templates/agent-os/stack/aws-cdk/.agents/skills/post-deploy-verify/SKILL.md +105 -0
  17. package/templates/agent-os/stack/aws-cdk/.agents/skills/ro-debug/SKILL.md +117 -0
  18. package/templates/agent-os/stack/aws-cdk/.claude/agents/cdk-diff-reviewer.md +41 -3
  19. package/templates/agent-os/stack/aws-cdk/.claude/rules/aws-cdk.md +1 -1
  20. package/templates/agent-os/stack/aws-cdk/.claude/skills/post-deploy-verify/SKILL.md +41 -0
  21. package/templates/agent-os/stack/aws-cdk/.codex/agents/cdk-diff-reviewer.toml +4 -0
  22. package/templates/agent-os/stack/node-ts/.claude/rules/node-ts.md +29 -0
  23. package/templates/agent-os/universal/.agents/skills/check-premises/SKILL.md +236 -0
  24. package/templates/agent-os/universal/.agents/skills/loop/SKILL.md +958 -0
  25. package/templates/agent-os/universal/.agents/skills/new-invariant/SKILL.md +102 -0
  26. package/templates/agent-os/universal/.agents/skills/new-invariant/guard-invariant.example.mjs +78 -0
  27. package/templates/agent-os/universal/.agents/skills/new-invariant/guard-invariant.example.test.mjs +89 -0
  28. package/templates/agent-os/universal/.agents/skills/pr-ship/SKILL.md +396 -0
  29. package/templates/agent-os/universal/.agents/skills/worktree-task/SKILL.md +73 -0
  30. package/templates/agent-os/universal/.claude/agents/code-reviewer.md +40 -1
  31. package/templates/agent-os/universal/.claude/agents/prose-reviewer.md +71 -1
  32. package/templates/agent-os/universal/.claude/agents/security-scanner.md +40 -0
  33. package/templates/agent-os/universal/.claude/hooks/gate-stop-dod.mjs +301 -26
  34. package/templates/agent-os/universal/.claude/hooks/guard-bash.mjs +43 -5
  35. package/templates/agent-os/universal/.claude/hooks/guard-core-purity.mjs +25 -11
  36. package/templates/agent-os/universal/.claude/hooks/guard-rulebook.mjs +127 -0
  37. package/templates/agent-os/universal/.claude/hooks/guard-secret-file.mjs +180 -0
  38. package/templates/agent-os/universal/.claude/hooks/guard-web-boundary.mjs +32 -13
  39. package/templates/agent-os/universal/.claude/hooks/inject-rules.mjs +175 -7
  40. package/templates/agent-os/universal/.claude/hooks/lib/edit-input.mjs +503 -0
  41. package/templates/agent-os/universal/.claude/rules/autonomy.md +69 -1
  42. package/templates/agent-os/universal/.claude/rules/invariants.md +104 -19
  43. package/templates/agent-os/universal/.claude/rules/workflow.md +55 -5
  44. package/templates/agent-os/universal/.claude/scripts/decision-router.mjs +1260 -0
  45. package/templates/agent-os/universal/.claude/scripts/detect-missed-gate.mjs +65 -2
  46. package/templates/agent-os/universal/.claude/scripts/doctor.mjs +351 -0
  47. package/templates/agent-os/universal/.claude/scripts/git-env.mjs +49 -0
  48. package/templates/agent-os/universal/.claude/scripts/lib/gate-coverage.mjs +306 -0
  49. package/templates/agent-os/universal/.claude/scripts/lib/revalidation-points.mjs +28 -0
  50. package/templates/agent-os/universal/.claude/scripts/lib/secrets.mjs +490 -0
  51. package/templates/agent-os/universal/.claude/scripts/lib/verdict.mjs +462 -0
  52. package/templates/agent-os/universal/.claude/scripts/preflight.mjs +33 -17
  53. package/templates/agent-os/universal/.claude/scripts/queue/as-of.mjs +51 -0
  54. package/templates/agent-os/universal/.claude/scripts/queue/checkout.mjs +149 -0
  55. package/templates/agent-os/universal/.claude/scripts/queue/core.mjs +809 -28
  56. package/templates/agent-os/universal/.claude/scripts/queue/gate-rounds.mjs +160 -0
  57. package/templates/agent-os/universal/.claude/scripts/queue/github-issues.mjs +95 -16
  58. package/templates/agent-os/universal/.claude/scripts/queue/index.mjs +543 -15
  59. package/templates/agent-os/universal/.claude/scripts/queue/jira.mjs +457 -47
  60. package/templates/agent-os/universal/.claude/scripts/queue/plan-md.mjs +289 -25
  61. package/templates/agent-os/universal/.claude/scripts/queue/state.mjs +197 -0
  62. package/templates/agent-os/universal/.claude/scripts/revalidate.mjs +316 -0
  63. package/templates/agent-os/universal/.claude/scripts/revalidation-report.mjs +180 -0
  64. package/templates/agent-os/universal/.claude/scripts/run-journal.mjs +435 -0
  65. package/templates/agent-os/universal/.claude/scripts/run-state.mjs +539 -0
  66. package/templates/agent-os/universal/.claude/scripts/stop-flag.mjs +15 -8
  67. package/templates/agent-os/universal/.claude/scripts/unattended-flag.mjs +239 -0
  68. package/templates/agent-os/universal/.claude/scripts/verdict.mjs +198 -0
  69. package/templates/agent-os/universal/.claude/settings.json +11 -2
  70. package/templates/agent-os/universal/.claude/skills/check-premises/SKILL.md +118 -7
  71. package/templates/agent-os/universal/.claude/skills/loop/SKILL.md +667 -38
  72. package/templates/agent-os/universal/.claude/skills/pr-ship/SKILL.md +359 -26
  73. package/templates/agent-os/universal/.codex/agents/code-reviewer.toml +4 -0
  74. package/templates/agent-os/universal/.codex/agents/prose-reviewer.toml +4 -0
  75. package/templates/agent-os/universal/.codex/agents/security-scanner.toml +4 -0
  76. package/templates/agent-os/universal/.codex/agents/test-writer.toml +4 -0
  77. package/templates/agent-os/universal/.codex/hooks.json +70 -0
  78. package/templates/agent-os/universal/AGENTS.md +166 -0
  79. package/templates/agent-os/universal/CLAUDE.md +49 -14
  80. package/templates/agent-os/universal/PLAN.md +7 -40
  81. package/templates/agent-os/universal/docs/decisions/closing-a-task.md +98 -0
  82. package/templates/agent-os/universal/docs/decisions/codex-adapter.md +108 -0
  83. package/templates/agent-os/universal/docs/decisions/fail-open-guards.md +43 -0
  84. package/templates/agent-os/universal/docs/decisions/gate-coverage.md +83 -0
  85. package/templates/agent-os/universal/docs/decisions/review-lanes.md +72 -0
  86. package/templates/agent-os/universal/docs/decisions/run-directory.md +44 -0
  87. package/templates/agent-os/universal/docs/decisions/spacing-rations-mechanisms.md +125 -0
  88. package/templates/agent-os/universal/docs/decisions/stop-conditions-in-a-file.md +46 -0
  89. package/templates/agent-os/universal/docs/decisions/two-empty-endings.md +74 -0
  90. package/templates/agent-os/universal/journal/README.md +101 -0
  91. package/templates/agent-os/universal/layers.json +45 -2
  92. package/templates/hash-history.json +310 -49
  93. package/templates/release-ledger.json +9 -0
  94. package/templates/skeleton/aws-serverless/.github/workflows/deploy.yml +34 -1
  95. package/templates/skeleton/aws-serverless/README.md +91 -9
  96. package/templates/skeleton/aws-serverless/apps/web/src/lib/api.ts +9 -2
  97. package/templates/skeleton/aws-serverless/gitignore +37 -0
  98. package/templates/skeleton/aws-serverless/infra/bin/app.ts +73 -13
  99. package/templates/skeleton/aws-serverless/infra/lib/app-stack.ts +136 -4
  100. package/templates/skeleton/aws-serverless/infra/lib/web-stack.ts +14 -1
  101. package/templates/skeleton/aws-serverless/infra/test/allowed-origins.test.ts +301 -0
  102. package/templates/skeleton/aws-serverless/infra/test/app-composition.test.ts +137 -0
  103. package/templates/skeleton/aws-serverless/infra/test/app-stack.test.ts +36 -20
  104. package/templates/skeleton/aws-serverless/infra/test/web-stack.test.ts +15 -12
  105. package/templates/skeleton/aws-serverless/services/api/src/handlers/create-note.ts +33 -6
  106. package/templates/skeleton/aws-serverless/services/api/src/handlers/list-notes.ts +18 -5
  107. package/templates/skeleton/aws-serverless/services/api/src/usecases/create-note.ts +11 -0
  108. package/templates/skeleton/aws-serverless/services/api/test/create-note.handler.test.ts +120 -1
  109. package/templates/skeleton/aws-serverless/services/api/test/list-notes.test.ts +72 -1
  110. package/templates/skeleton/node-service/README.md +11 -1
  111. package/templates/skeleton/node-service/gitignore +34 -0
  112. package/templates/skeleton/node-service/packages/db/src/note-store.ts +47 -10
  113. package/templates/skeleton/node-service/packages/db/test/note-store.test.ts +20 -0
  114. package/templates/skeleton/node-service/services/api/src/main.ts +2 -9
  115. package/templates/skeleton/node-service/services/api/src/server.ts +93 -10
  116. package/templates/skeleton/node-service/services/api/src/static-dir.ts +20 -0
  117. package/templates/skeleton/node-service/services/api/test/server.test.ts +98 -13
  118. 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
- gets a payload it does not understand, it must allow the edit. A crashed guard
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 of those bypasses hid whole commands.
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.** Each of those three bypasses arrived in a commit whose purpose was to make
99
- the guard stricter. Subtraction cannot introduce this class of defect; addition
100
- routinely does.
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
@@ -119,9 +168,28 @@ Two rules that follow from it:
119
168
  deliberately coarse and stop trying to out-parse the input. Where a false block
120
169
  interrupts ordinary work, stay narrow and specific. Uniform precision
121
170
  everywhere is how a guard ends up simultaneously too loose and too annoying.
122
- - **One mechanism, one implementation.** If two files enforce the same
123
- invariant, they will disagree — and the one nobody is looking at is the one
124
- that is wrong. Export it from a single module and import it.
171
+ - **One mechanism, one implementation.** And one spelling of a fact. If two
172
+ files enforce the same invariant, they will disagree — and the one nobody is
173
+ looking at is the one that is wrong. Export it from a single module and import
174
+ it. The same holds for any mechanically expressible fact two artifacts encode
175
+ — a list, a vocabulary, a set of paths, a lane's reviewer floor: prefer one
176
+ source that the others derive from or are generated from. Where a second copy
177
+ has to stay (prose a reader needs), put a correspondence check between the two
178
+ that goes red in both directions — a copy that gains an entry the source
179
+ lacks, and a source that gains one the copy lacks — and put that check in
180
+ before adding prose or a memory note about keeping them aligned. The shape is
181
+ in the generator's `test/template/correspondence.test.ts` (absent in a
182
+ generated rig): the check itself, ›
183
+ "every point the module knows is named by the loop or pr-ship skill, and vice versa"
184
+ and ›
185
+ "the pr-ship fan-out bullets name exactly the floor of each lane, and only known lanes";
186
+ and the proof that it names the offender, one mutation per side, ›
187
+ "reports a point named in prose that no script knows (mutation: BEFORE_MERGE)",
188
+ › "reports a point the module knows that no prose mentions (mutation: extended POINTS)",
189
+ › "reports a reviewer added to a bullet the floor does not include (mutation: prose)"
190
+ and › "reports a floor widened in the mapping that the prose does not carry (mutation: mapping)".
191
+ What such a check costs is written where the check is, in the header of the
192
+ file that carries it.
125
193
 
126
194
  ## The worked example — and it is one project's answer, not a law
127
195
 
@@ -153,6 +221,12 @@ in this repository — so by the rule above, as they sit here, they are checks
153
221
  without tests. That is deliberate and it has a boundary: it holds only while they
154
222
  are untouched.
155
223
 
224
+ This is the one narrow exception for a generator-authored hook: it may cite the
225
+ generator's upstream tests **only while unchanged downstream**, and its hook
226
+ header must identify the upstream generator tests as absent locally. That pointer
227
+ records the evidence used to author the inherited snapshot; it does not turn the
228
+ absent test into a local check.
229
+
156
230
  **The moment you edit one, its test is yours.** A guard whose behaviour has
157
231
  changed and whose test lives somewhere else is precisely the "quietly stopped
158
232
  matching" case this rule names, and nothing here would catch it. The same applies
@@ -162,6 +236,17 @@ If a hook matters enough to keep, it is worth ten minutes to copy the shape from
162
236
  `.claude/skills/new-invariant/guard-invariant.example.test.mjs` and pin the
163
237
  behaviour you actually rely on.
164
238
 
239
+ **That boundary is audited, not remembered.** `node .claude/scripts/doctor.mjs`
240
+ reads `.claude/.rig-manifest.json` and asks of every hook in `.claude/hooks/` (and
241
+ `.husky/`, when it exists) whether the project owns it — the bytes differ from
242
+ what the generator installed, or the manifest has no entry — and, if so, whether
243
+ `<hook>.test.mjs` sits beside it. A shipped, unchanged hook is not a finding; an
244
+ owned hook with no neighbour is; a rig with no manifest gets `unknown` for every
245
+ hook that has no test neighbour, never a pass. Exemptions are an explicit list with reasons in
246
+ `.claude/doctor-exemptions.json`, and the report ends with what the script did
247
+ not check. Pinned in the generator's `test/template/doctor.test.ts` — absent in a
248
+ generated rig — › "an owned hook without a test is a FAIL, and the run is STOP".
249
+
165
250
  ## Adding one
166
251
 
167
252
  Use the `new-invariant` skill. It asks what the invariant is (it will not invent
@@ -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
- - the `code-reviewer` agent **always**;
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, `CLAUDE.md`, the README. In this layer
56
- the prose *is* the implementation, and it fails the same way code does:
57
- silently, in the direction of false confidence;
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