@gate-forge/core 0.1.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 (242) hide show
  1. package/LICENSE +202 -0
  2. package/dist/baselines/adoption.d.ts +76 -0
  3. package/dist/baselines/adoption.d.ts.map +1 -0
  4. package/dist/baselines/adoption.js +163 -0
  5. package/dist/baselines/adoption.js.map +1 -0
  6. package/dist/baselines/index.d.ts +102 -0
  7. package/dist/baselines/index.d.ts.map +1 -0
  8. package/dist/baselines/index.js +186 -0
  9. package/dist/baselines/index.js.map +1 -0
  10. package/dist/canonical-json.d.ts +51 -0
  11. package/dist/canonical-json.d.ts.map +1 -0
  12. package/dist/canonical-json.js +112 -0
  13. package/dist/canonical-json.js.map +1 -0
  14. package/dist/classifier/bind.d.ts +64 -0
  15. package/dist/classifier/bind.d.ts.map +1 -0
  16. package/dist/classifier/bind.js +188 -0
  17. package/dist/classifier/bind.js.map +1 -0
  18. package/dist/classifier/classify.d.ts +154 -0
  19. package/dist/classifier/classify.d.ts.map +1 -0
  20. package/dist/classifier/classify.js +1207 -0
  21. package/dist/classifier/classify.js.map +1 -0
  22. package/dist/classifier/glob.d.ts +48 -0
  23. package/dist/classifier/glob.d.ts.map +1 -0
  24. package/dist/classifier/glob.js +104 -0
  25. package/dist/classifier/glob.js.map +1 -0
  26. package/dist/classifier/index.d.ts +10 -0
  27. package/dist/classifier/index.d.ts.map +1 -0
  28. package/dist/classifier/index.js +21 -0
  29. package/dist/classifier/index.js.map +1 -0
  30. package/dist/classifier/schema.d.ts +150 -0
  31. package/dist/classifier/schema.d.ts.map +1 -0
  32. package/dist/classifier/schema.js +139 -0
  33. package/dist/classifier/schema.js.map +1 -0
  34. package/dist/config/index.d.ts +259 -0
  35. package/dist/config/index.d.ts.map +1 -0
  36. package/dist/config/index.js +523 -0
  37. package/dist/config/index.js.map +1 -0
  38. package/dist/fingerprints.d.ts +56 -0
  39. package/dist/fingerprints.d.ts.map +1 -0
  40. package/dist/fingerprints.js +59 -0
  41. package/dist/fingerprints.js.map +1 -0
  42. package/dist/graph/build.d.ts +16 -0
  43. package/dist/graph/build.d.ts.map +1 -0
  44. package/dist/graph/build.js +423 -0
  45. package/dist/graph/build.js.map +1 -0
  46. package/dist/graph/index.d.ts +12 -0
  47. package/dist/graph/index.d.ts.map +1 -0
  48. package/dist/graph/index.js +10 -0
  49. package/dist/graph/index.js.map +1 -0
  50. package/dist/graph/schema.d.ts +448 -0
  51. package/dist/graph/schema.d.ts.map +1 -0
  52. package/dist/graph/schema.js +237 -0
  53. package/dist/graph/schema.js.map +1 -0
  54. package/dist/graph/symbols.d.ts +129 -0
  55. package/dist/graph/symbols.d.ts.map +1 -0
  56. package/dist/graph/symbols.js +177 -0
  57. package/dist/graph/symbols.js.map +1 -0
  58. package/dist/graph/util.d.ts +40 -0
  59. package/dist/graph/util.d.ts.map +1 -0
  60. package/dist/graph/util.js +41 -0
  61. package/dist/graph/util.js.map +1 -0
  62. package/dist/index.d.ts +683 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +563 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/mapping/resolve.d.ts +177 -0
  67. package/dist/mapping/resolve.d.ts.map +1 -0
  68. package/dist/mapping/resolve.js +523 -0
  69. package/dist/mapping/resolve.js.map +1 -0
  70. package/dist/policy/coverage.d.ts +79 -0
  71. package/dist/policy/coverage.d.ts.map +1 -0
  72. package/dist/policy/coverage.js +119 -0
  73. package/dist/policy/coverage.js.map +1 -0
  74. package/dist/policy/evaluate.d.ts +275 -0
  75. package/dist/policy/evaluate.d.ts.map +1 -0
  76. package/dist/policy/evaluate.js +382 -0
  77. package/dist/policy/evaluate.js.map +1 -0
  78. package/dist/policy/index.d.ts +15 -0
  79. package/dist/policy/index.d.ts.map +1 -0
  80. package/dist/policy/index.js +14 -0
  81. package/dist/policy/index.js.map +1 -0
  82. package/dist/policy/trusted.d.ts +59 -0
  83. package/dist/policy/trusted.d.ts.map +1 -0
  84. package/dist/policy/trusted.js +96 -0
  85. package/dist/policy/trusted.js.map +1 -0
  86. package/dist/provenance.d.ts +170 -0
  87. package/dist/provenance.d.ts.map +1 -0
  88. package/dist/provenance.js +306 -0
  89. package/dist/provenance.js.map +1 -0
  90. package/dist/receipt/index.d.ts +74 -0
  91. package/dist/receipt/index.d.ts.map +1 -0
  92. package/dist/receipt/index.js +154 -0
  93. package/dist/receipt/index.js.map +1 -0
  94. package/dist/report/index.d.ts +100 -0
  95. package/dist/report/index.d.ts.map +1 -0
  96. package/dist/report/index.js +355 -0
  97. package/dist/report/index.js.map +1 -0
  98. package/dist/schemas/adoption.d.ts +40 -0
  99. package/dist/schemas/adoption.d.ts.map +1 -0
  100. package/dist/schemas/adoption.js +87 -0
  101. package/dist/schemas/adoption.js.map +1 -0
  102. package/dist/schemas/baseline.d.ts +19 -0
  103. package/dist/schemas/baseline.d.ts.map +1 -0
  104. package/dist/schemas/baseline.js +45 -0
  105. package/dist/schemas/baseline.js.map +1 -0
  106. package/dist/schemas/claim.d.ts +32 -0
  107. package/dist/schemas/claim.d.ts.map +1 -0
  108. package/dist/schemas/claim.js +34 -0
  109. package/dist/schemas/claim.js.map +1 -0
  110. package/dist/schemas/classification-policy.d.ts +84 -0
  111. package/dist/schemas/classification-policy.d.ts.map +1 -0
  112. package/dist/schemas/classification-policy.js +130 -0
  113. package/dist/schemas/classification-policy.js.map +1 -0
  114. package/dist/schemas/classification-signal.d.ts +125 -0
  115. package/dist/schemas/classification-signal.d.ts.map +1 -0
  116. package/dist/schemas/classification-signal.js +126 -0
  117. package/dist/schemas/classification-signal.js.map +1 -0
  118. package/dist/schemas/classification.d.ts +120 -0
  119. package/dist/schemas/classification.d.ts.map +1 -0
  120. package/dist/schemas/classification.js +154 -0
  121. package/dist/schemas/classification.js.map +1 -0
  122. package/dist/schemas/common.d.ts +73 -0
  123. package/dist/schemas/common.d.ts.map +1 -0
  124. package/dist/schemas/common.js +60 -0
  125. package/dist/schemas/common.js.map +1 -0
  126. package/dist/schemas/coverage-policy.d.ts +89 -0
  127. package/dist/schemas/coverage-policy.d.ts.map +1 -0
  128. package/dist/schemas/coverage-policy.js +81 -0
  129. package/dist/schemas/coverage-policy.js.map +1 -0
  130. package/dist/schemas/evidence.d.ts +65 -0
  131. package/dist/schemas/evidence.d.ts.map +1 -0
  132. package/dist/schemas/evidence.js +64 -0
  133. package/dist/schemas/evidence.js.map +1 -0
  134. package/dist/schemas/execution-result.d.ts +214 -0
  135. package/dist/schemas/execution-result.d.ts.map +1 -0
  136. package/dist/schemas/execution-result.js +240 -0
  137. package/dist/schemas/execution-result.js.map +1 -0
  138. package/dist/schemas/gate-receipt.d.ts +86 -0
  139. package/dist/schemas/gate-receipt.d.ts.map +1 -0
  140. package/dist/schemas/gate-receipt.js +193 -0
  141. package/dist/schemas/gate-receipt.js.map +1 -0
  142. package/dist/schemas/index.d.ts +22 -0
  143. package/dist/schemas/index.d.ts.map +1 -0
  144. package/dist/schemas/index.js +22 -0
  145. package/dist/schemas/index.js.map +1 -0
  146. package/dist/schemas/obligation.d.ts +37 -0
  147. package/dist/schemas/obligation.d.ts.map +1 -0
  148. package/dist/schemas/obligation.js +46 -0
  149. package/dist/schemas/obligation.js.map +1 -0
  150. package/dist/schemas/plugin.d.ts +19 -0
  151. package/dist/schemas/plugin.d.ts.map +1 -0
  152. package/dist/schemas/plugin.js +20 -0
  153. package/dist/schemas/plugin.js.map +1 -0
  154. package/dist/schemas/policy.d.ts +73 -0
  155. package/dist/schemas/policy.d.ts.map +1 -0
  156. package/dist/schemas/policy.js +46 -0
  157. package/dist/schemas/policy.js.map +1 -0
  158. package/dist/schemas/resource.d.ts +26 -0
  159. package/dist/schemas/resource.d.ts.map +1 -0
  160. package/dist/schemas/resource.js +29 -0
  161. package/dist/schemas/resource.js.map +1 -0
  162. package/dist/schemas/run-manifest.d.ts +72 -0
  163. package/dist/schemas/run-manifest.d.ts.map +1 -0
  164. package/dist/schemas/run-manifest.js +125 -0
  165. package/dist/schemas/run-manifest.js.map +1 -0
  166. package/dist/schemas/runner-adapter.d.ts +169 -0
  167. package/dist/schemas/runner-adapter.d.ts.map +1 -0
  168. package/dist/schemas/runner-adapter.js +2 -0
  169. package/dist/schemas/runner-adapter.js.map +1 -0
  170. package/dist/schemas/test-catalog.d.ts +472 -0
  171. package/dist/schemas/test-catalog.d.ts.map +1 -0
  172. package/dist/schemas/test-catalog.js +311 -0
  173. package/dist/schemas/test-catalog.js.map +1 -0
  174. package/dist/schemas/test-map.d.ts +99 -0
  175. package/dist/schemas/test-map.d.ts.map +1 -0
  176. package/dist/schemas/test-map.js +103 -0
  177. package/dist/schemas/test-map.js.map +1 -0
  178. package/dist/schemas/verdict.d.ts +73 -0
  179. package/dist/schemas/verdict.d.ts.map +1 -0
  180. package/dist/schemas/verdict.js +92 -0
  181. package/dist/schemas/verdict.js.map +1 -0
  182. package/dist/schemas/waiver.d.ts +36 -0
  183. package/dist/schemas/waiver.d.ts.map +1 -0
  184. package/dist/schemas/waiver.js +42 -0
  185. package/dist/schemas/waiver.js.map +1 -0
  186. package/dist/supervision/index.d.ts +170 -0
  187. package/dist/supervision/index.d.ts.map +1 -0
  188. package/dist/supervision/index.js +354 -0
  189. package/dist/supervision/index.js.map +1 -0
  190. package/dist/testing/clock.d.ts +50 -0
  191. package/dist/testing/clock.d.ts.map +1 -0
  192. package/dist/testing/clock.js +75 -0
  193. package/dist/testing/clock.js.map +1 -0
  194. package/dist/testing/env.d.ts +66 -0
  195. package/dist/testing/env.d.ts.map +1 -0
  196. package/dist/testing/env.js +106 -0
  197. package/dist/testing/env.js.map +1 -0
  198. package/dist/testing/gate-runner.d.ts +141 -0
  199. package/dist/testing/gate-runner.d.ts.map +1 -0
  200. package/dist/testing/gate-runner.js +173 -0
  201. package/dist/testing/gate-runner.js.map +1 -0
  202. package/dist/testing/gf19.d.ts +106 -0
  203. package/dist/testing/gf19.d.ts.map +1 -0
  204. package/dist/testing/gf19.js +282 -0
  205. package/dist/testing/gf19.js.map +1 -0
  206. package/dist/testing/index.d.ts +20 -0
  207. package/dist/testing/index.d.ts.map +1 -0
  208. package/dist/testing/index.js +14 -0
  209. package/dist/testing/index.js.map +1 -0
  210. package/dist/testing/red-probe.d.ts +138 -0
  211. package/dist/testing/red-probe.d.ts.map +1 -0
  212. package/dist/testing/red-probe.js +251 -0
  213. package/dist/testing/red-probe.js.map +1 -0
  214. package/dist/testing/temp-repo.d.ts +126 -0
  215. package/dist/testing/temp-repo.d.ts.map +1 -0
  216. package/dist/testing/temp-repo.js +214 -0
  217. package/dist/testing/temp-repo.js.map +1 -0
  218. package/dist/verdict/cause.d.ts +99 -0
  219. package/dist/verdict/cause.d.ts.map +1 -0
  220. package/dist/verdict/cause.js +206 -0
  221. package/dist/verdict/cause.js.map +1 -0
  222. package/dist/verdict/evaluate.d.ts +135 -0
  223. package/dist/verdict/evaluate.d.ts.map +1 -0
  224. package/dist/verdict/evaluate.js +1513 -0
  225. package/dist/verdict/evaluate.js.map +1 -0
  226. package/dist/verdict/index.d.ts +21 -0
  227. package/dist/verdict/index.d.ts.map +1 -0
  228. package/dist/verdict/index.js +20 -0
  229. package/dist/verdict/index.js.map +1 -0
  230. package/dist/verdict/pack-verifiers.d.ts +97 -0
  231. package/dist/verdict/pack-verifiers.d.ts.map +1 -0
  232. package/dist/verdict/pack-verifiers.js +763 -0
  233. package/dist/verdict/pack-verifiers.js.map +1 -0
  234. package/dist/verdict/registry.d.ts +180 -0
  235. package/dist/verdict/registry.d.ts.map +1 -0
  236. package/dist/verdict/registry.js +67 -0
  237. package/dist/verdict/registry.js.map +1 -0
  238. package/dist/waivers/index.d.ts +103 -0
  239. package/dist/waivers/index.d.ts.map +1 -0
  240. package/dist/waivers/index.js +192 -0
  241. package/dist/waivers/index.js.map +1 -0
  242. package/package.json +40 -0
@@ -0,0 +1,354 @@
1
+ /**
2
+ * Trusted runner supervision — expected-set enforcement (plan
3
+ * 2026-09-13 Phase 4 work item 4, ADR 0005 D2). The supervisor fixes the
4
+ * expected test set BEFORE the run (from resolved mappings/catalog
5
+ * selection) and compares planned versus executed instances after it.
6
+ * Suite reporter data is INPUT here, never signature authority: a suite
7
+ * that writes its own report cannot shrink its expected set, hide a
8
+ * skip, or turn an incomplete shard into success.
9
+ *
10
+ * Every problem becomes a typed finding with a plan §5.4 run cause —
11
+ * `TEST_NOT_EXECUTED` / `TEST_FAILED` / `RUN_INCOMPLETE` — and any
12
+ * finding fails the whole run (§3.3 rule 5): there is no selective
13
+ * pass out of a failed required suite.
14
+ *
15
+ * Execution authority (enforcement-review fix 2): the runner-reported
16
+ * outcomes document is suite-writable and is therefore DEMOTED to detail
17
+ * input. When the supervisor supplies a witness-side `sessionTrace` (the
18
+ * signing authority's own record of which expected tests opened exactly
19
+ * one or more sealed sessions, and with which outcomes), supervision
20
+ * grades completeness from it: every expected test must have at least one
21
+ * SEALED session whose outcome is 'passed' (parameterized instances of one
22
+ * expected test produce one session per executed instance; every one of
23
+ * them must seal passed), no session may remain unsealed, and every traced
24
+ * test must belong to the expected set. A trace that could not be fetched
25
+ * (`sessionTrace: null`) blocks the run outright — a missing authority is
26
+ * never graded as success.
27
+ *
28
+ * Pure and deterministic: identical inputs produce identical findings in
29
+ * identical order.
30
+ */
31
+ import { compareStrings } from '../graph/util.js';
32
+ import { sha256Canonical } from '../canonical-json.js';
33
+ /** Identity key for planned/executed matching: project + file + titlePath. */
34
+ function instanceKey(project, file, titlePath) {
35
+ return `${project ?? '-'}\u0000${file}\u0000${titlePath.join('>')}`;
36
+ }
37
+ /**
38
+ * Enforces the expected test set (ADR 0005 D2). Findings cover, in
39
+ * deterministic order: zero selected tests, `.only`/`.skip`/`.fixme` in
40
+ * the required selection, cases the runner never enumerated, retry-
41
+ * assisted execution, planned instances that never executed, unexpected
42
+ * executed instances outside the expected set, failed instances,
43
+ * expected failures, skipped/fixme outcomes, teardown/fixture failures,
44
+ * shard incompleteness, adapter-judged incompleteness, and a nonzero
45
+ * runner exit. All are blocking; `complete` is true only when none fired.
46
+ *
47
+ * Args:
48
+ * planned: the expected set fixed BEFORE the run.
49
+ * envelope: the structured execution envelope (input only).
50
+ *
51
+ * Returns:
52
+ * SupervisionResult: complete flag plus typed findings.
53
+ */
54
+ export function superviseExecution(planned, envelope) {
55
+ const findings = [];
56
+ // Zero selected tests is never a clean run (plan Phase 4 acceptance:
57
+ // "no-tests case"). Nothing executed means nothing is proven.
58
+ if (planned.length === 0) {
59
+ findings.push({
60
+ cause: 'TEST_NOT_EXECUTED',
61
+ detail: 'the required selection contains zero tests — an empty run never proves coverage (run or repair the selected suite)',
62
+ logicalKey: null,
63
+ });
64
+ }
65
+ // Pre-run annotations: `.only`/`.skip`/`.fixme` inside the required
66
+ // selection block outright — required retries are zero and required
67
+ // skips do not exist (plan §3.3 rule 5, E08).
68
+ for (const instance of planned) {
69
+ for (const annotation of instance.blockingAnnotations) {
70
+ findings.push({
71
+ cause: 'TEST_NOT_EXECUTED',
72
+ detail: `required selection carries a '${annotation}' annotation on '${instance.logicalKey}' ` +
73
+ '(`.${annotation}` in a required run is blocking — remove it or drop the case from the required suite)',
74
+ logicalKey: instance.logicalKey,
75
+ });
76
+ }
77
+ if (instance.unenumeratedReason !== undefined) {
78
+ findings.push({
79
+ cause: 'RUN_INCOMPLETE',
80
+ detail: `the runner did not enumerate required case '${instance.logicalKey}' ` +
81
+ `(${instance.unenumeratedReason}) — the expected set cannot be sealed over an unenumerable case`,
82
+ logicalKey: instance.logicalKey,
83
+ });
84
+ }
85
+ }
86
+ // Retry-assisted execution: required retries are zero. Any attempt
87
+ // above the first invalidates the run regardless of outcome (E08).
88
+ if (envelope.retriesDetected) {
89
+ findings.push({
90
+ cause: 'RUN_INCOMPLETE',
91
+ detail: `retry-assisted execution detected${envelope.retriesDetail !== undefined ? ` (${envelope.retriesDetail})` : ''} — ` +
92
+ 'required retries are zero; only first-attempt outcomes prove behavior (plan §3.3 rule 3)',
93
+ logicalKey: null,
94
+ });
95
+ }
96
+ // Planned versus executed: multiset comparison by instance identity
97
+ // (project + file + titlePath). Reporter rows cannot shrink the set:
98
+ // every planned instance must appear, and nothing outside it may run.
99
+ const plannedByKey = new Map();
100
+ for (const instance of planned) {
101
+ const key = instanceKey(instance.project, instance.file, instance.titlePath);
102
+ plannedByKey.set(key, instance);
103
+ }
104
+ const executedByLogicalKey = new Map();
105
+ for (const outcome of envelope.outcomes) {
106
+ const list = executedByLogicalKey.get(outcome.logicalKey) ?? [];
107
+ list.push(outcome);
108
+ executedByLogicalKey.set(outcome.logicalKey, list);
109
+ }
110
+ const executedKeys = new Set();
111
+ for (const outcome of envelope.outcomes) {
112
+ executedKeys.add(instanceKey(outcome.project, outcome.file, outcome.titlePath));
113
+ }
114
+ for (const instance of planned) {
115
+ const key = instanceKey(instance.project, instance.file, instance.titlePath);
116
+ if (!executedKeys.has(key)) {
117
+ findings.push({
118
+ cause: 'RUN_INCOMPLETE',
119
+ detail: `planned instance '${instance.logicalKey}' never executed — a missing selected case blocks the run (plan §3.3 rule 5)`,
120
+ logicalKey: instance.logicalKey,
121
+ });
122
+ }
123
+ }
124
+ for (const outcome of envelope.outcomes) {
125
+ const key = instanceKey(outcome.project, outcome.file, outcome.titlePath);
126
+ if (!plannedByKey.has(key)) {
127
+ findings.push({
128
+ cause: 'RUN_INCOMPLETE',
129
+ detail: `executed instance '${outcome.logicalKey}' is outside the planned expected set — ` +
130
+ 'selective narrowing of the required suite is prohibited (no selective pass out of a failed suite)',
131
+ logicalKey: outcome.logicalKey,
132
+ });
133
+ }
134
+ }
135
+ // Per-instance outcomes: every non-pass, every expected failure, and
136
+ // every skip is a typed block (E07/E08). Attempts are supervision
137
+ // property too — any attempt above the first is retry-assisted even if
138
+ // the adapter failed to raise its own flag (fail closed, D2).
139
+ for (const outcome of envelope.outcomes) {
140
+ if (outcome.attempt > 1) {
141
+ findings.push({
142
+ cause: 'RUN_INCOMPLETE',
143
+ detail: `instance '${outcome.logicalKey}' executed on attempt ${String(outcome.attempt)} — ` +
144
+ 'retry-assisted execution is prohibited (required retries are zero; plan §3.3 rule 3)',
145
+ logicalKey: outcome.logicalKey,
146
+ });
147
+ }
148
+ if (outcome.status === 'failed' && !outcome.expectedFailure) {
149
+ findings.push({
150
+ cause: 'TEST_FAILED',
151
+ detail: `instance '${outcome.logicalKey}' failed on attempt ${String(outcome.attempt)} — the whole run fails (plan §3.3 rule 5)`,
152
+ logicalKey: outcome.logicalKey,
153
+ });
154
+ continue;
155
+ }
156
+ if (outcome.expectedFailure) {
157
+ findings.push({
158
+ cause: 'TEST_NOT_EXECUTED',
159
+ detail: `instance '${outcome.logicalKey}' is an expected failure — an expected-failure exemption never proves behavior (plan §3.3 rule 3)`,
160
+ logicalKey: outcome.logicalKey,
161
+ });
162
+ continue;
163
+ }
164
+ if (outcome.status === 'skipped' || outcome.status === 'fixme' || outcome.status === 'not-run') {
165
+ findings.push({
166
+ cause: 'TEST_NOT_EXECUTED',
167
+ detail: `instance '${outcome.logicalKey}' did not execute (status '${outcome.status}') — a skip is never coverage`,
168
+ logicalKey: outcome.logicalKey,
169
+ });
170
+ }
171
+ }
172
+ // Setup/teardown/runner-body failures (E07 teardown leg).
173
+ if (envelope.fixtureOutcome !== 'passed') {
174
+ findings.push({
175
+ cause: 'RUN_INCOMPLETE',
176
+ detail: envelope.fixtureOutcome === 'failed'
177
+ ? 'the run did not end successfully: a setup, teardown, or runner-level error was observed — fixtures and teardown must pass too (plan §3.3 rule 5)'
178
+ : 'the run did not report a final fixture/teardown outcome (crash or lost reporter contact) — fail closed',
179
+ logicalKey: null,
180
+ });
181
+ }
182
+ // Shard completeness (E12 incomplete-shard leg).
183
+ if (envelope.shards !== null && !envelope.shards.complete) {
184
+ findings.push({
185
+ cause: 'RUN_INCOMPLETE',
186
+ detail: `incomplete shards: ${envelope.shards.detail} — a partial shard run cannot certify the expected set`,
187
+ logicalKey: null,
188
+ });
189
+ }
190
+ // The adapter's own completeness judgment (timeouts, crashes, lost
191
+ // reporter data).
192
+ if (!envelope.complete) {
193
+ findings.push({
194
+ cause: 'RUN_INCOMPLETE',
195
+ detail: envelope.incompleteDetail !== undefined && envelope.incompleteDetail.length > 0
196
+ ? envelope.incompleteDetail
197
+ : 'the supervised run did not complete (no detail reported) — fail closed',
198
+ logicalKey: null,
199
+ });
200
+ }
201
+ // Runner process exit: supplementary, but nonzero is never clean.
202
+ if (envelope.processExit !== null && envelope.processExit !== 0) {
203
+ findings.push({
204
+ cause: 'RUN_INCOMPLETE',
205
+ detail: `runner process exited with status ${String(envelope.processExit)} — a failed runner never reports a green gate`,
206
+ logicalKey: null,
207
+ });
208
+ }
209
+ // Execution authority (enforcement-review fix 2b): when a witness-side
210
+ // session trace was supplied, the runner-reported outcomes are detail
211
+ // input only — completeness is graded from the trace. A fabricated or
212
+ // absent outcomes document cannot manufacture execution that the
213
+ // witness never recorded, and a trace the supervisor could not fetch
214
+ // blocks outright (fail closed).
215
+ if (envelope.sessionTrace !== undefined) {
216
+ findings.push(...gradeSessionTrace(planned, envelope.sessionTrace));
217
+ }
218
+ findings.sort((a, b) => compareStrings(a.cause, b.cause) ||
219
+ compareStrings(a.logicalKey ?? '', b.logicalKey ?? '') ||
220
+ compareStrings(a.detail, b.detail));
221
+ return { complete: findings.length === 0, findings };
222
+ }
223
+ /** Identity key shared by planned instances and traced tests. */
224
+ function traceKey(project, file, titlePath) {
225
+ return `${project ?? '-'}\u0000${file}\u0000${titlePath.join('>')}`;
226
+ }
227
+ /**
228
+ * Grades the witness-side session trace against the expected set (the
229
+ * typed core of enforcement-review fix 2). Deterministic findings, in a
230
+ * stable order:
231
+ * - a `null` trace (witness wired, trace unfetchable) blocks the run;
232
+ * - every expected test must have at least one SEALED session whose
233
+ * outcome is 'passed' — zero sessions yields the review's exact
234
+ * 'has no sealed session' cause, an unsealed session or a non-passed
235
+ * outcome blocks with its own precise detail;
236
+ * - every traced test must belong to the expected set (a session for an
237
+ * unregistered test cannot hide outside supervision).
238
+ *
239
+ * Args:
240
+ * planned: the expected set fixed BEFORE the run.
241
+ * trace: the witness-side trace (array), or null when unavailable.
242
+ *
243
+ * Returns:
244
+ * SupervisionFinding[]: the trace-derived findings (possibly empty).
245
+ */
246
+ function gradeSessionTrace(planned, trace) {
247
+ if (trace === null) {
248
+ return [
249
+ {
250
+ cause: 'RUN_INCOMPLETE',
251
+ detail: 'the witness-side execution trace is unavailable — the session record is the execution ' +
252
+ 'authority and a run without it is never graded complete (fail closed)',
253
+ logicalKey: null,
254
+ },
255
+ ];
256
+ }
257
+ const findings = [];
258
+ const tracedByKey = new Map();
259
+ for (const traced of trace) {
260
+ tracedByKey.set(traceKey(traced.project, traced.file, traced.titlePath), traced);
261
+ }
262
+ const plannedKeys = new Set();
263
+ for (const instance of planned) {
264
+ const key = traceKey(instance.project, instance.file, instance.titlePath);
265
+ plannedKeys.add(key);
266
+ const traced = tracedByKey.get(key);
267
+ if (traced === undefined) {
268
+ findings.push({
269
+ cause: 'RUN_INCOMPLETE',
270
+ detail: `expected test '${instance.logicalKey}' has no sealed session in the witness trace — ` +
271
+ 'the witness-side session record is the execution authority; a passing runner-reported ' +
272
+ 'outcome without a supervisor-sealed session never proves execution',
273
+ logicalKey: instance.logicalKey,
274
+ });
275
+ continue;
276
+ }
277
+ if (traced.sessions.length === 0) {
278
+ findings.push({
279
+ cause: 'RUN_INCOMPLETE',
280
+ detail: `expected test '${instance.logicalKey}' has no sealed session — the witness recorded ` +
281
+ 'no supervisor-opened session for it, so no execution of it is proven',
282
+ logicalKey: instance.logicalKey,
283
+ });
284
+ continue;
285
+ }
286
+ for (const session of traced.sessions) {
287
+ if (session.sealedTick === null) {
288
+ findings.push({
289
+ cause: 'RUN_INCOMPLETE',
290
+ detail: `expected test '${instance.logicalKey}' has an unsealed session (${session.sessionId}) — ` +
291
+ 'a session the supervisor never closed proves no completed execution',
292
+ logicalKey: instance.logicalKey,
293
+ });
294
+ continue;
295
+ }
296
+ if (session.outcome !== 'passed') {
297
+ findings.push({
298
+ cause: 'RUN_INCOMPLETE',
299
+ detail: `expected test '${instance.logicalKey}' sealed session (${session.sessionId}) with ` +
300
+ `outcome '${String(session.outcome ?? 'unknown')}' — only sealed passed sessions prove execution`,
301
+ logicalKey: instance.logicalKey,
302
+ });
303
+ continue;
304
+ }
305
+ // No activity requirement here (execution-authority fix): a
306
+ // genuine passing test that never touches the witness still
307
+ // executed — the supervisor observed its begin/end through the
308
+ // trusted reporter channel. Session fabrication is defeated at
309
+ // the channel (trusted-config synthesis + unknown run-state
310
+ // paths + lifecycle-conflict detection), not by counting.
311
+ }
312
+ }
313
+ for (const [key, traced] of tracedByKey) {
314
+ if (!plannedKeys.has(key)) {
315
+ findings.push({
316
+ cause: 'RUN_INCOMPLETE',
317
+ detail: `the witness recorded session(s) for a test outside the expected set ` +
318
+ `(${traced.file}#${traced.titlePath.join('>')}) — sessions are minted only for the ` +
319
+ 'registered expected tests; an unregistered trace entry blocks the run',
320
+ logicalKey: null,
321
+ });
322
+ }
323
+ }
324
+ findings.sort((a, b) => compareStrings(a.logicalKey ?? '', b.logicalKey ?? '') ||
325
+ compareStrings(a.detail, b.detail));
326
+ return findings;
327
+ }
328
+ /** Domain tag separating expected-set (enumeration) digests from every other hash. */
329
+ export const ENUMERATION_DOMAIN = 'gateforge.enumeration.v1';
330
+ /**
331
+ * Computes the domain-separated enumeration digest over the expected set
332
+ * registered with the witness BEFORE the run (enforcement-review fix 2d):
333
+ * the execution result binds this digest so a receipt names the exact
334
+ * expected set the witness enforced.
335
+ *
336
+ * Args:
337
+ * tests: the expected tests (sorted/deduplicated by this function).
338
+ *
339
+ * Returns:
340
+ * string: 64-char lowercase hex digest.
341
+ */
342
+ export function enumerationDigestOf(tests) {
343
+ const normalized = [...tests]
344
+ .map((test) => ({
345
+ project: test.project,
346
+ file: test.file,
347
+ titlePath: [...test.titlePath],
348
+ }))
349
+ .sort((a, b) => compareStrings(a.project ?? '', b.project ?? '') ||
350
+ compareStrings(a.file, b.file) ||
351
+ compareStrings(a.titlePath.join('>'), b.titlePath.join('>')));
352
+ return sha256Canonical({ domain: ENUMERATION_DOMAIN, tests: normalized });
353
+ }
354
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/supervision/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Injected clock for fixture runs (fixture harness, G7).
3
+ *
4
+ * Gateforge never reads the wall clock at test time: run manifests are
5
+ * stamped from the injected clock (pin #4) and verdict evaluation
6
+ * receives `now` (pin #9). `makeClock` produces deterministic `now`
7
+ * values from a fixed instant, an explicit sequence (e.g. waiver expiry
8
+ * crossings, GF-16), or a stepping start instant.
9
+ */
10
+ /**
11
+ * Minimal clock surface consumed by the gate runner and evaluators.
12
+ * `now()` always returns canonical ISO-8601 (`YYYY-MM-DDTHH:mm:ss.sssZ`).
13
+ */
14
+ export interface TestClock {
15
+ now(): string;
16
+ }
17
+ /** Clock specification: exactly one shape. */
18
+ export type ClockSpec = {
19
+ fixedAt: string | Date;
20
+ } | {
21
+ sequence: Array<string | Date>;
22
+ } | {
23
+ startAt: string | Date;
24
+ stepMs?: number;
25
+ };
26
+ /**
27
+ * Normalizes an instant to canonical ISO-8601.
28
+ *
29
+ * Args:
30
+ * value: ISO-8601 string or Date.
31
+ *
32
+ * Returns:
33
+ * string: canonical `toISOString()` form.
34
+ * @throws TypeError on unparseable input (fail loud, never NaN dates).
35
+ */
36
+ export declare function toIso(value: string | Date): string;
37
+ /**
38
+ * Builds a deterministic {@link TestClock} from a spec.
39
+ *
40
+ * Args:
41
+ * spec: `{fixedAt}` (constant), `{sequence}` (ordered instants,
42
+ * exhausted = thrown RangeError, never silent repetition), or
43
+ * `{startAt, stepMs}` (monotonic stepping).
44
+ *
45
+ * Returns:
46
+ * TestClock: `now()` yields canonical ISO-8601 instants.
47
+ * @throws TypeError on an empty sequence or unparseable instants.
48
+ */
49
+ export declare function makeClock(spec: ClockSpec): TestClock;
50
+ //# sourceMappingURL=clock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clock.d.ts","sourceRoot":"","sources":["../../src/testing/clock.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,GAAG,IAAI,MAAM,CAAC;CACf;AAED,8CAA8C;AAC9C,MAAM,MAAM,SAAS,GACjB;IACE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC1B;IACE,QAAQ,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;CAAE,GAClC;IACE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAExB;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAMlD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAqCpD"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Injected clock for fixture runs (fixture harness, G7).
3
+ *
4
+ * Gateforge never reads the wall clock at test time: run manifests are
5
+ * stamped from the injected clock (pin #4) and verdict evaluation
6
+ * receives `now` (pin #9). `makeClock` produces deterministic `now`
7
+ * values from a fixed instant, an explicit sequence (e.g. waiver expiry
8
+ * crossings, GF-16), or a stepping start instant.
9
+ */
10
+ /**
11
+ * Normalizes an instant to canonical ISO-8601.
12
+ *
13
+ * Args:
14
+ * value: ISO-8601 string or Date.
15
+ *
16
+ * Returns:
17
+ * string: canonical `toISOString()` form.
18
+ * @throws TypeError on unparseable input (fail loud, never NaN dates).
19
+ */
20
+ export function toIso(value) {
21
+ const date = value instanceof Date ? value : new Date(value);
22
+ if (Number.isNaN(date.getTime())) {
23
+ throw new TypeError(`not a valid instant: ${String(value)}`);
24
+ }
25
+ return date.toISOString();
26
+ }
27
+ /**
28
+ * Builds a deterministic {@link TestClock} from a spec.
29
+ *
30
+ * Args:
31
+ * spec: `{fixedAt}` (constant), `{sequence}` (ordered instants,
32
+ * exhausted = thrown RangeError, never silent repetition), or
33
+ * `{startAt, stepMs}` (monotonic stepping).
34
+ *
35
+ * Returns:
36
+ * TestClock: `now()` yields canonical ISO-8601 instants.
37
+ * @throws TypeError on an empty sequence or unparseable instants.
38
+ */
39
+ export function makeClock(spec) {
40
+ if ('fixedAt' in spec) {
41
+ const fixed = toIso(spec.fixedAt);
42
+ return { now: () => fixed };
43
+ }
44
+ if ('sequence' in spec) {
45
+ if (spec.sequence.length === 0) {
46
+ throw new TypeError('sequence clock requires at least one instant');
47
+ }
48
+ const instants = spec.sequence.map(toIso);
49
+ let index = 0;
50
+ return {
51
+ now: () => {
52
+ if (index >= instants.length) {
53
+ throw new RangeError('clock sequence exhausted (fail loud: give the fixture enough instants, never silently repeat)');
54
+ }
55
+ const next = instants[index];
56
+ if (next === undefined) {
57
+ throw new RangeError(`clock sequence index ${index} out of range (internal corruption)`);
58
+ }
59
+ index += 1;
60
+ return next;
61
+ },
62
+ };
63
+ }
64
+ const startMs = Date.parse(toIso(spec.startAt));
65
+ const stepMs = spec.stepMs ?? 1000;
66
+ let ticks = 0;
67
+ return {
68
+ now: () => {
69
+ const value = new Date(startMs + stepMs * ticks).toISOString();
70
+ ticks += 1;
71
+ return value;
72
+ },
73
+ };
74
+ }
75
+ //# sourceMappingURL=clock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clock.js","sourceRoot":"","sources":["../../src/testing/clock.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoBH;;;;;;;;;GASG;AACH,MAAM,UAAU,KAAK,CAAC,KAAoB;IACxC,MAAM,IAAI,GAAG,KAAK,YAAY,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7D,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,SAAS,CAAC,wBAAwB,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC5B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,SAAS,CAAC,IAAe;IACvC,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAClC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;IACD,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,SAAS,CAAC,8CAA8C,CAAC,CAAC;QACtE,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,OAAO;YACL,GAAG,EAAE,GAAG,EAAE;gBACR,IAAI,KAAK,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;oBAC7B,MAAM,IAAI,UAAU,CAClB,+FAA+F,CAChG,CAAC;gBACJ,CAAC;gBACD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;gBAC7B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;oBACvB,MAAM,IAAI,UAAU,CAAC,wBAAwB,KAAK,qCAAqC,CAAC,CAAC;gBAC3F,CAAC;gBACD,KAAK,IAAI,CAAC,CAAC;gBACX,OAAO,IAAI,CAAC;YACd,CAAC;SACF,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC;IACnC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO;QACL,GAAG,EAAE,GAAG,EAAE;YACR,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;YAC/D,KAAK,IAAI,CAAC,CAAC;YACX,OAAO,KAAK,CAAC;QACf,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,66 @@
1
+ import type { ChangedProvider } from '../schemas/run-manifest.js';
2
+ import type { TempRepo } from './temp-repo.js';
3
+ /**
4
+ * Runs `body` with a patched `process.env`, restoring the previous
5
+ * environment on every path (sync throw, async rejection, success).
6
+ * A `undefined` value deletes the variable for the body's duration.
7
+ *
8
+ * Args:
9
+ * vars: variable name to value (or undefined to unset).
10
+ * body: closure to run under the patched environment; may be async.
11
+ *
12
+ * Returns:
13
+ * The body's return value (awaited when async).
14
+ */
15
+ export declare function withEnv<V>(vars: Record<string, string | undefined>, body: () => V): V;
16
+ /**
17
+ * Minimal changed-file provider surface (pin #5 of architecture
18
+ * contracts). The real providers live with the CLI; fixtures consume
19
+ * this shape so parity tests run against both halves.
20
+ */
21
+ export interface ChangedFileProvider {
22
+ /** Provider identity stamped into the run manifest (pin #4). */
23
+ provider: ChangedProvider;
24
+ /**
25
+ * Repo-root-relative posix paths, deduplicated and codepoint-sorted.
26
+ */
27
+ changedFiles(): string[];
28
+ }
29
+ /**
30
+ * Normalizes a changed-file list: posix separators, deduplicated,
31
+ * codepoint-sorted. Provider outputs are compared as sets by GF-09.
32
+ *
33
+ * Args:
34
+ * files: raw provider output.
35
+ *
36
+ * Returns:
37
+ * string[]: canonical form.
38
+ */
39
+ export declare function normalizeChangedFiles(files: string[]): string[];
40
+ /**
41
+ * Builds a fake changed-file provider from an explicit list — the CI
42
+ * stand-in (`github-pr` / `gitlab-mr` merge-base diff) and the
43
+ * `all-files` full-scan provider for fixtures. The list is snapshotted
44
+ * (defensive copy + normalization) at construction.
45
+ *
46
+ * Args:
47
+ * provider: provider identity this fake impersonates.
48
+ * files: raw changed-file list.
49
+ *
50
+ * Returns:
51
+ * ChangedFileProvider: deterministic, offline, network-free.
52
+ */
53
+ export declare function fakeProvider(provider: ChangedProvider, files: string[]): ChangedFileProvider;
54
+ /**
55
+ * Real `local-staged` provider over a {@link TempRepo}: reads the
56
+ * repository's actual index (`git diff --cached --name-only`) at call
57
+ * time, so fixtures exercise the genuine local half of GF-09 parity.
58
+ *
59
+ * Args:
60
+ * repo: the fixture repository.
61
+ *
62
+ * Returns:
63
+ * ChangedFileProvider: staged files, freshly read on every call.
64
+ */
65
+ export declare function localStagedProvider(repo: TempRepo): ChangedFileProvider;
66
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/testing/env.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CA+BrF;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,gEAAgE;IAChE,QAAQ,EAAE,eAAe,CAAC;IAC1B;;OAEG;IACH,YAAY,IAAI,MAAM,EAAE,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAM/D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,mBAAmB,CAG5F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,GAAG,mBAAmB,CAEvE"}