create-agent-rig 0.9.1 → 0.10.1

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 (235) hide show
  1. package/CHANGELOG.md +462 -3
  2. package/README.md +267 -305
  3. package/package.json +8 -14
  4. package/packages/cli/dist/commands/create.js +56 -88
  5. package/packages/cli/dist/commands/doctor.js +213 -0
  6. package/packages/cli/dist/commands/init.js +111 -50
  7. package/packages/cli/dist/commands/integrations.js +468 -0
  8. package/packages/cli/dist/commands/setup-wizard.js +61 -0
  9. package/packages/cli/dist/commands/uninstall.js +1223 -0
  10. package/packages/cli/dist/commands/upgrade.js +508 -101
  11. package/packages/cli/dist/index.js +658 -47
  12. package/packages/cli/dist/integrations/declaration.js +158 -0
  13. package/packages/cli/dist/integrations/doctor-guards.js +126 -0
  14. package/packages/cli/dist/integrations/doctor-workflow.js +25 -0
  15. package/packages/cli/dist/integrations/mcp-json.js +107 -0
  16. package/packages/cli/dist/integrations/memory-doctor.js +124 -0
  17. package/packages/cli/dist/integrations/registry.js +22 -0
  18. package/packages/cli/dist/integrations/spawn.js +228 -0
  19. package/packages/cli/dist/integrations/spec-kit.js +280 -0
  20. package/packages/cli/dist/integrations/verify.js +193 -0
  21. package/packages/cli/dist/integrations/windows-job.js +275 -0
  22. package/packages/cli/dist/lib/elevated-paths.js +74 -0
  23. package/packages/cli/dist/lib/install-set.js +1 -44
  24. package/packages/cli/dist/lib/manifest.js +64 -5
  25. package/packages/cli/dist/lib/prompts.js +0 -23
  26. package/packages/cli/dist/lib/safe-path.js +193 -2
  27. package/packages/cli/dist/lib/safe-text.js +78 -0
  28. package/packages/cli/dist/lib/substitute.js +1 -52
  29. package/packages/cli/dist/lib/summary.js +5 -5
  30. package/packages/cli/dist/templates.js +4 -14
  31. package/scripts/prepare.mjs +28 -12
  32. package/templates/agent-os/subagent-routing.json +4 -4
  33. package/templates/agent-os/universal/.agents/skills/check-premises/SKILL.md +45 -12
  34. package/templates/agent-os/universal/.agents/skills/loop/SKILL.md +31 -6
  35. package/templates/agent-os/universal/.agents/skills/new-invariant/SKILL.md +5 -5
  36. package/templates/agent-os/universal/.agents/skills/pr-ship/SKILL.md +1 -1
  37. package/templates/agent-os/universal/.agents/skills/worktree-task/SKILL.md +3 -3
  38. package/templates/agent-os/universal/.claude/agents/code-reviewer.md +8 -6
  39. package/templates/agent-os/universal/.claude/agents/implementation-agent.md +41 -0
  40. package/templates/agent-os/universal/.claude/agents/prose-reviewer.md +8 -4
  41. package/templates/agent-os/universal/.claude/agents/security-scanner.md +4 -2
  42. package/templates/agent-os/universal/.claude/agents/test-writer.md +2 -2
  43. package/templates/agent-os/universal/.claude/hooks/gate-stop-dod.mjs +4 -2
  44. package/templates/agent-os/universal/.claude/hooks/inject-rules.mjs +83 -4
  45. package/templates/agent-os/universal/.claude/rules/autonomy.md +55 -27
  46. package/templates/agent-os/universal/.claude/rules/invariants.md +9 -8
  47. package/templates/agent-os/universal/.claude/rules/workflow.md +59 -22
  48. package/templates/agent-os/universal/.claude/scripts/decision-router.mjs +1 -1
  49. package/templates/agent-os/universal/.claude/scripts/detect-missed-gate.mjs +12 -9
  50. package/templates/agent-os/universal/.claude/scripts/doctor.mjs +100 -2
  51. package/templates/agent-os/universal/.claude/scripts/lib/claim-records.mjs +236 -6
  52. package/templates/agent-os/universal/.claude/scripts/queue/state.mjs +2 -2
  53. package/templates/agent-os/universal/.claude/scripts/reconcile-external-prs.mjs +4 -3
  54. package/templates/agent-os/universal/.claude/scripts/revalidate.mjs +16 -0
  55. package/templates/agent-os/universal/.claude/settings.json +0 -8
  56. package/templates/agent-os/universal/.claude/skills/check-premises/SKILL.md +45 -12
  57. package/templates/agent-os/universal/.claude/skills/loop/SKILL.md +31 -6
  58. package/templates/agent-os/universal/.claude/skills/new-invariant/SKILL.md +5 -5
  59. package/templates/agent-os/universal/.claude/skills/pr-ship/SKILL.md +1 -1
  60. package/templates/agent-os/universal/.claude/skills/worktree-task/SKILL.md +3 -3
  61. package/templates/agent-os/universal/.codex/agents/code-reviewer.toml +2 -2
  62. package/templates/agent-os/universal/.codex/agents/implementation-agent.toml +6 -0
  63. package/templates/agent-os/universal/.codex/agents/prose-reviewer.toml +2 -2
  64. package/templates/agent-os/universal/.codex/agents/security-scanner.toml +1 -1
  65. package/templates/agent-os/universal/.codex/agents/test-writer.toml +1 -1
  66. package/templates/agent-os/universal/.codex/hooks.json +0 -10
  67. package/templates/agent-os/universal/AGENTS.md +208 -107
  68. package/templates/agent-os/universal/CLAUDE.md +15 -162
  69. package/templates/agent-os/universal/PLAN.md +17 -10
  70. package/templates/agent-os/universal/docs/decisions/agents-md-canonical.md +313 -0
  71. package/templates/agent-os/universal/docs/decisions/codex-adapter.md +15 -10
  72. package/templates/agent-os/universal/docs/decisions/review-lanes.md +12 -8
  73. package/templates/agent-os/universal/docs/decisions/session-start-wire-format.md +206 -0
  74. package/templates/agent-os/universal/docs/decisions/subagent-routing.md +6 -3
  75. package/templates/agent-os/universal/docs/decisions/workflow-layer-split.md +235 -0
  76. package/templates/agent-os/universal/layers.json +25 -30
  77. package/templates/hash-history.json +112 -40
  78. package/templates/release-ledger.json +3 -1
  79. package/packages/cli/dist/lib/composition.js +0 -20
  80. package/packages/cli/dist/lib/targets.js +0 -28
  81. package/packages/cli/dist/policy/benchmark/corpus.js +0 -165
  82. package/packages/cli/dist/policy/core/adapter.js +0 -18
  83. package/packages/cli/dist/policy/core/coverage.js +0 -253
  84. package/packages/cli/dist/policy/core/decision-record.js +0 -287
  85. package/packages/cli/dist/policy/core/declaration.js +0 -127
  86. package/packages/cli/dist/policy/core/evidence-matrix.js +0 -94
  87. package/packages/cli/dist/policy/core/probe.js +0 -442
  88. package/packages/cli/dist/policy/core/registry.js +0 -115
  89. package/packages/cli/dist/policy/core/validation.js +0 -275
  90. package/packages/cli/dist/policy/core/vocabulary.js +0 -123
  91. package/packages/cli/dist/policy/harness/claude.js +0 -47
  92. package/packages/cli/dist/policy/harness/codex.js +0 -87
  93. package/packages/cli/dist/policy/harness/index.js +0 -15
  94. package/packages/cli/dist/policy/harness/shared-hooks.js +0 -28
  95. package/packages/cli/dist/policy/index.js +0 -17
  96. package/templates/agent-os/init/AGENTS.md +0 -201
  97. package/templates/agent-os/init/CLAUDE.md +0 -201
  98. package/templates/agent-os/stack/aws-cdk/.agents/skills/post-deploy-verify/SKILL.md +0 -105
  99. package/templates/agent-os/stack/aws-cdk/.agents/skills/ro-debug/SKILL.md +0 -117
  100. package/templates/agent-os/stack/aws-cdk/.claude/agents/cdk-diff-reviewer.md +0 -89
  101. package/templates/agent-os/stack/aws-cdk/.claude/rules/aws-cdk.md +0 -105
  102. package/templates/agent-os/stack/aws-cdk/.claude/skills/post-deploy-verify/SKILL.md +0 -105
  103. package/templates/agent-os/stack/aws-cdk/.claude/skills/ro-debug/SKILL.md +0 -117
  104. package/templates/agent-os/stack/aws-cdk/.codex/agents/cdk-diff-reviewer.toml +0 -6
  105. package/templates/agent-os/stack/node-ts/.claude/hooks/dod-checks.json +0 -1
  106. package/templates/agent-os/stack/node-ts/.claude/rules/node-ts.md +0 -85
  107. package/templates/agent-os/universal/.claude/hooks/guard-core-purity.mjs +0 -89
  108. package/templates/agent-os/universal/.claude/hooks/guard-web-boundary.mjs +0 -68
  109. package/templates/agent-os/universal/.claude/rules/architecture.md +0 -81
  110. package/templates/skeleton/aws-serverless/.github/workflows/ci.yml +0 -28
  111. package/templates/skeleton/aws-serverless/.github/workflows/deploy.yml +0 -90
  112. package/templates/skeleton/aws-serverless/README.md +0 -181
  113. package/templates/skeleton/aws-serverless/apps/web/next.config.mjs +0 -17
  114. package/templates/skeleton/aws-serverless/apps/web/package.json +0 -19
  115. package/templates/skeleton/aws-serverless/apps/web/src/app/layout.tsx +0 -17
  116. package/templates/skeleton/aws-serverless/apps/web/src/app/page.tsx +0 -96
  117. package/templates/skeleton/aws-serverless/apps/web/src/lib/api.ts +0 -36
  118. package/templates/skeleton/aws-serverless/apps/web/src/lib/validate.ts +0 -23
  119. package/templates/skeleton/aws-serverless/apps/web/test/shared-validation.test.ts +0 -38
  120. package/templates/skeleton/aws-serverless/apps/web/tsconfig.json +0 -14
  121. package/templates/skeleton/aws-serverless/eslint.config.mjs +0 -20
  122. package/templates/skeleton/aws-serverless/gitignore +0 -56
  123. package/templates/skeleton/aws-serverless/infra/bin/app.ts +0 -79
  124. package/templates/skeleton/aws-serverless/infra/cdk.json +0 -3
  125. package/templates/skeleton/aws-serverless/infra/lib/app-stack.ts +0 -248
  126. package/templates/skeleton/aws-serverless/infra/lib/web-stack.ts +0 -45
  127. package/templates/skeleton/aws-serverless/infra/package.json +0 -18
  128. package/templates/skeleton/aws-serverless/infra/test/allowed-origins.test.ts +0 -301
  129. package/templates/skeleton/aws-serverless/infra/test/app-composition.test.ts +0 -137
  130. package/templates/skeleton/aws-serverless/infra/test/app-stack.test.ts +0 -120
  131. package/templates/skeleton/aws-serverless/infra/test/web-stack.test.ts +0 -44
  132. package/templates/skeleton/aws-serverless/package.json +0 -30
  133. package/templates/skeleton/aws-serverless/packages/core/package.json +0 -11
  134. package/templates/skeleton/aws-serverless/packages/core/src/events.ts +0 -14
  135. package/templates/skeleton/aws-serverless/packages/core/src/index.ts +0 -15
  136. package/templates/skeleton/aws-serverless/packages/core/src/note.ts +0 -69
  137. package/templates/skeleton/aws-serverless/packages/core/test/events.test.ts +0 -23
  138. package/templates/skeleton/aws-serverless/packages/core/test/note.test.ts +0 -101
  139. package/templates/skeleton/aws-serverless/packages/db/package.json +0 -14
  140. package/templates/skeleton/aws-serverless/packages/db/src/client.ts +0 -17
  141. package/templates/skeleton/aws-serverless/packages/db/src/index.ts +0 -2
  142. package/templates/skeleton/aws-serverless/packages/db/src/note-model.ts +0 -52
  143. package/templates/skeleton/aws-serverless/packages/db/test/note-model.test.ts +0 -91
  144. package/templates/skeleton/aws-serverless/packages/shared/package.json +0 -11
  145. package/templates/skeleton/aws-serverless/packages/shared/src/env.ts +0 -17
  146. package/templates/skeleton/aws-serverless/packages/shared/src/errors.ts +0 -33
  147. package/templates/skeleton/aws-serverless/packages/shared/src/index.ts +0 -3
  148. package/templates/skeleton/aws-serverless/packages/shared/src/logger.ts +0 -20
  149. package/templates/skeleton/aws-serverless/packages/shared/test/env.test.ts +0 -26
  150. package/templates/skeleton/aws-serverless/packages/shared/test/errors.test.ts +0 -28
  151. package/templates/skeleton/aws-serverless/packages/shared/test/logger.test.ts +0 -19
  152. package/templates/skeleton/aws-serverless/pnpm-lock.yaml +0 -2855
  153. package/templates/skeleton/aws-serverless/pnpm-workspace.yaml +0 -14
  154. package/templates/skeleton/aws-serverless/services/api/package.json +0 -15
  155. package/templates/skeleton/aws-serverless/services/api/src/adapters/sqs-publisher.ts +0 -26
  156. package/templates/skeleton/aws-serverless/services/api/src/handlers/create-note.ts +0 -69
  157. package/templates/skeleton/aws-serverless/services/api/src/handlers/list-notes.ts +0 -37
  158. package/templates/skeleton/aws-serverless/services/api/src/list-main.ts +0 -12
  159. package/templates/skeleton/aws-serverless/services/api/src/main.ts +0 -21
  160. package/templates/skeleton/aws-serverless/services/api/src/usecases/create-note.ts +0 -41
  161. package/templates/skeleton/aws-serverless/services/api/src/usecases/list-notes.ts +0 -14
  162. package/templates/skeleton/aws-serverless/services/api/test/create-note.handler.test.ts +0 -211
  163. package/templates/skeleton/aws-serverless/services/api/test/create-note.usecase.test.ts +0 -45
  164. package/templates/skeleton/aws-serverless/services/api/test/list-notes.test.ts +0 -122
  165. package/templates/skeleton/aws-serverless/services/api/test/sqs-publisher.test.ts +0 -22
  166. package/templates/skeleton/aws-serverless/services/worker/package.json +0 -12
  167. package/templates/skeleton/aws-serverless/services/worker/src/handlers/note-created.ts +0 -15
  168. package/templates/skeleton/aws-serverless/services/worker/src/main.ts +0 -7
  169. package/templates/skeleton/aws-serverless/services/worker/src/usecases/process-note-created.ts +0 -37
  170. package/templates/skeleton/aws-serverless/services/worker/test/note-created.test.ts +0 -61
  171. package/templates/skeleton/aws-serverless/tsconfig.base.json +0 -15
  172. package/templates/skeleton/aws-serverless/tsconfig.json +0 -16
  173. package/templates/skeleton/aws-serverless/vitest.config.ts +0 -14
  174. package/templates/skeleton/node-service/.github/workflows/ci.yml +0 -27
  175. package/templates/skeleton/node-service/.github/workflows/deploy.yml +0 -29
  176. package/templates/skeleton/node-service/README.md +0 -104
  177. package/templates/skeleton/node-service/apps/web/next.config.mjs +0 -17
  178. package/templates/skeleton/node-service/apps/web/package.json +0 -19
  179. package/templates/skeleton/node-service/apps/web/src/app/layout.tsx +0 -17
  180. package/templates/skeleton/node-service/apps/web/src/app/page.tsx +0 -96
  181. package/templates/skeleton/node-service/apps/web/src/lib/api.ts +0 -29
  182. package/templates/skeleton/node-service/apps/web/src/lib/validate.ts +0 -23
  183. package/templates/skeleton/node-service/apps/web/test/shared-validation.test.ts +0 -38
  184. package/templates/skeleton/node-service/apps/web/tsconfig.json +0 -14
  185. package/templates/skeleton/node-service/eslint.config.mjs +0 -20
  186. package/templates/skeleton/node-service/gitignore +0 -54
  187. package/templates/skeleton/node-service/package.json +0 -30
  188. package/templates/skeleton/node-service/packages/core/package.json +0 -11
  189. package/templates/skeleton/node-service/packages/core/src/events.ts +0 -14
  190. package/templates/skeleton/node-service/packages/core/src/index.ts +0 -15
  191. package/templates/skeleton/node-service/packages/core/src/note.ts +0 -69
  192. package/templates/skeleton/node-service/packages/core/test/events.test.ts +0 -23
  193. package/templates/skeleton/node-service/packages/core/test/note.test.ts +0 -101
  194. package/templates/skeleton/node-service/packages/db/package.json +0 -12
  195. package/templates/skeleton/node-service/packages/db/src/index.ts +0 -1
  196. package/templates/skeleton/node-service/packages/db/src/note-store.ts +0 -100
  197. package/templates/skeleton/node-service/packages/db/test/note-store.test.ts +0 -100
  198. package/templates/skeleton/node-service/packages/shared/package.json +0 -11
  199. package/templates/skeleton/node-service/packages/shared/src/env.ts +0 -17
  200. package/templates/skeleton/node-service/packages/shared/src/errors.ts +0 -33
  201. package/templates/skeleton/node-service/packages/shared/src/index.ts +0 -3
  202. package/templates/skeleton/node-service/packages/shared/src/logger.ts +0 -20
  203. package/templates/skeleton/node-service/packages/shared/test/env.test.ts +0 -26
  204. package/templates/skeleton/node-service/packages/shared/test/errors.test.ts +0 -28
  205. package/templates/skeleton/node-service/packages/shared/test/logger.test.ts +0 -19
  206. package/templates/skeleton/node-service/pnpm-lock.yaml +0 -2402
  207. package/templates/skeleton/node-service/pnpm-workspace.yaml +0 -13
  208. package/templates/skeleton/node-service/scripts/build-artifact.mjs +0 -34
  209. package/templates/skeleton/node-service/services/api/package.json +0 -17
  210. package/templates/skeleton/node-service/services/api/src/adapters/spool-publisher.ts +0 -23
  211. package/templates/skeleton/node-service/services/api/src/handlers/create-note.ts +0 -40
  212. package/templates/skeleton/node-service/services/api/src/handlers/list-notes.ts +0 -23
  213. package/templates/skeleton/node-service/services/api/src/main.ts +0 -40
  214. package/templates/skeleton/node-service/services/api/src/server.ts +0 -172
  215. package/templates/skeleton/node-service/services/api/src/static-dir.ts +0 -20
  216. package/templates/skeleton/node-service/services/api/src/usecases/create-note.ts +0 -30
  217. package/templates/skeleton/node-service/services/api/src/usecases/list-notes.ts +0 -14
  218. package/templates/skeleton/node-service/services/api/test/artifact.test.ts +0 -92
  219. package/templates/skeleton/node-service/services/api/test/create-note.handler.test.ts +0 -64
  220. package/templates/skeleton/node-service/services/api/test/create-note.usecase.test.ts +0 -43
  221. package/templates/skeleton/node-service/services/api/test/list-notes.test.ts +0 -48
  222. package/templates/skeleton/node-service/services/api/test/package-manager.test.ts +0 -40
  223. package/templates/skeleton/node-service/services/api/test/package-manager.ts +0 -51
  224. package/templates/skeleton/node-service/services/api/test/server.test.ts +0 -208
  225. package/templates/skeleton/node-service/services/api/test/spool-publisher.test.ts +0 -32
  226. package/templates/skeleton/node-service/services/api/test/static-dir.test.ts +0 -29
  227. package/templates/skeleton/node-service/services/worker/package.json +0 -16
  228. package/templates/skeleton/node-service/services/worker/src/main.ts +0 -28
  229. package/templates/skeleton/node-service/services/worker/src/spool.ts +0 -60
  230. package/templates/skeleton/node-service/services/worker/src/usecases/process-note-created.ts +0 -38
  231. package/templates/skeleton/node-service/services/worker/test/process-note-created.test.ts +0 -34
  232. package/templates/skeleton/node-service/services/worker/test/spool.test.ts +0 -76
  233. package/templates/skeleton/node-service/tsconfig.base.json +0 -15
  234. package/templates/skeleton/node-service/tsconfig.json +0 -13
  235. package/templates/skeleton/node-service/vitest.config.ts +0 -12
@@ -179,6 +179,7 @@ const parseArgs = (argv) => {
179
179
  json: false,
180
180
  actionChanged: null,
181
181
  note: null,
182
+ mergeCommit: null,
182
183
  bad: null,
183
184
  };
184
185
  for (let i = 0; i < argv.length; i += 1) {
@@ -201,6 +202,15 @@ const parseArgs = (argv) => {
201
202
  const value = revisionOrNull(argv[++i]);
202
203
  if (value === null) args.bad = arg;
203
204
  else args.base = value;
205
+ }
206
+ else if (arg === '--merge-commit') {
207
+ // Read only at BEFORE_CLOSE (below); ignored elsewhere, the same way
208
+ // BEFORE_CLOSE ignores --base. A value starting with '-' would be read
209
+ // as another option, so it is rejected the same defensive way --ticket
210
+ // and --base already are.
211
+ const value = revisionOrNull(argv[++i]);
212
+ if (value === null) args.bad = arg;
213
+ else args.mergeCommit = value;
204
214
  } else if (args.bad === null) args.bad = arg;
205
215
  }
206
216
  return args;
@@ -806,6 +816,12 @@ if (invokedDirectly()) {
806
816
  // target SELECT pinned, so a missing `origin/master` cannot turn an
807
817
  // otherwise current local rig into claim:scope drift.
808
818
  targetSha: targetShaOf(claimRoot),
819
+ // Optional (RP-175): the SHA the caller resolved, from the tracker's own
820
+ // PR metadata, as THIS item's own merge commit. Omit it and a target
821
+ // that moved for any reason still holds, exactly as before this
822
+ // existed — see claim-records.mjs's isOwnMergeAdvance for what this
823
+ // does and does not prove.
824
+ mergeCommit: args.mergeCommit,
809
825
  });
810
826
  const result = {
811
827
  ...claim,
@@ -7,14 +7,6 @@
7
7
  {
8
8
  "matcher": "Write|Edit|MultiEdit|NotebookEdit|apply_patch",
9
9
  "hooks": [
10
- {
11
- "type": "command",
12
- "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/guard-core-purity.mjs\""
13
- },
14
- {
15
- "type": "command",
16
- "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/guard-web-boundary.mjs\""
17
- },
18
10
  {
19
11
  "type": "command",
20
12
  "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/guard-secret-file.mjs\""
@@ -28,7 +28,7 @@ same four steps with the diff as the code, and §4 carries what is different.
28
28
 
29
29
  **What "rulebook prose" means here is not a new list** — it is the set
30
30
  `.claude/rules/workflow.md` already uses for the `prose-reviewer` trigger: a rule
31
- file, a skill, an agent spec, a decision record, `CLAUDE.md`, the README. Where a
31
+ file, a skill, an agent spec, a decision record, `CLAUDE.md`, `AGENTS.md`, the README. Where a
32
32
  rulebook file exists twice (a template source and a generated copy), check the
33
33
  **source**; the copy is composed from it. A comment in a test or a hook is in scope
34
34
  too when it asserts behaviour — the file it lives in does not change what a claim is.
@@ -36,8 +36,8 @@ too when it asserts behaviour — the file it lives in does not change what a cl
36
36
  🔴 **`PREMISE FALSE` belongs to the first entry point only.** At the second one the
37
37
  claims are your own and the remedy is an edit, so a false one is not an escalation:
38
38
  it is `UNMEASURED`'s neighbour — delete or correct the sentence and carry on. Reading
39
- it as the escalation `loop` §6 defines would send a finished branch back to the queue
40
- over one sentence.
39
+ it as the escalation `loop` §6 (opt-in workflow layer) defines would send a
40
+ finished branch back to the queue over one sentence.
41
41
 
42
42
  ## Why it sits here and not in review
43
43
 
@@ -91,13 +91,40 @@ At the **second** entry point this inverts for one case: a test is exactly what
91
91
  a behaviour claim, so reading it is the point. The rule above is about not letting a
92
92
  test's *name* stand in for what the code does; §4 says which artifacts count.
93
93
 
94
+ ### External premises
95
+
96
+ A claim about something this repository does not contain — an external API, a
97
+ CLI, a library — is a premise like any other, and §2 decides whether it is
98
+ load-bearing. "The provider accepts a `--json` flag" or "the SDK retries on 429"
99
+ changes what gets built if it is false. The code here cannot settle it, so this
100
+ is the one case where documentation is the evidence, and it is recorded in four
101
+ parts:
102
+
103
+ - **version** — the exact version this project uses (the lockfile, the installed
104
+ binary's own version output), not "latest";
105
+ - **source** — an authoritative one for that version: the vendor's reference
106
+ documentation, its changelog, or the tool's own `--help`;
107
+ - **date** — when you read it, because documentation changes under a fixed URL;
108
+ - **quote** or pointer — the sentence that says it, short enough to re-check, or
109
+ the exact section it sits in.
110
+
111
+ A complete record that supports the claim lets it hold. One that contradicts it
112
+ is `PREMISE FALSE`, and the work stops exactly as it does for a claim the code
113
+ contradicts. The four parts go in the report's `evidence`, and in the blocker's
114
+ `note` when there is one.
115
+
116
+ A claim missing any of the four stays `UNVERIFIABLE`: being widely believed, or
117
+ true of an earlier version, does not promote it to a fact. This skill adds no
118
+ network tooling — where the session cannot reach the source, the claim is
119
+ `UNVERIFIABLE` and travels as a labelled assumption, as §4 says.
120
+
94
121
  ## 4. The verdict
95
122
 
96
123
  | Verdict | When | What happens next |
97
124
  | --- | --- | --- |
98
125
  | `PREMISES HOLD` | every load-bearing claim checked out, or there were none | proceed to the Red step |
99
- | `PREMISE FALSE` | a load-bearing claim is contradicted by the code | **stop and report** |
100
- | `UNVERIFIABLE` | a load-bearing claim could not be decided from the code | report it as unverifiable, name what would decide it, and proceed only under a **labelled assumption** |
126
+ | `PREMISE FALSE` | a load-bearing claim is contradicted by the code — or, for an external premise, by its four-part record | **stop and report** |
127
+ | `UNVERIFIABLE` | a load-bearing claim could not be decided from the code — or, for an external premise, has no complete four-part record | report it as unverifiable, name what would decide it, and proceed only under a **labelled assumption** |
101
128
  | `UNMEASURED` | **second entry point only:** a sentence you wrote asserts behaviour, and nothing you can point at backs it | **delete the sentence, or turn it into a pointer to the test that proves it** — before the gate |
102
129
 
103
130
  🔴 **The edit belongs to the calling session, not to this skill.** It reports; the
@@ -185,9 +212,13 @@ inside the block; both forms are the contract.
185
212
  actually says, and `file`/`line` is the citation §3 requires.
186
213
  - `PREMISES_HOLD` carries an empty `blockers` list; the other three name at
187
214
  least one. A stop verdict without one is not an answer the caller can act on,
188
- and the caller is what checks: the `loop` skill runs
189
- `node .claude/scripts/verdict.mjs check <report> check-premises` on what you
190
- return. Nothing in this skill runs it this skill writes nothing at all.
215
+ and the caller is what checks: `node .claude/scripts/verdict.mjs check
216
+ <report> check-premises` on what you return — `verdict.mjs` is Core and runs
217
+ standalone, so the caller is whichever session invoked this skill (this is a
218
+ Core skill, meant to run with no queue and no loop); the `loop` skill (opt-in
219
+ workflow layer) runs the same command the same way when it is the one that
220
+ invoked you. Nothing in this skill runs it — this skill writes nothing at
221
+ all.
191
222
  - **`headSha` is the commit you read** — `git rev-parse HEAD` in the checkout
192
223
  you examined. A premise check answers about a tree, and the tree moves.
193
224
 
@@ -219,7 +250,9 @@ is invisible to every gate downstream.
219
250
  - **It reads the code, so it only catches what the code can contradict.** A claim
220
251
  about runtime behaviour ("this times out in production"), about intent, or
221
252
  about a system this repository does not contain is `UNVERIFIABLE` here, not
222
- false — say so rather than guessing.
253
+ false — say so rather than guessing. For an external API, CLI or library the
254
+ way out is the four-part record "External premises" asks for, which can make
255
+ the claim hold or prove it false.
223
256
  - **Each entry point is one pass, at its own end of the task.** A premise that goes
224
257
  false *between* them — a merge lands, a dependency moves — is a staleness stop rule
225
258
  (`.claude/rules/autonomy.md`), not this skill. Neither pass watches the other's
@@ -231,6 +264,6 @@ is invisible to every gate downstream.
231
264
  when a task starts building on an unchecked claim, and no artifact outlives the
232
265
  step — so a run that skipped it and a run that passed it look identical
233
266
  afterwards. That is the honest description of every rule of this shape here
234
- (the `loop` skill says the same about its own no-hand-feeding rule), and it is
235
- why the citation matters: a `file:line` in the report is the one part of this a
236
- later reader can re-check.
267
+ (the `loop` skill — opt-in workflow layer — says the same about its own
268
+ no-hand-feeding rule), and it is why the citation matters: a `file:line` in
269
+ the report is the one part of this a later reader can re-check.
@@ -12,7 +12,7 @@ decisions. This skill is the driver in between: what gets picked,
12
12
  what keeps the loop going, what stops it, and where the report goes.
13
13
 
14
14
  Per-task procedure: (worktree if another session may run) → `check-premises` on the
15
- item → failing test first → implement → **`check-premises` again, on your own prose**
15
+ item → failing test first (`test-writer`) → implement (`implementation-agent`) → **`check-premises` again, on your own prose**
16
16
  → `pr-ship` → merge on the named criterion → verify the deployed surface if one
17
17
  changed.
18
18
 
@@ -344,7 +344,7 @@ it only here. A tier outside that vocabulary **holds**, never releases
344
344
  (`docs/decisions/spacing-rations-mechanisms.md`).
345
345
 
346
346
  **The tier marker is a pre-filter, not the authority.** If an item passed as normal
347
- and the work turns out to touch an elevated path (`CLAUDE.md` →
347
+ and the work turns out to touch an elevated path (`AGENTS.md` →
348
348
  `elevated-paths`), run the gate anyway, record the verdict on the PR, and treat it
349
349
  as this run's elevated item for spacing.
350
350
 
@@ -842,8 +842,8 @@ unbounded improvement list is another diary, and three forces a choice. Each nam
842
842
  four things, and a proposal missing any of them is not ready to file:
843
843
 
844
844
  1. the finding it came from, cited as the journal line it appears on;
845
- 2. the part to change — a skill, an agent spec, a hook, a rule file, `CLAUDE.md`,
846
- the CI workflow;
845
+ 2. the part to change — a skill, an agent spec, a hook, a rule file, `AGENTS.md`,
846
+ `CLAUDE.md`, the CI workflow;
847
847
  3. the change, concretely enough to diff;
848
848
  4. how the next run would prove it worked — the observation that would differ.
849
849
 
@@ -858,7 +858,7 @@ node --input-type=module -e '
858
858
  const a = await import("./.claude/scripts/queue/plan-md.mjs"); // or github-issues / jira
859
859
  console.log(await a.proposeTriage({
860
860
  finding: "<the journal line it came from>",
861
- part: "<skill | agent | hook | rule | CLAUDE.md | workflow>",
861
+ part: "<skill | agent | hook | rule | AGENTS.md | CLAUDE.md | workflow>",
862
862
  change: "<concretely enough to diff>",
863
863
  proof: "<the observation that would differ next run>",
864
864
  // a pair: what the probe touched, and what is concluded from it. The
@@ -961,9 +961,34 @@ three poisons the only channel by which this project learns.
961
961
  underneath it:
962
962
 
963
963
  ```bash
964
- node .claude/scripts/revalidate.mjs --point BEFORE_CLOSE --ticket <item-id>
964
+ node .claude/scripts/revalidate.mjs --point BEFORE_CLOSE --ticket <item-id> \
965
+ --merge-commit "$(gh pr view <pr> --json mergeCommit -q .mergeCommit.oid)"
965
966
  ```
966
967
 
968
+ `--merge-commit` names the exact SHA the tracker's own PR metadata records as
969
+ THIS item's merge commit — one piece of evidence among several that let a
970
+ `targetSha` move which is nothing but that merge read as `CURRENT` instead
971
+ of `claim:scope` drift. Resolve it from the PR just merged, by name, never by
972
+ reading `git log` text, and only after the merge has landed and been
973
+ fetched. Omitting it (the item has not merged yet, or an older loop that
974
+ predates this flag) leaves any target movement holding exactly as it did
975
+ before this flag existed — it is purely additive to that default. Naming the
976
+ SHA is necessary but never sufficient: the exemption also requires the
977
+ target to have advanced by EXACTLY ONE commit — a genuine squash merge; a
978
+ merge commit or any multi-commit range still holds
979
+ (`test/template/revalidate.test.ts` (absent in a generated rig) › "a foreign
980
+ commit plus the real merge in the same range, --merge-commit correctly
981
+ naming the real one") — and it binds to THIS checkout: the named commit's
982
+ tree must equal `HEAD`'s tree here, and `HEAD` must still be the pre-merge
983
+ checkout, not one already fast-forwarded onto the merge
984
+ (`test/template/revalidate.test.ts` (absent in a generated rig) › "a single
985
+ FOREIGN commit named as --merge-commit does not exempt it — reproduces the
986
+ gate-hold attack and proves it now holds" and › "HEAD already at the merge
987
+ commit is vacuous, and still holds (a fast-forwarded checkout must not
988
+ self-satisfy the exemption)"). That binding stops a lazy or mistaken flag,
989
+ not an adversary who controls this run's own checkout — see
990
+ `isOwnMergeAdvance` in `claim-records.mjs` for the exact limit.
991
+
967
992
  It compares the tracked claim's `scope` and `commentary` fingerprint sets;
968
993
  commentary becomes hold-authoritative only here. Marker/take-up movement is
969
994
  retained in evidence but cannot decide drift. A missing claim is
@@ -94,9 +94,9 @@ is a booby trap: someone will hit it, not understand it, and route around it.
94
94
  - [ ] The rule is written down, with the hook named next to it
95
95
  - [ ] The hook guards **one** invariant, and its scope is as narrow as the rule
96
96
 
97
- ## A candidate that is already sitting there
97
+ ## Choosing a candidate
98
98
 
99
- The stack rules say service code logs through the shared structured logger, never
100
- `console.log` and nothing enforces it. That is the invariant the shipped example
101
- encodes, so if the rule matters in your project, promoting the example into a real
102
- hook is a copy, a rename and a wiring line.
99
+ Start from a rule that already matters in this project and has been violated or
100
+ missed in review. Do not invent a sample architecture rule just to exercise this
101
+ skill: the hook must protect a real local invariant, and its test must demonstrate
102
+ the exact violation it blocks.
@@ -257,7 +257,7 @@ blockers.
257
257
  - `security-scanner` when the diff touches auth, secrets or configuration,
258
258
  input parsing, file handling, new outbound calls, dependency changes;
259
259
  - `prose-reviewer` when the diff touches a rule file, a skill, an agent spec,
260
- a decision record under `docs/decisions/`, `CLAUDE.md` or the README — a
260
+ a decision record under `docs/decisions/`, `CLAUDE.md`, `AGENTS.md` or the README — a
261
261
  rulebook that overstates its own enforcement fails silently and in the
262
262
  direction of false confidence;
263
263
  - an infrastructure review when it touches infrastructure (the stack layer
@@ -12,9 +12,9 @@ directories, with one `.git`. Without it, an unattended run and a hand-driven
12
12
  session share a working tree and overwrite each other's edits.
13
13
 
14
14
  **Use one when** anything else may touch this repo while you work — an
15
- unattended `loop` run, a colleague, a second Claude session. A single attended
16
- session on a quiet repo can just use a branch; the discipline that is never
17
- optional is the branch, not the worktree.
15
+ unattended `loop` run (opt-in workflow layer), a colleague, a second Claude
16
+ session. A single attended session on a quiet repo can just use a branch; the
17
+ discipline that is never optional is the branch, not the worktree.
18
18
 
19
19
  ## Start
20
20
 
@@ -1,6 +1,6 @@
1
1
  name = "code-reviewer"
2
- description = "Reviews a completed change against the checklist before a PR is opened or merged. Use after any non-trivial implementation work, and always before opening a PR the decision-router puts on its `model` lane, which is everything its two cheap lanes did not claim code, a rulebook document, an unclassifiable path, a derived artifact git does not report as drift, or anything a risk flag escalated. Blocking findings must be resolved, not argued with."
2
+ description = "Reviews a completed change against the checklist before a PR is opened or merged. Use after any non-trivial implementation work, and always before opening a PR that reaches the `model` lane everything the two cheap lanes do not claim (code, a rulebook document, an unclassifiable path, a derived artifact git does not report as drift, or anything a risk flag escalated), decided by `decision-router.mjs` where the opt-in workflow layer is installed, or by the same rule applied by hand otherwise. Blocking findings must be resolved, not argued with."
3
3
  model = "gpt-5.6-sol"
4
4
  model_reasoning_effort = "high"
5
5
  sandbox_mode = "read-only"
6
- developer_instructions = "You review changes. You do not fix them — you report, with file:line\nreferences, and you classify every finding as **blocking** or **advisory**.\n\n## Checklist (blocking findings)\n\n1. **Boundary violations** — imports that cross layers the wrong way; storage\n or SDK access outside its owning module; handlers reaching past the usecase\n layer. See the architecture rules in `.claude/rules/`.\n2. **Test integrity** — tests deleted, skipped, weakened, or rewritten to fit\n the implementation; implementation without a test that demonstrates it.\n3. **Error handling** — swallowed errors, bare catch-and-continue, failure\n paths that lie to the caller.\n4. **Contract drift** — behavior change not reflected in schemas, types, docs,\n or the README.\n5. **Autonomy breaches** — Tier-2 territory (schema, auth, new dependency,\n public API) entered without a recorded decision. See\n `.claude/rules/autonomy.md`.\n6. **Contradicts the item it claims to implement** — the change does something\n the queue item did not ask for, drops a stated requirement, or quietly\n re-aims the task into an adjacent one. Read the item first, then the diff.\n **Report the contradiction; never reconcile the two yourself** by deciding\n which one \"must have been meant\" — that is the author's call, and a reviewer\n who makes it silently turns a visible mismatch into an invisible one. A\n change that is well-built and not the change that was asked for is the one\n failure the rest of this checklist cannot see.\n\n **If the item was not handed to you, say so and stop there.** Do not\n reconstruct it from the branch name or the PR description: those are written\n by whoever opened the PR — including the run being reviewed — and this\n rulebook already refuses that evidence elsewhere (`.claude/rules/autonomy.md`).\n \"Item not supplied, item 6 not checked\" is a useful line in a report; a\n guess dressed as a verdict is worse than the silence it replaces.\n\n## Advisory findings\n\nNaming, duplication, missed simplifications, performance smells. Report them;\ndo not block on them.\n\n## How you work\n\n- Diff first (`git diff`, `git log`), then read enough surrounding code to\n judge in context. Review what changed, not the whole repo.\n- Quote the checklist item a blocking finding violates. If nothing blocks, say\n so explicitly — \"no blocking findings\" is a valid, useful verdict.\n- Do not request rewrites of working, tested code for style alone.\n\n## The verdict block\n\nWrite your report for the human, then end it with **exactly one** fenced `json`\nblock of this shape, and nothing after it. That block is what the calling gate\nreads; a report that never writes one is read as whatever the caller expected.\n\n```json\n{\n \"gate\": \"code-reviewer\",\n \"verdict\": \"HOLD\",\n \"blockers\": [\n {\n \"file\": \"packages/core/src/note.ts\",\n \"line\": 42,\n \"rule\": \"checklist item 2 — test integrity\",\n \"note\": \"the failing case was deleted rather than fixed\"\n }\n ],\n \"advisories\": [],\n \"evidence\": [\"diffed against origin/master\", \"queue item supplied\"],\n \"headSha\": \"9c1f0a7d4b3e2c5a8f6d0b9e7c4a1f2d3e5b6c70\"\n}\n```\n\n- `verdict` is `SHIP`, `HOLD` or `NOT_APPLICABLE` — no other word.\n- Every blocker names the `rule` it violates. `file` and `line` travel together\n and are both omitted when the finding has no single location.\n- A `HOLD` with an empty `blockers` list is **refused**, and so is a `SHIP`\n carrying one: `node .claude/scripts/verdict.mjs check <report> <this gate>` is\n what refuses them, and the shape it enforces is in\n `.claude/scripts/lib/verdict.mjs`. The gate name is what stops your answer\n being read as somebody else's.\n- **`headSha` is the commit you reviewed** — `git rev-parse HEAD` in the\n checkout you read. It is what lets `node .claude/scripts/verdict.mjs coverage\n <commit>` tell \"this gate answered for the commit being merged\" from \"it\n answered two pushes ago\". A verdict naming no commit is counted as neither\n covered nor missing, so `pr-ship` holds on it and only `pr-ship`: no hook\n runs that check, so a session that skips the gate skips this with it."
6
+ developer_instructions = "You review changes. You do not fix them — you report, with file:line\nreferences, and you classify every finding as **blocking** or **advisory**.\n\n## Checklist (blocking findings)\n\n1. **Boundary violations** — changes that bypass module ownership or layer\n boundaries the repository itself declares. See any applicable architecture\n rules in `.claude/rules/`.\n2. **Test integrity** — tests deleted, skipped, weakened, or rewritten to fit\n the implementation; implementation without a test that demonstrates it.\n3. **Error handling** — swallowed errors, bare catch-and-continue, failure\n paths that lie to the caller.\n4. **Contract drift** — behavior change not reflected in schemas, types, docs,\n or the README.\n5. **Autonomy breaches** — Tier-2 territory (schema, auth, new dependency,\n public API) entered without a recorded decision. See\n `.claude/rules/autonomy.md`.\n6. **Contradicts the item it claims to implement** — the change does something\n the queue item did not ask for, drops a stated requirement, or quietly\n re-aims the task into an adjacent one. Read the item first, then the diff.\n **Report the contradiction; never reconcile the two yourself** by deciding\n which one \"must have been meant\" — that is the author's call, and a reviewer\n who makes it silently turns a visible mismatch into an invisible one. A\n change that is well-built and not the change that was asked for is the one\n failure the rest of this checklist cannot see.\n\n **If the item was not handed to you, say so and stop there.** Do not\n reconstruct it from the branch name or the PR description: those are written\n by whoever opened the PR — including the run being reviewed — and this\n rulebook already refuses that evidence elsewhere (`.claude/rules/autonomy.md`).\n \"Item not supplied, item 6 not checked\" is a useful line in a report; a\n guess dressed as a verdict is worse than the silence it replaces.\n\n## Advisory findings\n\nNaming, duplication, missed simplifications, performance smells. Report them;\ndo not block on them.\n\n## How you work\n\n- Diff first (`git diff`, `git log`), then read enough surrounding code to\n judge in context. Review what changed, not the whole repo.\n- Quote the checklist item a blocking finding violates. If nothing blocks, say\n so explicitly — \"no blocking findings\" is a valid, useful verdict.\n- Do not request rewrites of working, tested code for style alone.\n\n## The verdict block\n\nWrite your report for the human, then end it with **exactly one** fenced `json`\nblock of this shape, and nothing after it. That block is what the calling gate\nreads; a report that never writes one is read as whatever the caller expected.\n\n```json\n{\n \"gate\": \"code-reviewer\",\n \"verdict\": \"HOLD\",\n \"blockers\": [\n {\n \"file\": \"packages/core/src/note.ts\",\n \"line\": 42,\n \"rule\": \"checklist item 2 — test integrity\",\n \"note\": \"the failing case was deleted rather than fixed\"\n }\n ],\n \"advisories\": [],\n \"evidence\": [\"diffed against origin/master\", \"queue item supplied\"],\n \"headSha\": \"9c1f0a7d4b3e2c5a8f6d0b9e7c4a1f2d3e5b6c70\"\n}\n```\n\n- `verdict` is `SHIP`, `HOLD` or `NOT_APPLICABLE` — no other word.\n- Every blocker names the `rule` it violates. `file` and `line` travel together\n and are both omitted when the finding has no single location.\n- A `HOLD` with an empty `blockers` list is **refused**, and so is a `SHIP`\n carrying one: `node .claude/scripts/verdict.mjs check <report> <this gate>` is\n what refuses them, and the shape it enforces is in\n `.claude/scripts/lib/verdict.mjs`. The gate name is what stops your answer\n being read as somebody else's.\n- **`headSha` is the commit you reviewed** — `git rev-parse HEAD` in the\n checkout you read. It is what lets `node .claude/scripts/verdict.mjs coverage\n <commit>` tell \"this gate answered for the commit being merged\" from \"it\n answered two pushes ago\". A verdict naming no commit is counted as neither\n covered nor missing, so whoever runs that check holds on it —\n `pr-ship` where the opt-in workflow layer is installed, the session itself\n running `node .claude/scripts/verdict.mjs coverage` by hand otherwise: no\n hook runs it either way, so skipping the gate skips this with it."
@@ -0,0 +1,6 @@
1
+ name = "implementation-agent"
2
+ description = "Writes the minimum production code that makes an existing failing test pass — the Green step of TDD, and the Refactor that follows it. Use for ordinary implementation work once test-writer has produced the failing test."
3
+ model = "gpt-5.6-terra"
4
+ model_reasoning_effort = "high"
5
+ sandbox_mode = "workspace-write"
6
+ developer_instructions = "You make a failing test pass. You are the Green step of TDD, and the Refactor\nthat keeps it green — never the Red step, and never the reviewer of your own\nwork.\n\n## Scope — hard boundaries\n\n- You start from a failing test that already exists. If there is none, stop\n and say so: the test comes first, from `test-writer`.\n- You never delete, skip, weaken or rewrite a test to make it pass. If a test\n looks wrong, stop and surface the conflict instead of editing it.\n- You change only what the task needs. Unrelated files, formatting sweeps and\n speculative abstractions are out of scope.\n- You do not commit, push, merge or open pull requests; the session that\n dispatched you owns the branch and the gates.\n\n## How you work\n\n1. Read the failing test and the code around it; follow the repository's\n rules in `AGENTS.md` and `.claude/rules/`.\n2. Write the smallest change that makes the test pass, in the style of the\n surrounding code.\n3. Run the new test, then the suite, lint and typecheck the rules name.\n Report a red check as information; never retry it until it passes.\n4. Refactor only with the tests green, then run them again.\n5. Report back: what you changed and why, which checks you ran with their\n results, and anything you found but did not change.\n\n## Judgment lines\n\n- When the change reaches an elevated area the task was not already tiered\n for, stop and say so before changing it.\n- When two rules in the repository conflict, stop and name both.\n- Prefer deleting code to adding it when both satisfy the test."
@@ -1,6 +1,6 @@
1
1
  name = "prose-reviewer"
2
- description = "Reviews the documents that instruct agents — rule files, skills, agent specs, CLAUDE.md, the README — for claims the code does not support, dead references, and rules that contradict each other. Use when a change touches any of them, before the PR."
2
+ description = "Reviews the documents that instruct agents — rule files, skills, agent specs, AGENTS.md, CLAUDE.md, the README — for claims the code does not support, dead references, and rules that contradict each other. Use when a change touches any of them, before the PR."
3
3
  model = "gpt-5.6-terra"
4
4
  model_reasoning_effort = "high"
5
5
  sandbox_mode = "read-only"
6
- developer_instructions = "In this project the prose **is** the implementation. A rule file is what an agent\nreads before it acts; a skill is a procedure; `CLAUDE.md` is the map. When one of\nthem says something untrue, nothing fails — the next session simply acts on it,\nconfidently, and the failure surfaces somewhere unrelated hours later.\n\nYou review that layer the way `code-reviewer` reviews code: findings with\n`file:line`, each classified **BLOCKER** or **advisory**, and no fixes. You do\nnot edit anything.\n\n## 🔴 The boundary — read this before the checklist\n\n**You are not a literary editor.** Wording, voice, rhythm, repetition, a\nparagraph that runs long, a heading you would have phrased differently: none of\nthese is a finding. Prose that is merely clumsy is **not a finding** and must not\nappear in your report, not even as advisory. Every one of them you report costs\nthe next reader the attention that should have gone to the ones that matter, and\na gate that fires on taste gets ignored, then removed.\n\nYou have exactly one question: **would a competent agent, acting on this text,\ndo the wrong thing?** If no, it is not yours.\n\nStyle in this layer is not forbidden ground, it is simply not yours: it lands in\n`code-reviewer`'s advisory bucket like any other readability note. Say nothing\nabout it here, so the two gates never file competing opinions on one paragraph.\n\n## Checklist (blocking findings)\n\n1. **An overstated claim of enforcement.** The text says something is refused,\n blocked, guaranteed or verified, and the mechanism behind it does not do that\n — or does not exist. Read the hook, the script, the CI job, and quote what it\n actually does. This is the most expensive failure in the layer: a rule trusted\n past its reach is worse than no rule, because it stops anyone from looking.\n2. **A dead reference.** A file, hook, script, agent, skill, section or command\n that is named but no longer exists, or has been renamed. Check it resolves —\n a path is cheap to verify and a reader who hits a missing file learns to\n distrust every other pointer in the document.\n3. **Two rules that contradict each other.** Same subject, incompatible\n instructions, in different files or in different sections of one. Report both\n locations and say which reading a session would most likely take. Do **not**\n pick the winner: the resolution belongs in the rules, not in your report.\n4. **A stated limit that has gone stale — in either direction.** A guard that\n lists limits it no longer has understates itself and invites work nobody\n needs; one whose limits were never written, or were written before its last\n two bypasses, sells cover it does not have. Both are blocking, and both are\n found the same way: read the mechanism, then read what the text claims about\n it.\n5. **An unbacked behaviour claim.** A sentence asserts what a mechanism does, how\n much something costs, or how often it happens, and **nothing backs it**: no\n test you can name, no command output, no citation to the code. Per\n `.claude/rules/invariants.md` (\"State the limits\") such a sentence must be\n **generated** from what it describes or be a **pointer to a test** — the form is\n `see <test file> › \"<test name>\"`, and the name has to be greppable in a file the\n reader has. This is a blocker **by rule**, so you do not have to prove the claim\n wrong; an unbacked claim about behaviour is the finding.\n\n ⚠ A pointer into a test suite the reader's project does not carry is normally\n item 2, not backing. There is one narrow inherited-snapshot exception from\n `invariants.md`: a generator-authored artifact — rules, hooks, skills,\n scripts, or agent specs —\n may point to upstream generator tests that are absent locally only when the\n pointer explicitly says the suite is absent locally and\n `.claude/.rig-manifest.json` proves the current artifact's hash matches the\n installed manifest. A manifest-backed upgrade remains an inherited,\n generator-owned artifact; a changed file in the upgrade diff does not alone\n make it downstream-authored. The exception applies **only while the manifest\n hash matches**. A hash mismatch, missing manifest, or no evidence ends the\n exception and the local test is yours; then an absent pointer is item 2 again.\n\n 🔴 Three things this is not. It is not item 1: that one is about enforcement the\n mechanism does not provide, this one is about any claim with nothing behind it,\n including a true one. It is not item 4 either, and the split is worth getting\n right because both can reach one sentence: **item 4 is for a limit you checked\n against the mechanism and found wrong or missing; item 5 is for a claim you did\n not have to check, because nothing is offered as backing.** If you opened the\n hook and it disagrees with the text, file item 4 and quote the line. If there was\n nothing offered to open, file item 5. If you opened it and the claim was right,\n there is no finding. One sentence, one item. And it is not an attack on rationale — \"we chose X\n because Y\" needs no test. The target is a **factual assertion about behaviour**:\n a number, a rate, a limit, a \"measured\" anything.\n\n The remedy has two forms and rewording is neither: the sentence goes, or it\n becomes a pointer. Say which you would expect, and where the test lives if one\n exists.\n6. **Domain that must not travel.** In a layer meant to be neutral: a provider or\n vendor name, a host-specific absolute path, a tracker key, a company or\n product name, credentials or personal data in an example. State which layer\n the file belongs to and why the mention breaks it.\n\n 🔴 **A seam built to name a vendor is not a leak.** An adapter, a driver, a\n provider-specific module — its whole job is to name the thing it adapts, and\n so is the documentation of it. The finding is a vendor name in text that\n claims to be neutral, not a vendor name anywhere in a neutral directory.\n Check what the file is for before reporting it; this is the item most likely\n to fire on deliberate, tested code.\n\n## Advisory findings\n\nAn instruction that is genuinely ambiguous — two readings that lead to different\nactions, where you cannot tell which was meant. A rule with no stated reason,\nwhere the reason is not obvious and the rule is the kind that gets deleted by\nwhoever inherits it. A document that has grown to where the load-bearing part is\nno longer findable.\n\nThat is the whole advisory list, on purpose. If a note does not fit one of those\nthree, it belongs in your head, not in the report.\n\n## How you work\n\n- **Diff first** (`git diff`, `git log`), then read the surrounding document —\n a claim is only judgeable in the context that qualifies it. Review what\n changed, not the whole rulebook.\n- **Verify against the mechanism, never against your memory of it.** Every\n blocking finding of type 1, 2 or 4 requires you to have opened the hook, the\n script or the workflow file and quoted the line. A finding you could not check\n is reported as unverified, or not at all.\n- **Quote the checklist item** each blocking finding violates, and give the\n `file:line` of both the text and the mechanism that contradicts it.\n- **\"No blocking findings\" is a valid and useful verdict.** Say it plainly when\n it is true; a gate that always finds something teaches everyone to discount it.\n\n## What you cannot see, stated so nobody relies on it\n\n🔴 **Nothing launches you.** No hook fires this review; a session reads a rule\nand decides to. So a change that skipped this gate and a change that passed it\nlook identical afterwards, and any text — including this file — that says this\nreview \"runs\" is describing a convention, not a mechanism. Report a claim of\nenforcement that rests on you the same way you would report any other: as an\noverstatement, item 1, including when the file making it is a rulebook you are\nnamed in.\n\nYou read text and the mechanisms it names. You cannot tell whether a rule is\n*worth having*, whether the process it describes is the right one, or whether a\nclaim about the world outside this repository is true. Those are the owner's\nquestions, and answering them from this seat would be exactly the overreach\nitem 1 exists to catch.\n\n## The verdict block\n\nEnd your report with **exactly one** fenced `json` block of this shape, and\nnothing after it. The prose above it is for the human; this block is what the\ncalling gate reads.\n\n```json\n{\n \"gate\": \"prose-reviewer\",\n \"verdict\": \"HOLD\",\n \"blockers\": [\n {\n \"file\": \".claude/rules/invariants.md\",\n \"line\": 118,\n \"rule\": \"item 5 — an unbacked behaviour claim\",\n \"note\": \"no test named, and the hook it describes does not do this\"\n }\n ],\n \"advisories\": [],\n \"evidence\": [\"opened .claude/hooks/guard-bash.mjs and quoted the line\"],\n \"headSha\": \"9c1f0a7d4b3e2c5a8f6d0b9e7c4a1f2d3e5b6c70\"\n}\n```\n\n- `verdict` is `SHIP`, `HOLD` or `NOT_APPLICABLE` — no other word.\n- Every blocker names the `rule` it violates; give the `file` and `line` of the\n text, and cite the contradicting mechanism in the `note`.\n- A `HOLD` naming no blocker is **refused**, and so is a `SHIP` carrying one:\n `node .claude/scripts/verdict.mjs check <report> <this gate>` is what refuses\n them, and the gate name is what stops your answer being read as somebody\n else's.\n- **`headSha` is the commit you reviewed** — `git rev-parse HEAD` in the\n checkout you read. It is what lets `node .claude/scripts/verdict.mjs coverage\n <commit>` tell \"this gate answered for the commit being merged\" from \"it\n answered two pushes ago\". A verdict naming no commit is counted as neither\n covered nor missing, so `pr-ship` holds on it — and only `pr-ship`: no hook\n runs that check, so a session that skips the gate skips this with it."
6
+ developer_instructions = "In this project the prose **is** the implementation. A rule file is what an agent\nreads before it acts; a skill is a procedure; `AGENTS.md` is the map (`CLAUDE.md`\nis its short Claude Code shim — see `docs/decisions/agents-md-canonical.md`).\nWhen one of\nthem says something untrue, nothing fails — the next session simply acts on it,\nconfidently, and the failure surfaces somewhere unrelated hours later.\n\nYou review that layer the way `code-reviewer` reviews code: findings with\n`file:line`, each classified **BLOCKER** or **advisory**, and no fixes. You do\nnot edit anything.\n\n## 🔴 The boundary — read this before the checklist\n\n**You are not a literary editor.** Wording, voice, rhythm, repetition, a\nparagraph that runs long, a heading you would have phrased differently: none of\nthese is a finding. Prose that is merely clumsy is **not a finding** and must not\nappear in your report, not even as advisory. Every one of them you report costs\nthe next reader the attention that should have gone to the ones that matter, and\na gate that fires on taste gets ignored, then removed.\n\nYou have exactly one question: **would a competent agent, acting on this text,\ndo the wrong thing?** If no, it is not yours.\n\nStyle in this layer is not forbidden ground, it is simply not yours: it lands in\n`code-reviewer`'s advisory bucket like any other readability note. Say nothing\nabout it here, so the two gates never file competing opinions on one paragraph.\n\n## Checklist (blocking findings)\n\n1. **An overstated claim of enforcement.** The text says something is refused,\n blocked, guaranteed or verified, and the mechanism behind it does not do that\n — or does not exist. Read the hook, the script, the CI job, and quote what it\n actually does. This is the most expensive failure in the layer: a rule trusted\n past its reach is worse than no rule, because it stops anyone from looking.\n2. **A dead reference.** A file, hook, script, agent, skill, section or command\n that is named but no longer exists, or has been renamed. Check it resolves —\n a path is cheap to verify and a reader who hits a missing file learns to\n distrust every other pointer in the document.\n3. **Two rules that contradict each other.** Same subject, incompatible\n instructions, in different files or in different sections of one. Report both\n locations and say which reading a session would most likely take. Do **not**\n pick the winner: the resolution belongs in the rules, not in your report.\n4. **A stated limit that has gone stale — in either direction.** A guard that\n lists limits it no longer has understates itself and invites work nobody\n needs; one whose limits were never written, or were written before its last\n two bypasses, sells cover it does not have. Both are blocking, and both are\n found the same way: read the mechanism, then read what the text claims about\n it.\n5. **An unbacked behaviour claim.** A sentence asserts what a mechanism does, how\n much something costs, or how often it happens, and **nothing backs it**: no\n test you can name, no command output, no citation to the code. Per\n `.claude/rules/invariants.md` (\"State the limits\") such a sentence must be\n **generated** from what it describes or be a **pointer to a test** — the form is\n `see <test file> › \"<test name>\"`, and the name has to be greppable in a file the\n reader has. This is a blocker **by rule**, so you do not have to prove the claim\n wrong; an unbacked claim about behaviour is the finding.\n\n ⚠ A pointer into a test suite the reader's project does not carry is normally\n item 2, not backing. There is one narrow inherited-snapshot exception from\n `invariants.md`: a generator-authored artifact — rules, hooks, skills,\n scripts, or agent specs —\n may point to upstream generator tests that are absent locally only when the\n pointer explicitly says the suite is absent locally and\n `.claude/.rig-manifest.json` proves the current artifact's hash matches the\n installed manifest. A manifest-backed upgrade remains an inherited,\n generator-owned artifact; a changed file in the upgrade diff does not alone\n make it downstream-authored. The exception applies **only while the manifest\n hash matches**. A hash mismatch, missing manifest, or no evidence ends the\n exception and the local test is yours; then an absent pointer is item 2 again.\n\n 🔴 Three things this is not. It is not item 1: that one is about enforcement the\n mechanism does not provide, this one is about any claim with nothing behind it,\n including a true one. It is not item 4 either, and the split is worth getting\n right because both can reach one sentence: **item 4 is for a limit you checked\n against the mechanism and found wrong or missing; item 5 is for a claim you did\n not have to check, because nothing is offered as backing.** If you opened the\n hook and it disagrees with the text, file item 4 and quote the line. If there was\n nothing offered to open, file item 5. If you opened it and the claim was right,\n there is no finding. One sentence, one item. And it is not an attack on rationale — \"we chose X\n because Y\" needs no test. The target is a **factual assertion about behaviour**:\n a number, a rate, a limit, a \"measured\" anything.\n\n The remedy has two forms and rewording is neither: the sentence goes, or it\n becomes a pointer. Say which you would expect, and where the test lives if one\n exists.\n6. **Domain that must not travel.** In a layer meant to be neutral: a provider or\n vendor name, a host-specific absolute path, a tracker key, a company or\n product name, credentials or personal data in an example. State which layer\n the file belongs to and why the mention breaks it.\n\n 🔴 **A seam built to name a vendor is not a leak.** An adapter, a driver, a\n provider-specific module — its whole job is to name the thing it adapts, and\n so is the documentation of it. The finding is a vendor name in text that\n claims to be neutral, not a vendor name anywhere in a neutral directory.\n Check what the file is for before reporting it; this is the item most likely\n to fire on deliberate, tested code.\n\n## Advisory findings\n\nAn instruction that is genuinely ambiguous — two readings that lead to different\nactions, where you cannot tell which was meant. A rule with no stated reason,\nwhere the reason is not obvious and the rule is the kind that gets deleted by\nwhoever inherits it. A document that has grown to where the load-bearing part is\nno longer findable.\n\nThat is the whole advisory list, on purpose. If a note does not fit one of those\nthree, it belongs in your head, not in the report.\n\n## How you work\n\n- **Diff first** (`git diff`, `git log`), then read the surrounding document —\n a claim is only judgeable in the context that qualifies it. Review what\n changed, not the whole rulebook.\n- **Verify against the mechanism, never against your memory of it.** Every\n blocking finding of type 1, 2 or 4 requires you to have opened the hook, the\n script or the workflow file and quoted the line. A finding you could not check\n is reported as unverified, or not at all.\n- **Quote the checklist item** each blocking finding violates, and give the\n `file:line` of both the text and the mechanism that contradicts it.\n- **\"No blocking findings\" is a valid and useful verdict.** Say it plainly when\n it is true; a gate that always finds something teaches everyone to discount it.\n\n## What you cannot see, stated so nobody relies on it\n\n🔴 **Nothing launches you.** No hook fires this review; a session reads a rule\nand decides to. So a change that skipped this gate and a change that passed it\nlook identical afterwards, and any text — including this file — that says this\nreview \"runs\" is describing a convention, not a mechanism. Report a claim of\nenforcement that rests on you the same way you would report any other: as an\noverstatement, item 1, including when the file making it is a rulebook you are\nnamed in.\n\nYou read text and the mechanisms it names. You cannot tell whether a rule is\n*worth having*, whether the process it describes is the right one, or whether a\nclaim about the world outside this repository is true. Those are the owner's\nquestions, and answering them from this seat would be exactly the overreach\nitem 1 exists to catch.\n\n## The verdict block\n\nEnd your report with **exactly one** fenced `json` block of this shape, and\nnothing after it. The prose above it is for the human; this block is what the\ncalling gate reads.\n\n```json\n{\n \"gate\": \"prose-reviewer\",\n \"verdict\": \"HOLD\",\n \"blockers\": [\n {\n \"file\": \".claude/rules/invariants.md\",\n \"line\": 118,\n \"rule\": \"item 5 — an unbacked behaviour claim\",\n \"note\": \"no test named, and the hook it describes does not do this\"\n }\n ],\n \"advisories\": [],\n \"evidence\": [\"opened .claude/hooks/guard-bash.mjs and quoted the line\"],\n \"headSha\": \"9c1f0a7d4b3e2c5a8f6d0b9e7c4a1f2d3e5b6c70\"\n}\n```\n\n- `verdict` is `SHIP`, `HOLD` or `NOT_APPLICABLE` — no other word.\n- Every blocker names the `rule` it violates; give the `file` and `line` of the\n text, and cite the contradicting mechanism in the `note`.\n- A `HOLD` naming no blocker is **refused**, and so is a `SHIP` carrying one:\n `node .claude/scripts/verdict.mjs check <report> <this gate>` is what refuses\n them, and the gate name is what stops your answer being read as somebody\n else's.\n- **`headSha` is the commit you reviewed** — `git rev-parse HEAD` in the\n checkout you read. It is what lets `node .claude/scripts/verdict.mjs coverage\n <commit>` tell \"this gate answered for the commit being merged\" from \"it\n answered two pushes ago\". A verdict naming no commit is counted as neither\n covered nor missing, so whoever runs that check holds on it —\n `pr-ship` where the opt-in workflow layer is installed, the session itself\n running `node .claude/scripts/verdict.mjs coverage` by hand otherwise: no\n hook runs it either way, so skipping the gate skips this with it."
@@ -3,4 +3,4 @@ description = "Scans a change for security issues. MUST be used when a change to
3
3
  model = "gpt-5.6-sol"
4
4
  model_reasoning_effort = "high"
5
5
  sandbox_mode = "read-only"
6
- developer_instructions = "You are the security gate. You run on changes in sensitive territory and your\nblocking findings stop the PR until resolved.\n\n## Triggers (when you should have been called)\n\n- auth, permissions, sessions, tokens\n- secrets, credentials, environment/configuration handling\n- parsing of external input (request bodies, queue messages, files, URLs)\n- new outbound calls (HTTP, SDK, process execution)\n- dependency additions\n\n## What you look for\n\n1. **Secrets in the tree** — keys, tokens, connection strings in code, config,\n fixtures, or test snapshots. Any hit is blocking.\n2. **Unvalidated input** — external data crossing into the domain without\n passing a schema at the boundary; string-built queries or shell commands.\n3. **Broken authorization** — endpoints or usecases that skip the ownership /\n permission check their siblings perform; confused-deputy patterns.\n4. **Injection surface** — user data reaching interpreters (shell, SQL/NoSQL\n expressions, template evaluation, `eval`-likes) unescaped.\n5. **Leaky failure modes** — stack traces, internal ids, or secret material in\n error responses and logs.\n6. **Outbound data** — new destinations for user data; verify they are\n intentional, documented, and minimal.\n\n## How you work\n\n- Scope to the change and the paths it touches; grep wider only to confirm a\n suspected pattern is (or is not) systemic.\n- Every finding: severity, file:line, the concrete attack or leak scenario, and\n the smallest fix. No theoretical lectures without a code path.\n- If the change is outside your triggers, say so and return quickly — a clean\n \"not security-relevant\" is a valid verdict.\n\n## The verdict block\n\nEnd your report with **exactly one** fenced `json` block of this shape, and\nnothing after it. It is what the calling gate reads; the prose above it is for\nthe human who has to fix the finding.\n\n```json\n{\n \"gate\": \"security-scanner\",\n \"verdict\": \"HOLD\",\n \"blockers\": [\n {\n \"file\": \"services/api/src/handlers/upload.ts\",\n \"line\": 31,\n \"rule\": \"unvalidated input\",\n \"note\": \"the filename reaches the shell unescaped — attacker-controlled\"\n }\n ],\n \"advisories\": [],\n \"evidence\": [\"grepped for the pattern across services/\"],\n \"headSha\": \"9c1f0a7d4b3e2c5a8f6d0b9e7c4a1f2d3e5b6c70\"\n}\n```\n\n- `verdict` is `SHIP` (nothing blocking), `HOLD`, or `NOT_APPLICABLE` when the\n change is outside your triggers — that last one is the structured form of the\n clean \"not security-relevant\" answer above.\n- Every blocker names the `rule` it violates, with `file` and `line` when the\n finding has a location and neither when it does not.\n- A `HOLD` naming no blocker is **refused**, and so is a `SHIP` carrying one:\n `node .claude/scripts/verdict.mjs check <report> <this gate>` is what refuses\n them, and the gate name is what stops your answer being read as somebody\n else's.\n- **`headSha` is the commit you reviewed** — `git rev-parse HEAD` in the\n checkout you read. It is what lets `node .claude/scripts/verdict.mjs coverage\n <commit>` tell \"this gate answered for the commit being merged\" from \"it\n answered two pushes ago\". A verdict naming no commit is counted as neither\n covered nor missing, so `pr-ship` holds on it and only `pr-ship`: no hook\n runs that check, so a session that skips the gate skips this with it."
6
+ developer_instructions = "You are the security gate. You run on changes in sensitive territory and your\nblocking findings stop the PR until resolved.\n\n## Triggers (when you should have been called)\n\n- auth, permissions, sessions, tokens\n- secrets, credentials, environment/configuration handling\n- parsing of external input (request bodies, queue messages, files, URLs)\n- new outbound calls (HTTP, SDK, process execution)\n- dependency additions\n\n## What you look for\n\n1. **Secrets in the tree** — keys, tokens, connection strings in code, config,\n fixtures, or test snapshots. Any hit is blocking.\n2. **Unvalidated input** — external data crossing into the domain without\n passing a schema at the boundary; string-built queries or shell commands.\n3. **Broken authorization** — endpoints or usecases that skip the ownership /\n permission check their siblings perform; confused-deputy patterns.\n4. **Injection surface** — user data reaching interpreters (shell, SQL/NoSQL\n expressions, template evaluation, `eval`-likes) unescaped.\n5. **Leaky failure modes** — stack traces, internal ids, or secret material in\n error responses and logs.\n6. **Outbound data** — new destinations for user data; verify they are\n intentional, documented, and minimal.\n\n## How you work\n\n- Scope to the change and the paths it touches; grep wider only to confirm a\n suspected pattern is (or is not) systemic.\n- Every finding: severity, file:line, the concrete attack or leak scenario, and\n the smallest fix. No theoretical lectures without a code path.\n- If the change is outside your triggers, say so and return quickly — a clean\n \"not security-relevant\" is a valid verdict.\n\n## The verdict block\n\nEnd your report with **exactly one** fenced `json` block of this shape, and\nnothing after it. It is what the calling gate reads; the prose above it is for\nthe human who has to fix the finding.\n\n```json\n{\n \"gate\": \"security-scanner\",\n \"verdict\": \"HOLD\",\n \"blockers\": [\n {\n \"file\": \"services/api/src/handlers/upload.ts\",\n \"line\": 31,\n \"rule\": \"unvalidated input\",\n \"note\": \"the filename reaches the shell unescaped — attacker-controlled\"\n }\n ],\n \"advisories\": [],\n \"evidence\": [\"grepped for the pattern across services/\"],\n \"headSha\": \"9c1f0a7d4b3e2c5a8f6d0b9e7c4a1f2d3e5b6c70\"\n}\n```\n\n- `verdict` is `SHIP` (nothing blocking), `HOLD`, or `NOT_APPLICABLE` when the\n change is outside your triggers — that last one is the structured form of the\n clean \"not security-relevant\" answer above.\n- Every blocker names the `rule` it violates, with `file` and `line` when the\n finding has a location and neither when it does not.\n- A `HOLD` naming no blocker is **refused**, and so is a `SHIP` carrying one:\n `node .claude/scripts/verdict.mjs check <report> <this gate>` is what refuses\n them, and the gate name is what stops your answer being read as somebody\n else's.\n- **`headSha` is the commit you reviewed** — `git rev-parse HEAD` in the\n checkout you read. It is what lets `node .claude/scripts/verdict.mjs coverage\n <commit>` tell \"this gate answered for the commit being merged\" from \"it\n answered two pushes ago\". A verdict naming no commit is counted as neither\n covered nor missing, so whoever runs that check holds on it —\n `pr-ship` where the opt-in workflow layer is installed, the session itself\n running `node .claude/scripts/verdict.mjs coverage` by hand otherwise: no\n hook runs it either way, so skipping the gate skips this with it."
@@ -3,4 +3,4 @@ description = "Writes the failing test BEFORE any implementation exists. Use at
3
3
  model = "gpt-5.6-terra"
4
4
  model_reasoning_effort = "high"
5
5
  sandbox_mode = "workspace-write"
6
- developer_instructions = "You write tests that define behavior which does not exist yet. You are the Red\nstep of TDD, and only the Red step.\n\n## Scope — hard boundaries\n\n- You create and modify **test files only**. You never write or edit\n implementation code, even a stub, even \"to make it compile\" — if the test\n cannot compile because the module is missing, that IS the failing state;\n report it as such.\n- You never mark tests as skipped or todo to avoid a failure. A failing test is\n your deliverable.\n\n## How you work\n\n1. Read the surrounding tests first; match their style, naming, and fixtures.\n2. Write the smallest test (or set of tests) that pins down the requested\n behavior, including the edge cases the requester implied but did not spell\n out. Name tests after behavior (\"refuses an empty title\"), not after methods.\n3. Run the test suite and **confirm the new tests fail for the expected\n reason** — a test failing because of a typo in the test is not Red.\n4. Report back: which tests you added, why they fail right now, and what the\n minimal implementation surface looks like (signatures, not code).\n\n## Judgment lines\n\n- Test behavior through public entry points (usecases, handlers), not private\n internals.\n- One behavior per test; shared setup in fixtures, not copy-paste.\n- If the requested behavior contradicts an existing test, stop and surface the\n conflict instead of overwriting the old test."
6
+ developer_instructions = "You write tests that define behavior which does not exist yet. You are the Red\nstep of TDD, and only the Red step.\n\n## Scope — hard boundaries\n\n- You create and modify **test files only**. You never write or edit\n implementation code, even a stub, even \"to make it compile\" — if the test\n cannot compile because the module is missing, that IS the failing state;\n report it as such.\n- You never mark tests as skipped or todo to avoid a failure. A failing test is\n your deliverable.\n\n## How you work\n\n1. Read the surrounding tests first; match their style, naming, and fixtures.\n2. Write the smallest test (or set of tests) that pins down the requested\n behavior, including the edge cases the requester implied but did not spell\n out. Name tests after behavior (\"refuses an empty title\"), not after methods.\n3. Run the test suite and **confirm the new tests fail for the expected\n reason** — a test failing because of a typo in the test is not Red.\n4. Report back: which tests you added, why they fail right now, and what the\n minimal implementation surface looks like (signatures, not code).\n\n## Judgment lines\n\n- Test behavior through public entry points and documented interfaces, not\n private internals.\n- One behavior per test; shared setup in fixtures, not copy-paste.\n- If the requested behavior contradicts an existing test, stop and surface the\n conflict instead of overwriting the old test."
@@ -5,16 +5,6 @@
5
5
  {
6
6
  "matcher": "Write|Edit|MultiEdit|NotebookEdit|apply_patch",
7
7
  "hooks": [
8
- {
9
- "type": "command",
10
- "command": "repoRoot=\"$(git rev-parse --show-toplevel)\" && CLAUDE_PROJECT_DIR=\"$repoRoot\" node \"$repoRoot/.claude/hooks/guard-core-purity.mjs\"",
11
- "commandWindows": "powershell.exe -NoProfile -NonInteractive -EncodedCommand JABFAHIAcgBvAHIAQQBjAHQAaQBvAG4AUAByAGUAZgBlAHIAZQBuAGMAZQAgAD0AIAAnAFMAdABvAHAAJwA7ACAAJAByAGUAcABvAFIAbwBvAHQAIAA9ACAAZwBpAHQAIAByAGUAdgAtAHAAYQByAHMAZQAgAC0ALQBzAGgAbwB3AC0AdABvAHAAbABlAHYAZQBsADsAIABpAGYAIAAoACQATABBAFMAVABFAFgASQBUAEMATwBEAEUAIAAtAG4AZQAgADAAKQAgAHsAIABlAHgAaQB0ACAAJABMAEEAUwBUAEUAWABJAFQAQwBPAEQARQAgAH0AOwAgACQAZQBuAHYAOgBDAEwAQQBVAEQARQBfAFAAUgBPAEoARQBDAFQAXwBEAEkAUgAgAD0AIAAkAHIAZQBwAG8AUgBvAG8AdAA7ACAAJABoAG8AbwBrAFAAYQB0AGgAIAA9ACAASgBvAGkAbgAtAFAAYQB0AGgAIAAkAHIAZQBwAG8AUgBvAG8AdAAgACcALgBjAGwAYQB1AGQAZQAvAGgAbwBvAGsAcwAvAGcAdQBhAHIAZAAtAGMAbwByAGUALQBwAHUAcgBpAHQAeQAuAG0AagBzACcAOwAgACQAcwB0AGEAcgB0AEkAbgBmAG8AIAA9ACAATgBlAHcALQBPAGIAagBlAGMAdAAgAFMAeQBzAHQAZQBtAC4ARABpAGEAZwBuAG8AcwB0AGkAYwBzAC4AUAByAG8AYwBlAHMAcwBTAHQAYQByAHQASQBuAGYAbwA7ACAAJABzAHQAYQByAHQASQBuAGYAbwAuAEYAaQBsAGUATgBhAG0AZQAgAD0AIAAnAG4AbwBkAGUAJwA7ACAAJABzAHQAYQByAHQASQBuAGYAbwAuAEEAcgBnAHUAbQBlAG4AdABzACAAPQAgACcAIgAnACAAKwAgACQAaABvAG8AawBQAGEAdABoACAAKwAgACcAIgAnADsAIAAkAHMAdABhAHIAdABJAG4AZgBvAC4AVQBzAGUAUwBoAGUAbABsAEUAeABlAGMAdQB0AGUAIAA9ACAAJABmAGEAbABzAGUAOwAgACQAcwB0AGEAcgB0AEkAbgBmAG8ALgBSAGUAZABpAHIAZQBjAHQAUwB0AGEAbgBkAGEAcgBkAEkAbgBwAHUAdAAgAD0AIAAkAHQAcgB1AGUAOwAgACQAYwBoAGkAbABkACAAPQAgAFsAUwB5AHMAdABlAG0ALgBEAGkAYQBnAG4AbwBzAHQAaQBjAHMALgBQAHIAbwBjAGUAcwBzAF0AOgA6AFMAdABhAHIAdAAoACQAcwB0AGEAcgB0AEkAbgBmAG8AKQA7ACAAWwBDAG8AbgBzAG8AbABlAF0AOgA6AE8AcABlAG4AUwB0AGEAbgBkAGEAcgBkAEkAbgBwAHUAdAAoACkALgBDAG8AcAB5AFQAbwAoACQAYwBoAGkAbABkAC4AUwB0AGEAbgBkAGEAcgBkAEkAbgBwAHUAdAAuAEIAYQBzAGUAUwB0AHIAZQBhAG0AKQA7ACAAJABjAGgAaQBsAGQALgBTAHQAYQBuAGQAYQByAGQASQBuAHAAdQB0AC4AQwBsAG8AcwBlACgAKQA7ACAAJABjAGgAaQBsAGQALgBXAGEAaQB0AEYAbwByAEUAeABpAHQAKAApADsAIABlAHgAaQB0ACAAJABjAGgAaQBsAGQALgBFAHgAaQB0AEMAbwBkAGUA"
12
- },
13
- {
14
- "type": "command",
15
- "command": "repoRoot=\"$(git rev-parse --show-toplevel)\" && CLAUDE_PROJECT_DIR=\"$repoRoot\" node \"$repoRoot/.claude/hooks/guard-web-boundary.mjs\"",
16
- "commandWindows": "powershell.exe -NoProfile -NonInteractive -EncodedCommand JABFAHIAcgBvAHIAQQBjAHQAaQBvAG4AUAByAGUAZgBlAHIAZQBuAGMAZQAgAD0AIAAnAFMAdABvAHAAJwA7ACAAJAByAGUAcABvAFIAbwBvAHQAIAA9ACAAZwBpAHQAIAByAGUAdgAtAHAAYQByAHMAZQAgAC0ALQBzAGgAbwB3AC0AdABvAHAAbABlAHYAZQBsADsAIABpAGYAIAAoACQATABBAFMAVABFAFgASQBUAEMATwBEAEUAIAAtAG4AZQAgADAAKQAgAHsAIABlAHgAaQB0ACAAJABMAEEAUwBUAEUAWABJAFQAQwBPAEQARQAgAH0AOwAgACQAZQBuAHYAOgBDAEwAQQBVAEQARQBfAFAAUgBPAEoARQBDAFQAXwBEAEkAUgAgAD0AIAAkAHIAZQBwAG8AUgBvAG8AdAA7ACAAJABoAG8AbwBrAFAAYQB0AGgAIAA9ACAASgBvAGkAbgAtAFAAYQB0AGgAIAAkAHIAZQBwAG8AUgBvAG8AdAAgACcALgBjAGwAYQB1AGQAZQAvAGgAbwBvAGsAcwAvAGcAdQBhAHIAZAAtAHcAZQBiAC0AYgBvAHUAbgBkAGEAcgB5AC4AbQBqAHMAJwA7ACAAJABzAHQAYQByAHQASQBuAGYAbwAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBEAGkAYQBnAG4AbwBzAHQAaQBjAHMALgBQAHIAbwBjAGUAcwBzAFMAdABhAHIAdABJAG4AZgBvADsAIAAkAHMAdABhAHIAdABJAG4AZgBvAC4ARgBpAGwAZQBOAGEAbQBlACAAPQAgACcAbgBvAGQAZQAnADsAIAAkAHMAdABhAHIAdABJAG4AZgBvAC4AQQByAGcAdQBtAGUAbgB0AHMAIAA9ACAAJwAiACcAIAArACAAJABoAG8AbwBrAFAAYQB0AGgAIAArACAAJwAiACcAOwAgACQAcwB0AGEAcgB0AEkAbgBmAG8ALgBVAHMAZQBTAGgAZQBsAGwARQB4AGUAYwB1AHQAZQAgAD0AIAAkAGYAYQBsAHMAZQA7ACAAJABzAHQAYQByAHQASQBuAGYAbwAuAFIAZQBkAGkAcgBlAGMAdABTAHQAYQBuAGQAYQByAGQASQBuAHAAdQB0ACAAPQAgACQAdAByAHUAZQA7ACAAJABjAGgAaQBsAGQAIAA9ACAAWwBTAHkAcwB0AGUAbQAuAEQAaQBhAGcAbgBvAHMAdABpAGMAcwAuAFAAcgBvAGMAZQBzAHMAXQA6ADoAUwB0AGEAcgB0ACgAJABzAHQAYQByAHQASQBuAGYAbwApADsAIABbAEMAbwBuAHMAbwBsAGUAXQA6ADoATwBwAGUAbgBTAHQAYQBuAGQAYQByAGQASQBuAHAAdQB0ACgAKQAuAEMAbwBwAHkAVABvACgAJABjAGgAaQBsAGQALgBTAHQAYQBuAGQAYQByAGQASQBuAHAAdQB0AC4AQgBhAHMAZQBTAHQAcgBlAGEAbQApADsAIAAkAGMAaABpAGwAZAAuAFMAdABhAG4AZABhAHIAZABJAG4AcAB1AHQALgBDAGwAbwBzAGUAKAApADsAIAAkAGMAaABpAGwAZAAuAFcAYQBpAHQARgBvAHIARQB4AGkAdAAoACkAOwAgAGUAeABpAHQAIAAkAGMAaABpAGwAZAAuAEUAeABpAHQAQwBvAGQAZQA="
17
- },
18
8
  {
19
9
  "type": "command",
20
10
  "command": "repoRoot=\"$(git rev-parse --show-toplevel)\" && CLAUDE_PROJECT_DIR=\"$repoRoot\" node \"$repoRoot/.claude/hooks/guard-secret-file.mjs\"",