@tiphys/kernel 0.0.0 → 0.2.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 (207) hide show
  1. package/AGENTS.md +663 -0
  2. package/LICENSE +202 -0
  3. package/assurance-modes.yaml +299 -0
  4. package/checklists/clean-room.yaml +325 -0
  5. package/checklists/env-failure-diagnosis.yaml +68 -0
  6. package/checklists/flake-playbook.yaml +68 -0
  7. package/checklists/hazard-review.yaml +144 -0
  8. package/checklists/plan-review.yaml +103 -0
  9. package/dist/bin/tiphys.d.ts +2 -0
  10. package/dist/bin/tiphys.js +92 -0
  11. package/dist/src/adapters/load.d.ts +202 -0
  12. package/dist/src/adapters/load.js +440 -0
  13. package/dist/src/brief.d.ts +32 -0
  14. package/dist/src/brief.js +66 -0
  15. package/dist/src/checklists.d.ts +169 -0
  16. package/dist/src/checklists.js +310 -0
  17. package/dist/src/checks.d.ts +1539 -0
  18. package/dist/src/checks.js +5025 -0
  19. package/dist/src/cli.d.ts +9 -0
  20. package/dist/src/cli.js +68 -0
  21. package/dist/src/commands/brief.d.ts +92 -0
  22. package/dist/src/commands/brief.js +343 -0
  23. package/dist/src/commands/checklist.d.ts +42 -0
  24. package/dist/src/commands/checklist.js +168 -0
  25. package/dist/src/commands/cutover.d.ts +35 -0
  26. package/dist/src/commands/cutover.js +448 -0
  27. package/dist/src/commands/doctor.d.ts +264 -0
  28. package/dist/src/commands/doctor.js +1546 -0
  29. package/dist/src/commands/gates.d.ts +9 -0
  30. package/dist/src/commands/gates.js +360 -0
  31. package/dist/src/commands/init.d.ts +30 -0
  32. package/dist/src/commands/init.js +177 -0
  33. package/dist/src/commands/lock.d.ts +34 -0
  34. package/dist/src/commands/lock.js +340 -0
  35. package/dist/src/commands/mode.d.ts +40 -0
  36. package/dist/src/commands/mode.js +134 -0
  37. package/dist/src/commands/next.d.ts +130 -0
  38. package/dist/src/commands/next.js +597 -0
  39. package/dist/src/commands/plan.d.ts +20 -0
  40. package/dist/src/commands/plan.js +105 -0
  41. package/dist/src/commands/pool.d.ts +1 -0
  42. package/dist/src/commands/pool.js +139 -0
  43. package/dist/src/commands/resume.d.ts +1 -0
  44. package/dist/src/commands/resume.js +88 -0
  45. package/dist/src/commands/spawn.d.ts +1 -0
  46. package/dist/src/commands/spawn.js +195 -0
  47. package/dist/src/commands/status.d.ts +21 -0
  48. package/dist/src/commands/status.js +122 -0
  49. package/dist/src/commands/sync.d.ts +47 -0
  50. package/dist/src/commands/sync.js +341 -0
  51. package/dist/src/commands/teardown.d.ts +1 -0
  52. package/dist/src/commands/teardown.js +87 -0
  53. package/dist/src/commands/tuition.d.ts +21 -0
  54. package/dist/src/commands/tuition.js +218 -0
  55. package/dist/src/commands/validate.d.ts +78 -0
  56. package/dist/src/commands/validate.js +430 -0
  57. package/dist/src/commands/watch.d.ts +1 -0
  58. package/dist/src/commands/watch.js +172 -0
  59. package/dist/src/cutover.d.ts +584 -0
  60. package/dist/src/cutover.js +1444 -0
  61. package/dist/src/exclusion.d.ts +389 -0
  62. package/dist/src/exclusion.js +843 -0
  63. package/dist/src/exec/env.d.ts +278 -0
  64. package/dist/src/exec/env.js +334 -0
  65. package/dist/src/fleet.d.ts +223 -0
  66. package/dist/src/fleet.js +298 -0
  67. package/dist/src/gates/adapters/http-json.d.ts +5 -0
  68. package/dist/src/gates/adapters/http-json.js +283 -0
  69. package/dist/src/gates/adapters/migrations-command.d.ts +1 -0
  70. package/dist/src/gates/adapters/migrations-command.js +373 -0
  71. package/dist/src/gates/citations.d.ts +408 -0
  72. package/dist/src/gates/citations.js +1169 -0
  73. package/dist/src/gates/coverage.d.ts +375 -0
  74. package/dist/src/gates/coverage.js +836 -0
  75. package/dist/src/gates/credentials.d.ts +233 -0
  76. package/dist/src/gates/credentials.js +752 -0
  77. package/dist/src/gates/deploy.d.ts +1 -0
  78. package/dist/src/gates/deploy.js +33 -0
  79. package/dist/src/gates/gate-classes.d.ts +56 -0
  80. package/dist/src/gates/gate-classes.js +633 -0
  81. package/dist/src/gates/manifest.d.ts +99 -0
  82. package/dist/src/gates/manifest.js +208 -0
  83. package/dist/src/gates/merge-preconditions.d.ts +319 -0
  84. package/dist/src/gates/merge-preconditions.js +932 -0
  85. package/dist/src/gates/migrations.d.ts +1 -0
  86. package/dist/src/gates/migrations.js +36 -0
  87. package/dist/src/gates/pin.d.ts +114 -0
  88. package/dist/src/gates/pin.js +154 -0
  89. package/dist/src/gates/red-witness.d.ts +22 -0
  90. package/dist/src/gates/red-witness.js +482 -0
  91. package/dist/src/gates/release.d.ts +283 -0
  92. package/dist/src/gates/release.js +820 -0
  93. package/dist/src/gates/result.d.ts +116 -0
  94. package/dist/src/gates/result.js +91 -0
  95. package/dist/src/gates/run.d.ts +614 -0
  96. package/dist/src/gates/run.js +1614 -0
  97. package/dist/src/gates/schemas/citation-config.schema.json +59 -0
  98. package/dist/src/gates/schemas/coverage-config.schema.json +77 -0
  99. package/dist/src/gates/schemas/gate-manifest.schema.json +125 -0
  100. package/dist/src/gates/schemas/gate-result.schema.json +160 -0
  101. package/dist/src/gates/schemas/phase-declaration.schema.json +87 -0
  102. package/dist/src/gates/schemas/release-record.schema.json +119 -0
  103. package/dist/src/gates/schemas/verifier-config.schema.json +101 -0
  104. package/dist/src/gates/schemas/witness-spec.schema.json +110 -0
  105. package/dist/src/gates/scope.d.ts +131 -0
  106. package/dist/src/gates/scope.js +1018 -0
  107. package/dist/src/gates/suite.d.ts +217 -0
  108. package/dist/src/gates/suite.js +968 -0
  109. package/dist/src/gates/validate.d.ts +121 -0
  110. package/dist/src/gates/validate.js +414 -0
  111. package/dist/src/hooks.d.ts +84 -0
  112. package/dist/src/hooks.js +125 -0
  113. package/dist/src/index.d.ts +31 -0
  114. package/dist/src/index.js +30 -0
  115. package/dist/src/liveness.d.ts +321 -0
  116. package/dist/src/liveness.js +396 -0
  117. package/dist/src/lock.d.ts +256 -0
  118. package/dist/src/lock.js +792 -0
  119. package/dist/src/model-resolution.d.ts +159 -0
  120. package/dist/src/model-resolution.js +307 -0
  121. package/dist/src/modes.d.ts +149 -0
  122. package/dist/src/modes.js +258 -0
  123. package/dist/src/path-identity.d.ts +34 -0
  124. package/dist/src/path-identity.js +48 -0
  125. package/dist/src/plan.d.ts +73 -0
  126. package/dist/src/plan.js +153 -0
  127. package/dist/src/pool.d.ts +326 -0
  128. package/dist/src/pool.js +988 -0
  129. package/dist/src/roles.d.ts +461 -0
  130. package/dist/src/roles.js +776 -0
  131. package/dist/src/spawn.d.ts +482 -0
  132. package/dist/src/spawn.js +1003 -0
  133. package/dist/src/status.d.ts +116 -0
  134. package/dist/src/status.js +148 -0
  135. package/dist/src/task.d.ts +504 -0
  136. package/dist/src/task.js +307 -0
  137. package/dist/src/teardown.d.ts +39 -0
  138. package/dist/src/teardown.js +422 -0
  139. package/dist/src/tuition.d.ts +159 -0
  140. package/dist/src/tuition.js +311 -0
  141. package/dist/src/validate.d.ts +263 -0
  142. package/dist/src/validate.js +742 -0
  143. package/dist/src/version.d.ts +3 -0
  144. package/dist/src/version.js +38 -0
  145. package/dist/src/watcher.d.ts +275 -0
  146. package/dist/src/watcher.js +849 -0
  147. package/dist/src/witness/run.d.ts +299 -0
  148. package/dist/src/witness/run.js +1373 -0
  149. package/dist/src/witness/spec.d.ts +270 -0
  150. package/dist/src/witness/spec.js +475 -0
  151. package/dist/tsconfig.src.tsbuildinfo +1 -0
  152. package/gate-registry.yaml +526 -0
  153. package/gates.manifest.json +257 -0
  154. package/package.json +72 -3
  155. package/role-model-config.yaml +88 -0
  156. package/roles/README.md +128 -0
  157. package/roles/_shared-dispatch-contract.md +87 -0
  158. package/roles/adversarial-plan-reviewer.md +80 -0
  159. package/roles/clean-room-reviewer.md +140 -0
  160. package/roles/implementer.md +463 -0
  161. package/roles/investigator.md +138 -0
  162. package/roles/plan-writer.md +95 -0
  163. package/schemas/README.md +82 -0
  164. package/schemas/assurance-modes.schema.json +264 -0
  165. package/schemas/charter.schema.json +185 -0
  166. package/schemas/checklist.schema.json +114 -0
  167. package/schemas/cutover-state.schema.json +64 -0
  168. package/schemas/decision-record.schema.json +88 -0
  169. package/schemas/executor-record.schema.json +36 -0
  170. package/schemas/final-report.schema.json +90 -0
  171. package/schemas/finding.schema.json +106 -0
  172. package/schemas/gate-registry.schema.json +260 -0
  173. package/schemas/mechanism-index.schema.json +94 -0
  174. package/schemas/model-resolution.schema.json +362 -0
  175. package/schemas/plan.schema.json +300 -0
  176. package/schemas/report.schema.json +579 -0
  177. package/schemas/role-brief.schema.json +105 -0
  178. package/schemas/role-model-config.schema.json +90 -0
  179. package/schemas/status-line.schema.json +40 -0
  180. package/schemas/tuition.schema.json +191 -0
  181. package/schemas/verdict.schema.json +295 -0
  182. package/schemas/work-history.schema.json +183 -0
  183. package/schemas/write-bypass.schema.json +69 -0
  184. package/templates/charter.example.yaml +54 -0
  185. package/templates/decision-record.example.yaml +27 -0
  186. package/templates/final-report.example.yaml +80 -0
  187. package/templates/plan.example.yaml +87 -0
  188. package/templates/report.example.yaml +236 -0
  189. package/templates/warnings.md +74 -0
  190. package/templates/work-history.example.yaml +185 -0
  191. package/tuition/README.md +76 -0
  192. package/tuition/T-001.yaml +48 -0
  193. package/tuition/T-002.yaml +51 -0
  194. package/tuition/T-003.yaml +100 -0
  195. package/tuition/T-004.yaml +52 -0
  196. package/tuition/T-005.yaml +72 -0
  197. package/tuition/T-006.yaml +81 -0
  198. package/tuition/T-007.yaml +56 -0
  199. package/tuition/T-008.yaml +111 -0
  200. package/tuition/T-009.yaml +50 -0
  201. package/tuition/T-015.yaml +36 -0
  202. package/tuition/T-016.yaml +36 -0
  203. package/tuition/T-017.yaml +46 -0
  204. package/tuition/T-018.yaml +84 -0
  205. package/tuition/T-021.yaml +40 -0
  206. package/tuition/T-022.yaml +36 -0
  207. package/tuition/mechanism-index.yaml +256 -0
@@ -0,0 +1,526 @@
1
+ # THE CANONICAL GATE REGISTRY (kernel plan M3, M3-P2; R-094).
2
+ #
3
+ # This file is the canonical DECLARATION of this repository's gates, and R-094
4
+ # asks for one source that CI and the briefs both read. HOW FAR THAT IS TRUE
5
+ # TODAY, stated in the present tense only where it is:
6
+ #
7
+ # BRIEFS: delivered. The agent-rules gate section in CLAUDE.md is GENERATED
8
+ # from this file by `scripts/render-agent-rules-gates.mjs`. Editing that
9
+ # block by hand makes `node scripts/render-agent-rules-gates.mjs --check`
10
+ # exit nonzero, and the `gates` workflow runs that command as a step on both
11
+ # CI events, so a hand edit fails the build. Edit this file instead.
12
+ #
13
+ # CI: NOT YET. The `gates` workflow calls `scripts/m2-exit-test.sh`, which
14
+ # invokes the runner with `--manifest gates.manifest.json` on both arms.
15
+ # `--registry` occurs nowhere in either file. So a gate declared ONLY here
16
+ # does not run in CI, and `agent-rules-drift` is exactly that case: it runs
17
+ # because the workflow also carries a direct step for it. Closing this needs
18
+ # an edit to `scripts/m2-exit-test.sh`, which is not on M3-P2's declaration
19
+ # and is tracked with the orchestrator. The divergence is not left to be
20
+ # noticed: `test/gate-registry.test.ts` asserts it in BOTH directions, so a
21
+ # NEW script gate declared only here reddens rather than silently not
22
+ # running.
23
+ #
24
+ # PROMOTED, NOT INVENTED. Every `script` entry below is the M2-P1
25
+ # `gates.manifest.json` entry of the same id, carried across with its
26
+ # `command`, `unitLabel`, `applicability`, `parameters` and `precondition`
27
+ # byte-for-byte, plus the three fields this registry adds (`verified-by`,
28
+ # `modes`, `events`). SC-011's precondition semantics travel with the
29
+ # precondition blocks unchanged: a gate whose precondition is unmet reports
30
+ # not-applicable and NEVER green.
31
+ #
32
+ # WHAT IS NOT HERE, stated rather than left to be noticed. Kernel plan M3
33
+ # step 3 names project-specific gates `i18n`, `analytics`, `manifest regen`,
34
+ # `e2e` and `docs grep` as things to keep "declared-but-not-applicable on the
35
+ # kernel exactly as M2-P1 left them". M2-P1 left them NOWHERE: the delivered
36
+ # `gates.manifest.json` on main at 40427b6 declares eleven gates and none of
37
+ # those five is among them. Promoting a gate M2 never shipped would be
38
+ # inventing it, not promoting it, so the two entries that ARE structurally
39
+ # not-applicable on the kernel (`deploy`, `migrations`) are carried across and
40
+ # the other five are recorded as absent here and in
41
+ # delivery/work-history/m3-p2.md rather than fabricated.
42
+
43
+ kind: gate-registry
44
+ version: 1
45
+
46
+ # The ordered local commands every change must pass. Rendered into CLAUDE.md's
47
+ # gate section together with the table below, so that section has no
48
+ # hand-maintained list in it (M3-P2 criterion 6).
49
+ preflight:
50
+ - command: [npm, ci]
51
+ note: install exactly the lockfile, npm only, never pnpm or yarn
52
+ - command: [npm, run, build]
53
+ note: the type gate (tsc -b); emits dist/, which is never committed, and git status must be clean afterwards
54
+ - command: [node, --test]
55
+ note: sources are TypeScript run natively via Node type stripping, so the suite needs no prior build
56
+
57
+ gates:
58
+ - id: manifest-self-check
59
+ command: [node, bin/tiphys.ts, gates, self-check, --manifest, gates.manifest.json]
60
+ unitLabel: schema documents validated
61
+ applicability: required
62
+ verified-by: script
63
+ modes: [full, direct-pr, local-only]
64
+ events: [pull_request, push]
65
+
66
+ - id: coverage
67
+ command: [node, src/gates/coverage.ts]
68
+ unitLabel: finding ids checked
69
+ applicability: required
70
+ verified-by: script
71
+ modes: [full, direct-pr]
72
+ events: [pull_request, push]
73
+ precondition:
74
+ id: coverage-inventory-exists
75
+ kind: file-exists
76
+ path: delivery/requirements/migration-table.md
77
+
78
+ - id: credential-scrub
79
+ command: [node, src/gates/credentials.ts, credential-scrub]
80
+ unitLabel: credential sources probed
81
+ applicability: required
82
+ verified-by: script
83
+ modes: [full, direct-pr, local-only]
84
+ events: [pull_request, push]
85
+
86
+ - id: credential-token
87
+ command: [node, src/gates/credentials.ts, credential-token]
88
+ unitLabel: tokens probed
89
+ applicability: conditional
90
+ verified-by: script
91
+ modes: [full, direct-pr]
92
+ events: [pull_request]
93
+ precondition:
94
+ id: implementer-token-present-owner-action-a-3
95
+ kind: command-exit-zero
96
+ command: [node, -e, "process.exit(process.env.TIPHYS_IMPLEMENTER_TOKEN === undefined ? 1 : 0)"]
97
+
98
+ - id: suite
99
+ command: [node, src/gates/suite.ts, --pin-root, src, --pin-root, bin, --pin-root, test]
100
+ unitLabel: tests reported
101
+ applicability: required
102
+ verified-by: script
103
+ modes: [full, direct-pr, local-only]
104
+ events: [pull_request, push]
105
+ parameters: [base]
106
+
107
+ - id: citations
108
+ command: [node, src/gates/citations.ts]
109
+ unitLabel: citations resolved
110
+ applicability: required
111
+ verified-by: script
112
+ modes: [full, direct-pr]
113
+ events: [pull_request]
114
+ precondition:
115
+ id: citations-diff-touches-documents
116
+ kind: diff-touches
117
+ paths:
118
+ - delivery/plan/
119
+ - delivery/verification/
120
+ - delivery/decisions/
121
+ - delivery/tuition/
122
+ - delivery/requirements/
123
+ - delivery/STATE.md
124
+
125
+ - id: scope
126
+ command: [node, src/gates/scope.ts, --declarations, delivery/plan/phase-declarations]
127
+ unitLabel: changed paths audited
128
+ applicability: required
129
+ verified-by: script
130
+ modes: [full, direct-pr]
131
+ events: [pull_request]
132
+ parameters: [base, head]
133
+ precondition:
134
+ id: scope-branch-is-a-phase-branch
135
+ kind: branch-matches
136
+ pattern: claude/m[0-9]+-p[0-9]+-.*
137
+
138
+ - id: deploy
139
+ command: [node, src/gates/deploy.ts]
140
+ unitLabel: release verifications satisfied
141
+ applicability: conditional
142
+ verified-by: script
143
+ modes: [full]
144
+ events: [pull_request, push]
145
+ precondition:
146
+ id: "deploy-release-verification-declared (an unmet result here is STRUCTURAL in any pre-merge bundle, not local to this repository: release verification runs post-merge against a commit that exists only once the merge has happened; kernel plan M2 section 1.4, investigation observation O-3)"
147
+ kind: file-exists
148
+ path: release-verification.json
149
+
150
+ - id: migrations
151
+ command: [node, src/gates/migrations.ts]
152
+ unitLabel: migrations compared
153
+ applicability: conditional
154
+ verified-by: script
155
+ modes: [full]
156
+ events: [pull_request, push]
157
+ precondition:
158
+ id: "migrations-release-verification-declared (an unmet result here is STRUCTURAL in any pre-merge bundle, not local to this repository: release verification runs post-merge against a commit that exists only once the merge has happened; kernel plan M2 section 1.4, investigation observation O-3)"
159
+ kind: file-exists
160
+ path: release-verification.json
161
+
162
+ - id: clause-map
163
+ command: [node, scripts/check-clause-map.mjs]
164
+ unitLabel: clause-map rows checked
165
+ applicability: required
166
+ verified-by: script
167
+ modes: [full, direct-pr]
168
+ events: [pull_request]
169
+
170
+ - id: red-witness
171
+ command: [node, src/gates/red-witness.ts]
172
+ unitLabel: witnesses evaluated
173
+ applicability: required
174
+ verified-by: script
175
+ modes: [full, direct-pr]
176
+ events: [pull_request]
177
+ parameters: [base, head]
178
+ precondition:
179
+ id: red-witness-diff
180
+ kind: diff-touches
181
+ paths:
182
+ - src/
183
+ - bin/
184
+ - plugin/
185
+
186
+ # NEW IN M3-P2, entry one of three.
187
+ - id: agent-rules-drift
188
+ $comment: >-
189
+ R-094. The check that makes the BRIEFS half of "single source consumed by
190
+ CI and briefs" true rather than asserted: it re-renders CLAUDE.md's gate
191
+ block from this file and fails when the two differ. It is declared here
192
+ because D-M3-34 requires every M3 check to be declared in the registry,
193
+ and its `events` say which arms evaluate it. What actually EXECUTES it in
194
+ CI today is a step in the `gates` workflow, not this entry, because CI
195
+ runs `--manifest gates.manifest.json`; see this file's header. Both arms
196
+ are declared, and the workflow step carries no `if:`, because CLAUDE.md
197
+ drift is a property of `main`, not of a pull request (T-009).
198
+ command: [node, scripts/render-agent-rules-gates.mjs, --check]
199
+ unitLabel: rendered gate rows compared
200
+ applicability: required
201
+ verified-by: script
202
+ modes: [full, direct-pr, local-only]
203
+ events: [pull_request, push]
204
+
205
+ # NEW IN M3-P6 (R-094, D-M3-28, D-M3-34).
206
+ - id: brief-drift
207
+ $comment: >-
208
+ R-094's briefs half, applied to roles/implementer.md the way
209
+ agent-rules-drift applies it to CLAUDE.md: the brief's gate-list section is
210
+ a generated projection of THIS file for the mode its own begin marker
211
+ declares, and this check re-renders and compares. UNLIKE agent-rules-drift,
212
+ it is ALSO in gates.manifest.json, so on a pull request the runner executes
213
+ it as a real gate with a GateResult and M2-C-2's vacuity protection rather
214
+ than as a raw workflow step. WHY `events` IS pull_request ALONE, stated
215
+ exactly because "declared" and "runs" are not the same thing: the push arm
216
+ of scripts/m2-exit-test.sh has a hard-coded `--only` list of six gate ids
217
+ and that script is on no M3 phase's declaration, so a manifest gate outside
218
+ that list cannot run on push, and test/gate-registry.test.ts asserts
219
+ `events` against the harness rather than against a wish. The push arm is
220
+ covered instead by a direct step in .github/workflows/gates.yml carrying no
221
+ `if:`, because brief drift is a property of the default branch and not only
222
+ of a pull request (T-009). Both arms have a witness; they are not the same
223
+ witness, and that is recorded here rather than left to be discovered.
224
+ AND WHICH HALF IS THE TEETH, measured rather than assumed, because the
225
+ sentence above says the runner EXECUTES this as a gate and a reader could
226
+ take that to mean a red one fails the pull request. It does not on its own:
227
+ scripts/m2-exit-test.sh's PR expectation table lists eleven gate ids and
228
+ this is not one of them, and its assertion program compares only gates that
229
+ table names, plus global zero-error and zero-vacuous checks over every row
230
+ with no global zero-red. So an `error` here is caught by the harness and a
231
+ plain `red` is caught by the workflow step, which carries no `if:` and
232
+ therefore fails the job on either event. Adding a table row is an edit to
233
+ that harness, which is on no M3 phase's declaration.
234
+ command: [node, scripts/check-brief-drift.mjs, --check]
235
+ unitLabel: generated brief gate rows compared
236
+ applicability: required
237
+ verified-by: script
238
+ modes: [full, direct-pr, local-only]
239
+ events: [pull_request]
240
+
241
+ # NEW IN M3-P9, entry one of two (D-M3-34, section 2.2a).
242
+ - id: check-agents-references
243
+ $comment: >-
244
+ M3-P9 step 5. Every path `AGENTS.md` references exists AND the ANCHOR it
245
+ names occurs inside that path (criterion 2b), and `AGENTS.md` carries no
246
+ gate id list, no mode table and no model-tier table (criterion 3), because
247
+ section 1.5's anti-duplication rule is what stops the orchestrator brief
248
+ drifting away from the registry it summarizes. WHY BOTH EVENTS: a broken
249
+ reference on `main` is the state that matters, not only one proposed in a
250
+ pull request, which is T-009's rule that where behaviour forks on the CI
251
+ event both arms need a witness. WHAT EXECUTES IT IN CI, stated rather than
252
+ implied: `scripts/m2-exit-test.sh` invokes the runner with `--manifest
253
+ gates.manifest.json` on both arms and that file is on no M3 phase's
254
+ declaration, so a registry-only gate does not run through the runner.
255
+ A step in `.github/workflows/gates.yml` carrying no `if:` is what runs it,
256
+ exactly as for `agent-rules-drift`, and `test/gate-registry.test.ts`
257
+ records the divergence with its reason so the gap is declared rather than
258
+ discovered.
259
+ command: [node, scripts/check-agents-references.mjs]
260
+ unitLabel: references resolved
261
+ applicability: required
262
+ verified-by: script
263
+ modes: [full, direct-pr, local-only]
264
+ events: [pull_request, push]
265
+
266
+ # NEW IN M3-P9, entry two of two (M3R-004, DR-0012, T-001, T-007, D-M3-34).
267
+ - id: check-dual-review
268
+ $comment: >-
269
+ M3-P9 step 3b. The `decorrelated-review` duty made into a command with an
270
+ exit code: two verdicts for one head, distinct `produced-by` model
271
+ families (DR-0012 condition 1), distinct `framing` values (T-001), and
272
+ distinct `review-contract` values (T-007). The comparison itself is the
273
+ Kind B derived check `dual-review-decorrelation` in `src/checks.ts`; this
274
+ script is the runner around it, so deregistering the check is a real
275
+ witness rather than a simulated one (section 2.3 rule 3).
276
+ `events: [pull_request]` ALONE, and the reason is the plan's own: a merged
277
+ head has no pair of verdicts to compare. The PRECONDITION is what keeps
278
+ that from becoming a vacuous green: a gate that cannot reach a verdict
279
+ reports not-applicable with a reason, never green, which is M2-C-3 and
280
+ SC-011 applied to M3's own check. It probes with the script's own
281
+ `--precondition` arm rather than a `file-exists` path, because the
282
+ question is whether any verdict DOCUMENT exists under `delivery/review/`
283
+ and not whether that directory does; this repository has the directory and
284
+ has never had a verdict document in it.
285
+ THE COMMIT UNDER AUDIT IS A PARAMETER, AND ITS ABSENCE WAS THE DR-0047
286
+ SWEEP'S ONE HIGH FINDING (CR-VS-001). Without it the check grouped by the
287
+ head THE VERDICT DOCUMENTS THEMSELVES DECLARE, and nothing compared that
288
+ to the commit the gate was running against or established that it named a
289
+ commit at all. Reproduced: two decorrelated APPROVE verdicts naming
290
+ `deadbeefdeadbeefdeadbeefdeadbeefdeadbeef`, a sha `git cat-file -t`
291
+ reports as not an object, gave `check-dual-review: green`, 2 units, exit
292
+ 0, in a repository whose HEAD was a real commit and carried a further
293
+ commit of work no verdict mentioned. So ONE committed approving pair
294
+ turned this gate green for that phase at every later head, indefinitely.
295
+ The runner already had the mechanism: `scope` has declared
296
+ `parameters: [base, head]` since M2 and the runner appends the flags to
297
+ the gate command. This entry simply never declared it. With `--head` the
298
+ audited commit comes from the RUN and the declared head comes from the
299
+ EVIDENCE, and the second is measured against the first instead of
300
+ replacing it. With no `--head` the script falls back to the commit the
301
+ context's own HEAD resolves to, which is what keeps the direct workflow
302
+ step in `.github/workflows/gates.yml` correct.
303
+ command: [node, scripts/check-dual-review.mjs, .]
304
+ unitLabel: review verdicts examined for decorrelation
305
+ applicability: conditional
306
+ verified-by: script
307
+ modes: [full, direct-pr]
308
+ events: [pull_request]
309
+ parameters: [head]
310
+ precondition:
311
+ id: dual-review-verdicts-present
312
+ kind: command-exit-zero
313
+ command: [node, scripts/check-dual-review.mjs, --precondition, .]
314
+
315
+ # NEW IN M3-P10 (EXT-F-09, DR-0013 clause 5, D-M3-34).
316
+ - id: license
317
+ $comment: >-
318
+ M3-P10 step 2. EXT-F-09's five checks as one command with one exit code:
319
+ inventory the TRANSITIVE production dependency set, require license
320
+ metadata on every member, refuse any license off the declared allowlist,
321
+ require THIRD-PARTY-NOTICES whenever copied third-party code is declared
322
+ (plan v1 D-1's license note makes this declaration-driven, because
323
+ protocol reimplementation carries no notice obligation), and require
324
+ LICENSE in the `npm pack` listing. WHY IT IS A GATE AND NOT ONLY A RELEASE
325
+ STEP: what ships is a property of every change, not of the release that
326
+ happens to notice it, and DR-0013 marked a grown production tree the
327
+ costly direction. WHERE IT RUNS, STATED AS WHAT IS TRUE RATHER THAN AS AN
328
+ ABSOLUTE (corrected in M3-P10 fix round 1, clean-room finding HRB-7):
329
+ `.github/workflows/release.yml` runs it as an explicit STEP before
330
+ publishing, which is the enforcement point because it depends on no
331
+ lifecycle hook; `prepublishOnly` runs it on every publish path that runs
332
+ lifecycle scripts, and `--ignore-scripts` skips it, measured on npm
333
+ 11.18.0. The earlier wording said no publish path at all could skip it,
334
+ and that was false in a file that SHIPS. BOTH EVENTS, and the
335
+ reason is T-009's: a dependency added on `main` by a merge is exactly the
336
+ state that matters, and an arm with no witness is the arm that breaks.
337
+ WHAT EXECUTES IT IN CI, stated rather than implied: `scripts/m2-exit-test.sh`
338
+ invokes the runner with `--manifest gates.manifest.json` on both arms and
339
+ that file is on no M3 phase's declaration, so a registry-only gate does not
340
+ reach the runner. A step in `.github/workflows/gates.yml` carrying no `if:`
341
+ is what runs it, exactly as for `agent-rules-drift` and
342
+ `check-agents-references`, and `test/gate-registry.test.ts` records the
343
+ divergence with its reason so the gap is declared rather than discovered.
344
+ NOT CONDITIONAL, DELIBERATELY: there is no tree here with no production
345
+ dependencies, so a precondition would be a not-applicable arm that can
346
+ never be reached, and M2-C-2 already refuses a green with zero units.
347
+ command: [node, scripts/license-gate.mjs]
348
+ unitLabel: production packages licensed
349
+ applicability: required
350
+ verified-by: script
351
+ modes: [full, direct-pr, local-only]
352
+ events: [pull_request, push]
353
+
354
+ # NEW IN M4-P14, entry one of two (R-041, DR-0028, DR-0029, M4-P13).
355
+ - id: typecheck
356
+ $comment: >-
357
+ R-041, retargeted by M4-P13 under DR-0028. The kernel ships the gate
358
+ CLASS and no command; the project ships the command. This repository is
359
+ the project, so this is the kernel dogfooding the extension point rather
360
+ than being exempt from it (DR-0029: "the kernel becomes just another
361
+ project under the scheme, with its own registry"). The subject is the one
362
+ M4 has, package.json's build script, which CLAUDE.md already calls the
363
+ type gate. WHY IT IS NOT THE SAME THING AS THE `build` PREFLIGHT STEP:
364
+ preflight is an ordered local command with no record, no units and no
365
+ vacuity protection, so nothing downstream can tell a build that checked
366
+ the whole project from one that checked nothing. This entry produces a
367
+ GateResult whose `units` is the count of distinct paths `tsc --listFiles`
368
+ PRINTED, so a compiler that read no file is error rather than green.
369
+ WHY `--force` IS IN THE COMMAND: without it `tsc -b` is incremental and
370
+ the printed file list depends on the state of `dist/`, which would make
371
+ the unit count a property of the runner's working directory instead of
372
+ the code. `events: [pull_request]` alone, and it is derived rather than
373
+ assigned: `scripts/m2-exit-test.sh` declares the push arm's gate set once
374
+ in MAIN_ONLY_GATES and this id is not in it, so the push arm cannot run
375
+ it and test/gate-registry.test.ts asserts `events` against that harness.
376
+ command: [node, src/gates/gate-classes.ts, typecheck, --project, tsconfig.src.json, --project, tsconfig.test.json, --project, plugin/tsconfig.json]
377
+ unitLabel: source files type-checked
378
+ applicability: required
379
+ verified-by: script
380
+ modes: [full, direct-pr, local-only]
381
+ events: [pull_request]
382
+
383
+ # NEW IN M4-P14, entry two of two (DR-0029 part 2a, M4-P13).
384
+ - id: gate-classes
385
+ $comment: >-
386
+ DR-0029 part 2a made executable. A PHASE declares at least one gate in
387
+ each required class (`correctness`, `scope`, `review`), or declares an
388
+ escape that is DATA: `not-applicable` WITH a recorded reason, or
389
+ `not-yet-establishable` NAMING the phase that will establish it. The
390
+ requirement attaches to the phase and not to the project, because "this
391
+ project must have a correctness gate" is unsatisfiable on day zero while
392
+ "this phase either asserts correctness or declares when it will" is
393
+ satisfiable from the first commit. The property protected, in DR-0029's
394
+ words: you can start from nothing; you can never SILENTLY have nothing.
395
+ THE ESCAPES ARE PRINTED ON THE GREEN ARM, not merely permitted, which is
396
+ the same disclosure trade src/gates/scope.ts makes for a declaration
397
+ addition under M3-P11 change B: a reviewer who does not read the printed
398
+ line gets no protection from it. The PRECONDITION is branch-matches, the
399
+ same one `scope` carries and for the same reason: a phase declaration is
400
+ selected by phase id, and a non-phase branch has none, so the gate
401
+ reports not-applicable with an evaluated precondition rather than a
402
+ vacuous green (SC-011, M2-C-3). `events: [pull_request]` alone because
403
+ the push arm of scripts/m2-exit-test.sh passes no `--phase` at all, so
404
+ there is no declaration for it to select.
405
+ command: [node, src/gates/gate-classes.ts, gate-classes, --declarations, delivery/plan/phase-declarations, --registry, gate-registry.yaml]
406
+ unitLabel: declared gate classes checked
407
+ applicability: required
408
+ verified-by: script
409
+ modes: [full, direct-pr]
410
+ events: [pull_request]
411
+ parameters: [phase]
412
+ precondition:
413
+ id: gate-classes-branch-is-a-phase-branch
414
+ kind: branch-matches
415
+ pattern: claude/m[0-9]+-p[0-9]+-.*
416
+
417
+ # NEW IN M4-P12 (DR-0012, DR-0036, T-009, R-064, R-065a).
418
+ - id: merge-preconditions
419
+ $comment: >-
420
+ M4-P12. The artifact DR-0012's delegated grant has never had: one record
421
+ per condition, each carrying the HEAD SHA it was evaluated against, so
422
+ that "the six conditions held" stops being a sentence somebody wrote and
423
+ becomes a thing a reader can check. It is a PRECONDITION READER and not a
424
+ merge command: M4-D-09 puts the merge capability in the plugin at cutover
425
+ and DR-0036 keeps merge authority with the current process for the whole
426
+ of M4, so the orchestrator reads this record before merging, by hand.
427
+ CONDITIONS 1 AND 2 ARE COMPOSED, NOT REIMPLEMENTED. M4-P10 shipped
428
+ `dual-review-decorrelation` and `verdict-pair-approves` as derived checks
429
+ in src/checks.ts; this gate is a second caller of the same exported
430
+ primitives and runs those checks BY ID, so deregistering either one is
431
+ visible here as an `error` rather than absorbed as a pass.
432
+ `applicability: conditional`, AND THE PRECONDITION IS ANSWERED IN TWO
433
+ PLACES BECAUSE IT IS TWO QUESTIONS. The declared `command-exit-zero` block
434
+ is the SAME probe check-dual-review declares, and it answers the question
435
+ a registry precondition can answer: does this project have any committed
436
+ review verdict at all. The narrower question, whether a verdict names THIS
437
+ head, needs the run's `--head`, and a precondition command is spawned
438
+ without it (src/gates/run.ts:1524 appends the parameter flags to the GATE
439
+ command, not to the precondition's). So the gate answers that half itself
440
+ and emits its own PreconditionRecord with `met: false`, which is SC-011's
441
+ requirement that not-applicable ASSERT an evaluated precondition rather
442
+ than stand for silence. Both arms are not-applicable and each names which
443
+ question was unmet, so a reader is never told only that the gate did not
444
+ run.
445
+ `events: [pull_request]` ALONE, and it is derived rather than assigned:
446
+ scripts/m2-exit-test.sh declares the push arm's gate set once in
447
+ MAIN_ONLY_GATES and this id is not in it, so the push arm cannot run it,
448
+ and test/gate-registry.test.ts asserts `events` against that harness
449
+ rather than against a wish. It is ALSO in gates.manifest.json, because CI
450
+ invokes the runner with `--manifest`: a gate declared only here is
451
+ declared and never executed, which is a gate that cannot go red.
452
+ THE API IS PROBED, NEVER ASSUMED. CLAUDE.md standing warning 6 records
453
+ REST reachability here measured both ways on different days with the
454
+ cause unestablished, so the gate probes first and reports `error` with
455
+ units 0 when it cannot reach the API. Never `not-applicable`, never green:
456
+ M2-C-3, and the hazard this whole entry exists against is a merge
457
+ precondition check that is green because it could not look.
458
+ command: [node, src/gates/merge-preconditions.ts]
459
+ unitLabel: merge preconditions evaluated
460
+ applicability: conditional
461
+ verified-by: script
462
+ modes: [full, direct-pr]
463
+ events: [pull_request]
464
+ parameters: [head, phase]
465
+ precondition:
466
+ id: merge-preconditions-verdicts-present
467
+ kind: command-exit-zero
468
+ command: [node, scripts/check-dual-review.mjs, --precondition, .]
469
+
470
+ # NEW IN M3-P2, entry two of three (R-043, D-11).
471
+ - id: unit-tests-for-changed-service-methods
472
+ $comment: >-
473
+ R-043. Every changed service method has a unit test. D-11 settles that
474
+ this is NOT reliably computable from a diff (mapping changed methods to
475
+ tests produces false positives and negatives on any nontrivial codebase),
476
+ so it is verified by a clean-room checklist probe at L3 rather than by a
477
+ script at L1, and the registry says so in `verified-by` instead of leaving
478
+ a reader to infer it from a missing command. The probe id below is
479
+ M3-P7's to supply. WHAT THE RUNNER DOES WITH THIS ENTRY, stated exactly
480
+ because criterion 3c is about not confusing these two: it is NOT
481
+ executed and its precondition is NOT evaluated. There is no command to
482
+ run, so the runner selects it out before any precondition is reached and
483
+ reports it on stdout and in `summary.declaredByChecklist` as declared
484
+ and not executed. It produces no record, no evidence directory and no
485
+ status, and `not-applicable` is not among the things it can report. The
486
+ `precondition` below is a DECLARATION for M3-P7's checklist, not
487
+ something this runner evaluates.
488
+ unitLabel: changed service methods checked
489
+ applicability: conditional
490
+ verified-by: clean-room-checklist
491
+ probe: unit-tests-for-changed-service-methods
492
+ modes: [full, direct-pr]
493
+ events: [pull_request]
494
+ precondition:
495
+ id: clean-room-checklist-present
496
+ kind: file-exists
497
+ path: checklists/clean-room.yaml
498
+
499
+ # NEW IN M3-P2, entry three of three (R-044, D-11).
500
+ - id: fixtures-for-changed-component-states
501
+ $comment: >-
502
+ R-044. Every changed component state has a story or fixture. Same shape as
503
+ R-043 and settled the same way by D-11: "component state" is not
504
+ machine-enumerable, so the gate is a clean-room checklist probe. Not
505
+ executed and its precondition not evaluated, for the reason spelled out
506
+ on the entry above. Same declared precondition, so M3-P7's checklist is
507
+ what both wait on.
508
+ unitLabel: changed component states checked
509
+ applicability: conditional
510
+ verified-by: clean-room-checklist
511
+ probe: fixtures-for-changed-component-states
512
+ modes: [full, direct-pr]
513
+ events: [pull_request]
514
+ precondition:
515
+ id: clean-room-checklist-present
516
+ kind: file-exists
517
+ path: checklists/clean-room.yaml
518
+
519
+ # Carried across from gates.manifest.json unchanged. M2-P2 rule (e) DERIVES a
520
+ # witness class from this list rather than trusting an implementer to declare
521
+ # one, so shortening it weakens the red-witness gate.
522
+ destructiveCommands:
523
+ - pool destroy
524
+ - teardown
525
+ - src/pool.ts
526
+ - src/teardown.ts