@gate-forge/pack-playwright 0.7.1 → 0.8.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 (202) hide show
  1. package/README.md +31 -0
  2. package/dist/constants.d.ts +13 -140
  3. package/dist/constants.d.ts.map +1 -1
  4. package/dist/constants.js +13 -148
  5. package/dist/constants.js.map +1 -1
  6. package/dist/cypress/plugin.d.ts +53 -0
  7. package/dist/cypress/plugin.d.ts.map +1 -0
  8. package/dist/cypress/plugin.js +369 -0
  9. package/dist/cypress/plugin.js.map +1 -0
  10. package/dist/cypress/spec-scan.d.ts +39 -0
  11. package/dist/cypress/spec-scan.d.ts.map +1 -0
  12. package/dist/cypress/spec-scan.js +301 -0
  13. package/dist/cypress/spec-scan.js.map +1 -0
  14. package/dist/diagnosis.d.ts +39 -0
  15. package/dist/diagnosis.d.ts.map +1 -0
  16. package/dist/diagnosis.js +63 -0
  17. package/dist/diagnosis.js.map +1 -0
  18. package/dist/discovery/adapter-projection.d.ts +13 -0
  19. package/dist/discovery/adapter-projection.d.ts.map +1 -0
  20. package/dist/discovery/adapter-projection.js +84 -0
  21. package/dist/discovery/adapter-projection.js.map +1 -0
  22. package/dist/discovery/config-locations.d.ts +5 -0
  23. package/dist/discovery/config-locations.d.ts.map +1 -0
  24. package/dist/discovery/config-locations.js +19 -0
  25. package/dist/discovery/config-locations.js.map +1 -0
  26. package/dist/discovery/cypress-runner-adapter.d.ts +112 -0
  27. package/dist/discovery/cypress-runner-adapter.d.ts.map +1 -0
  28. package/dist/discovery/cypress-runner-adapter.js +501 -0
  29. package/dist/discovery/cypress-runner-adapter.js.map +1 -0
  30. package/dist/discovery/discover.d.ts +53 -1
  31. package/dist/discovery/discover.d.ts.map +1 -1
  32. package/dist/discovery/discover.js +100 -3
  33. package/dist/discovery/discover.js.map +1 -1
  34. package/dist/discovery/git-ignore.d.ts +13 -0
  35. package/dist/discovery/git-ignore.d.ts.map +1 -0
  36. package/dist/discovery/git-ignore.js +62 -0
  37. package/dist/discovery/git-ignore.js.map +1 -0
  38. package/dist/discovery/index.d.ts +9 -0
  39. package/dist/discovery/index.d.ts.map +1 -1
  40. package/dist/discovery/index.js +9 -0
  41. package/dist/discovery/index.js.map +1 -1
  42. package/dist/discovery/playwright-runner-adapter.d.ts +108 -0
  43. package/dist/discovery/playwright-runner-adapter.d.ts.map +1 -0
  44. package/dist/discovery/playwright-runner-adapter.js +185 -0
  45. package/dist/discovery/playwright-runner-adapter.js.map +1 -0
  46. package/dist/discovery/prepare-barrier.d.ts +522 -0
  47. package/dist/discovery/prepare-barrier.d.ts.map +1 -0
  48. package/dist/discovery/prepare-barrier.js +739 -0
  49. package/dist/discovery/prepare-barrier.js.map +1 -0
  50. package/dist/discovery/pytest-adapter.d.ts.map +1 -1
  51. package/dist/discovery/pytest-adapter.js +6 -2
  52. package/dist/discovery/pytest-adapter.js.map +1 -1
  53. package/dist/discovery/pytest-runner-adapter.d.ts +124 -0
  54. package/dist/discovery/pytest-runner-adapter.d.ts.map +1 -0
  55. package/dist/discovery/pytest-runner-adapter.js +454 -0
  56. package/dist/discovery/pytest-runner-adapter.js.map +1 -0
  57. package/dist/discovery/reconcile.d.ts +80 -11
  58. package/dist/discovery/reconcile.d.ts.map +1 -1
  59. package/dist/discovery/reconcile.js +395 -78
  60. package/dist/discovery/reconcile.js.map +1 -1
  61. package/dist/discovery/runner-env.d.ts +88 -2
  62. package/dist/discovery/runner-env.d.ts.map +1 -1
  63. package/dist/discovery/runner-env.js +142 -4
  64. package/dist/discovery/runner-env.js.map +1 -1
  65. package/dist/discovery/static-discovery.d.ts +38 -3
  66. package/dist/discovery/static-discovery.d.ts.map +1 -1
  67. package/dist/discovery/static-discovery.js +454 -15
  68. package/dist/discovery/static-discovery.js.map +1 -1
  69. package/dist/discovery/supervised-run.d.ts +199 -10
  70. package/dist/discovery/supervised-run.d.ts.map +1 -1
  71. package/dist/discovery/supervised-run.js +350 -39
  72. package/dist/discovery/supervised-run.js.map +1 -1
  73. package/dist/discovery/trusted-config.d.ts +180 -6
  74. package/dist/discovery/trusted-config.d.ts.map +1 -1
  75. package/dist/discovery/trusted-config.js +279 -10
  76. package/dist/discovery/trusted-config.js.map +1 -1
  77. package/dist/discovery/vitest-runner-adapter.d.ts +172 -0
  78. package/dist/discovery/vitest-runner-adapter.d.ts.map +1 -0
  79. package/dist/discovery/vitest-runner-adapter.js +607 -0
  80. package/dist/discovery/vitest-runner-adapter.js.map +1 -0
  81. package/dist/fixture/consumer-runner.d.ts +4 -0
  82. package/dist/fixture/consumer-runner.d.ts.map +1 -0
  83. package/dist/fixture/consumer-runner.js +38 -0
  84. package/dist/fixture/consumer-runner.js.map +1 -0
  85. package/dist/fixture/evidence.d.ts +27 -0
  86. package/dist/fixture/evidence.d.ts.map +1 -1
  87. package/dist/fixture/evidence.js +12 -0
  88. package/dist/fixture/evidence.js.map +1 -1
  89. package/dist/fixture/fixture.d.ts +2 -1
  90. package/dist/fixture/fixture.d.ts.map +1 -1
  91. package/dist/fixture/fixture.js +1 -17
  92. package/dist/fixture/fixture.js.map +1 -1
  93. package/dist/fixture/witness-client.d.ts +6 -174
  94. package/dist/fixture/witness-client.d.ts.map +1 -1
  95. package/dist/fixture/witness-client.js +6 -267
  96. package/dist/fixture/witness-client.js.map +1 -1
  97. package/dist/index.d.ts +9 -6
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +5 -4
  100. package/dist/index.js.map +1 -1
  101. package/dist/json.d.ts +6 -12
  102. package/dist/json.d.ts.map +1 -1
  103. package/dist/json.js +6 -17
  104. package/dist/json.js.map +1 -1
  105. package/dist/reporter/project-graph-reporter.d.ts +92 -0
  106. package/dist/reporter/project-graph-reporter.d.ts.map +1 -0
  107. package/dist/reporter/project-graph-reporter.js +92 -0
  108. package/dist/reporter/project-graph-reporter.js.map +1 -0
  109. package/dist/reporter/reporter.cjs.map +1 -1
  110. package/dist/reporter/reporter.d.ts +93 -4
  111. package/dist/reporter/reporter.d.ts.map +1 -1
  112. package/dist/reporter/reporter.js +174 -18
  113. package/dist/reporter/reporter.js.map +1 -1
  114. package/dist/runner-claims.d.ts +13 -0
  115. package/dist/runner-claims.d.ts.map +1 -0
  116. package/dist/runner-claims.js +46 -0
  117. package/dist/runner-claims.js.map +1 -0
  118. package/dist/runner-resolution.d.ts +3 -0
  119. package/dist/runner-resolution.d.ts.map +1 -0
  120. package/dist/runner-resolution.js +14 -0
  121. package/dist/runner-resolution.js.map +1 -0
  122. package/dist/supervisor/client.d.ts +23 -1
  123. package/dist/supervisor/client.d.ts.map +1 -1
  124. package/dist/supervisor/client.js +49 -0
  125. package/dist/supervisor/client.js.map +1 -1
  126. package/dist/supervisor/drain.d.ts +65 -0
  127. package/dist/supervisor/drain.d.ts.map +1 -1
  128. package/dist/supervisor/drain.js +353 -9
  129. package/dist/supervisor/drain.js.map +1 -1
  130. package/dist/supervisor/index.d.ts +7 -3
  131. package/dist/supervisor/index.d.ts.map +1 -1
  132. package/dist/supervisor/index.js +7 -3
  133. package/dist/supervisor/index.js.map +1 -1
  134. package/dist/supervisor/spool.d.ts +65 -2
  135. package/dist/supervisor/spool.d.ts.map +1 -1
  136. package/dist/supervisor/spool.js +47 -0
  137. package/dist/supervisor/spool.js.map +1 -1
  138. package/dist/surface.d.ts +6 -207
  139. package/dist/surface.d.ts.map +1 -1
  140. package/dist/surface.js +6 -264
  141. package/dist/surface.js.map +1 -1
  142. package/dist/vitest/index.d.ts +23 -0
  143. package/dist/vitest/index.d.ts.map +1 -0
  144. package/dist/vitest/index.js +356 -0
  145. package/dist/vitest/index.js.map +1 -0
  146. package/dist/vitest/reporter.d.ts +83 -0
  147. package/dist/vitest/reporter.d.ts.map +1 -0
  148. package/dist/vitest/reporter.js +211 -0
  149. package/dist/vitest/reporter.js.map +1 -0
  150. package/dist/vitest/worker-slot.d.ts +36 -0
  151. package/dist/vitest/worker-slot.d.ts.map +1 -0
  152. package/dist/vitest/worker-slot.js +51 -0
  153. package/dist/vitest/worker-slot.js.map +1 -0
  154. package/dist/witness/adapter-registry.d.ts +6 -50
  155. package/dist/witness/adapter-registry.d.ts.map +1 -1
  156. package/dist/witness/adapter-registry.js +6 -257
  157. package/dist/witness/adapter-registry.js.map +1 -1
  158. package/dist/witness/behavior-request.d.ts +6 -76
  159. package/dist/witness/behavior-request.d.ts.map +1 -1
  160. package/dist/witness/behavior-request.js +6 -273
  161. package/dist/witness/behavior-request.js.map +1 -1
  162. package/dist/witness/behavior.d.ts +6 -48
  163. package/dist/witness/behavior.d.ts.map +1 -1
  164. package/dist/witness/behavior.js +6 -114
  165. package/dist/witness/behavior.js.map +1 -1
  166. package/dist/witness/bin.d.ts +6 -17
  167. package/dist/witness/bin.d.ts.map +1 -1
  168. package/dist/witness/bin.js +6 -168
  169. package/dist/witness/bin.js.map +1 -1
  170. package/dist/witness/browser.d.ts +14 -206
  171. package/dist/witness/browser.d.ts.map +1 -1
  172. package/dist/witness/browser.js +15 -654
  173. package/dist/witness/browser.js.map +1 -1
  174. package/dist/witness/classifications.d.ts +6 -38
  175. package/dist/witness/classifications.d.ts.map +1 -1
  176. package/dist/witness/classifications.js +6 -83
  177. package/dist/witness/classifications.js.map +1 -1
  178. package/dist/witness/env-attestation.d.ts +6 -94
  179. package/dist/witness/env-attestation.d.ts.map +1 -1
  180. package/dist/witness/env-attestation.js +6 -198
  181. package/dist/witness/env-attestation.js.map +1 -1
  182. package/dist/witness/fixture-provider.d.ts +6 -82
  183. package/dist/witness/fixture-provider.d.ts.map +1 -1
  184. package/dist/witness/fixture-provider.js +6 -105
  185. package/dist/witness/fixture-provider.js.map +1 -1
  186. package/dist/witness/loopback-pins.d.ts +6 -75
  187. package/dist/witness/loopback-pins.d.ts.map +1 -1
  188. package/dist/witness/loopback-pins.js +6 -240
  189. package/dist/witness/loopback-pins.js.map +1 -1
  190. package/dist/witness/server.d.ts +8 -26
  191. package/dist/witness/server.d.ts.map +1 -1
  192. package/dist/witness/server.js +24 -3761
  193. package/dist/witness/server.js.map +1 -1
  194. package/dist/witness/types.d.ts +6 -882
  195. package/dist/witness/types.d.ts.map +1 -1
  196. package/dist/witness/types.js +9 -1
  197. package/dist/witness/types.js.map +1 -1
  198. package/examples/overlay-proof.spec.js +75 -0
  199. package/package.json +8 -2
  200. package/python/__pycache__/gateforge_persistence_intents.cpython-312.pyc +0 -0
  201. package/python/__pycache__/gateforge_persistence_intents.cpython-314.pyc +0 -0
  202. package/python/gateforge_pytest_plugin.py +462 -0
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Project-graph reporter: the ONLY trustworthy source of a Playwright
3
+ * config's per-project `dependencies`.
4
+ *
5
+ * The json reporter Gateforge enumerates with does NOT carry the edge:
6
+ * `config.projects[]` there is rebuilt field by field (outputDir,
7
+ * repeatEach, retries, metadata, id, name, testDir, testIgnore,
8
+ * testMatch, timeout) and `dependencies` is simply absent — verified
9
+ * against the pack's pinned playwright 1.58.2 and against a consumer's
10
+ * own 1.62.1. Reading the edge from the consumer config file instead
11
+ * would mean parsing arbitrary candidate code, which the trusted
12
+ * supervision boundary exists to avoid.
13
+ *
14
+ * So the enumeration adds this ENGINE-OWNED reporter next to the json
15
+ * one (`--reporter=json,<this file>`). The runner hands every reporter
16
+ * the resolved FullConfig in `onConfigure` — the same object the runner
17
+ * itself schedules from — so the graph is the runner's own truth, not
18
+ * the candidate's claim about it.
19
+ *
20
+ * It records the project graph and, alongside it, the RESOLVED
21
+ * `use.storageState` of each project — but only when that value is a
22
+ * plain string path. That is DATA the runner already resolved from the
23
+ * consumer config (a file location), not consumer code, and it is what
24
+ * the standard auth pattern needs: a `setup` project signs in and saves
25
+ * `playwright/.auth/user.json`, and the dependent project is the one
26
+ * that must be handed that file. Every other project option is
27
+ * consumer configuration a supervised run deliberately does not honor,
28
+ * so none of it crosses this boundary — including a `storageState`
29
+ * given as an inline `{cookies, origins}` document, which is a value
30
+ * this boundary does not carry.
31
+ *
32
+ * The output path arrives in the environment, exactly like the json
33
+ * reporter's own `PLAYWRIGHT_JSON_OUTPUT_FILE`: the reporter list a CLI
34
+ * accepts cannot carry per-reporter options, and this reporter runs in
35
+ * the ENUMERATION child (`--list`, no test ever executes), never in the
36
+ * supervised run whose run-state paths must stay out of worker hands.
37
+ */
38
+ import { writeFileSync } from 'node:fs';
39
+ /** Environment variable naming the graph document to write. */
40
+ export const PROJECT_GRAPH_PATH_ENV = 'PLAYWRIGHT_GATEFORGE_PROJECT_GRAPH_FILE';
41
+ /**
42
+ * The Playwright reporter that records the resolved project dependency
43
+ * graph and the storage states its projects declare. Written for the
44
+ * v2 reporter protocol (`version()` returning `'v2'`), whose
45
+ * `onConfigure` receives the resolved full config.
46
+ */
47
+ export class ProjectGraphReporter {
48
+ /** Opts into the v2 reporter protocol (config arrives in onConfigure). */
49
+ version() {
50
+ return 'v2';
51
+ }
52
+ /**
53
+ * Records the graph, and the declared storage states, the RUNNER
54
+ * resolved.
55
+ *
56
+ * @param config: the resolved full config, exactly as the runner has it.
57
+ */
58
+ onConfigure(config) {
59
+ const graphPath = process.env[PROJECT_GRAPH_PATH_ENV];
60
+ if (graphPath === undefined || graphPath.length === 0)
61
+ return;
62
+ // Null-prototype: a project NAME is candidate data and `__proto__` is
63
+ // a legal one, so a map keyed by it must not inherit anything.
64
+ const projectDependencies = Object.create(null);
65
+ const projectStorageStates = Object.create(null);
66
+ for (const project of config.projects ?? []) {
67
+ if (typeof project.name !== 'string' || project.name.length === 0)
68
+ continue;
69
+ projectDependencies[project.name] = [
70
+ ...new Set((project.dependencies ?? []).filter((name) => typeof name === 'string' && name.length > 0)),
71
+ ].sort();
72
+ // Only a plain path string crosses. An inline `{cookies, origins}`
73
+ // document is a value this boundary does not carry, and a
74
+ // function-valued state is consumer code, not data. Every STRING
75
+ // crosses verbatim — an empty one included: a project that asked
76
+ // for no state at all is a refusal the planner owes, not a
77
+ // silently-dropped declaration that would run logged out.
78
+ const storageState = project.use?.storageState;
79
+ if (typeof storageState === 'string') {
80
+ projectStorageStates[project.name] = storageState;
81
+ }
82
+ }
83
+ const document = {
84
+ schemaVersion: 2,
85
+ projectDependencies,
86
+ ...(Object.keys(projectStorageStates).length > 0 ? { projectStorageStates } : {}),
87
+ };
88
+ writeFileSync(graphPath, `${JSON.stringify(document)}\n`, 'utf8');
89
+ }
90
+ }
91
+ export default ProjectGraphReporter;
92
+ //# sourceMappingURL=project-graph-reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-graph-reporter.js","sourceRoot":"","sources":["../../src/reporter/project-graph-reporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC,+DAA+D;AAC/D,MAAM,CAAC,MAAM,sBAAsB,GAAG,yCAAyC,CAAC;AAqChF;;;;;GAKG;AACH,MAAM,OAAO,oBAAoB;IAC/B,0EAA0E;IACnE,OAAO;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;OAKG;IACI,WAAW,CAAC,MAAwB;QACzC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;QACtD,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC9D,sEAAsE;QACtE,+DAA+D;QAC/D,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAA6B,CAAC;QAC5E,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAA2B,CAAC;QAC3E,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;YAC5C,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAC5E,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG;gBAClC,GAAG,IAAI,GAAG,CACR,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,MAAM,CACjC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CACtE,CACF;aACF,CAAC,IAAI,EAAE,CAAC;YACT,mEAAmE;YACnE,0DAA0D;YAC1D,iEAAiE;YACjE,iEAAiE;YACjE,2DAA2D;YAC3D,0DAA0D;YAC1D,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,EAAE,YAAY,CAAC;YAC/C,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;gBACrC,oBAAoB,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC;YACpD,CAAC;QACH,CAAC;QACD,MAAM,QAAQ,GAAyB;YACrC,aAAa,EAAE,CAAC;YAChB,mBAAmB;YACnB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,oBAAoB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClF,CAAC;QACF,aAAa,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpE,CAAC;CACF;AAED,eAAe,oBAAoB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"reporter.cjs","sourceRoot":"","sources":["../../src/reporter/reporter.cts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reporter.cjs","sourceRoot":"","sources":["../../src/reporter/reporter.cts"],"names":[],"mappings":"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"}
@@ -1,4 +1,12 @@
1
+ import { type SerializedFailure } from '../diagnosis.js';
1
2
  import { type LedgerRow } from './ledger.js';
3
+ /**
4
+ * The scope the CLI graded, from the run-scope view it writes before the
5
+ * suite starts. A run that graded a SELECTION (a named test list, a
6
+ * changed slice) observed no repository-wide debt, so the reporter must
7
+ * not verdict on it.
8
+ */
9
+ export type ReporterRunScope = 'full' | 'changed' | 'named';
2
10
  /** Minimal reporter-side test shape (Playwright's TestCase in practice). */
3
11
  export interface ReporterTest {
4
12
  id: string;
@@ -47,11 +55,13 @@ export interface ReporterSuiteLike {
47
55
  * Args:
48
56
  * rows: the per-claim ledger rows.
49
57
  * unclaimedObligations: count of run obligations with no claim row.
58
+ * scope: the scope the CLI graded (a selection run never verdicts
59
+ * repository debt it did not observe).
50
60
  *
51
61
  * Returns:
52
62
  * string: the aggregate summary line.
53
63
  */
54
- export declare function gateSummaryLine(rows: readonly LedgerRow[], unclaimedObligations: number): string;
64
+ export declare function gateSummaryLine(rows: readonly LedgerRow[], unclaimedObligations: number, scope?: ReporterRunScope): string;
55
65
  /** Constructor options for the gateforge reporter (trusted-config path). */
56
66
  export interface GateforgeReporterOptions {
57
67
  /** Run-state dir override (precedence over GATEFORGE_STATE_DIR env). */
@@ -62,10 +72,45 @@ export interface GateforgeReporterOptions {
62
72
  outcomesPath?: string;
63
73
  /** Obligations document path override (precedence over GATEFORGE_OBLIGATIONS env). */
64
74
  obligationsPath?: string;
75
+ /**
76
+ * Absolute CANDIDATE ROOT every reported file identity is relative
77
+ * to. A supervised run hands it as trusted CONSTRUCTOR DATA because
78
+ * the runner child does not run from there: it runs from the
79
+ * selected native config directory, exactly as enumeration did and as
80
+ * the project's own relative paths require. The identities the
81
+ * supervisor registered — and every claim injection keyed by them —
82
+ * are repo-relative, so anchoring them at the child's cwd would
83
+ * rename `frontend/tests/x.spec.ts` to `tests/x.spec.ts`, miss every
84
+ * claim injection and fail every session binding closed.
85
+ *
86
+ * Absent (the standalone reporter a project runs itself, wired only
87
+ * through the environment) means the process cwd, which IS the honest
88
+ * root for that case.
89
+ */
90
+ candidateRoot?: string;
91
+ /**
92
+ * Absolute path of the engine's own generated preparation-freeze
93
+ * controller spec, when the trusted CLI armed the global native
94
+ * freeze. A test whose location file IS this file is the engine's
95
+ * control, not a candidate case: it produces no lifecycle event, no
96
+ * outcome row and no claim, so the native count and the registered
97
+ * expected set speak about the candidate's own tests only.
98
+ *
99
+ * The comparison is on the resolved absolute path (never a title, a
100
+ * basename or a project-name prefix), so a candidate test that merely
101
+ * borrows the controller's name — or lives beside it — is never
102
+ * excluded. The path arrives already symlink-resolved: the spec lives
103
+ * in a private per-run temporary directory (`/tmp` is commonly a
104
+ * symlink), and the runner reports it under the `testDir` the config
105
+ * was handed, so both sides must name the same physical file. Absent
106
+ * means every reported test is a candidate case, exactly as before.
107
+ */
108
+ controlSpecPath?: string;
65
109
  }
66
110
  /**
67
- * The gateforge reporter. No options today; the constructor signature is
68
- * the Playwright reporter contract (`(options: object)`).
111
+ * The gateforge reporter. Its constructor signature is the Playwright
112
+ * reporter contract (`(options: object)`); every path and the identity
113
+ * root arrive as options.
69
114
  */
70
115
  export declare class GateforgeReporter {
71
116
  private readonly rows;
@@ -81,8 +126,23 @@ export declare class GateforgeReporter {
81
126
  private readonly runnerErrors;
82
127
  /** The lifecycle spool file (null when the run has no state dir). */
83
128
  private readonly spoolFile;
129
+ /**
130
+ * The candidate root file identities are relative to, when the
131
+ * trusted supervisor supplied one; null means the process cwd (the
132
+ * standalone reporter's own honest root).
133
+ */
134
+ private readonly candidateRoot;
84
135
  /** Resolved run-state paths (options win, env is the legacy fallback). */
85
136
  private readonly resolved;
137
+ /**
138
+ * Absolute path of the engine's own generated preparation-freeze
139
+ * controller spec, or null when this run armed no freeze. A test
140
+ * located exactly at this path is the engine's control: it reaches
141
+ * neither the lifecycle spool, nor the outcomes document, nor the
142
+ * claim registry, so the native count and the registered expected set
143
+ * speak about the candidate's own tests only.
144
+ */
145
+ private readonly controlSpecPath;
86
146
  constructor(options?: GateforgeReporterOptions);
87
147
  /**
88
148
  * Lifecycle spool write (enforcement-review fix 3): announces the
@@ -105,12 +165,20 @@ export declare class GateforgeReporter {
105
165
  status: string;
106
166
  workerIndex?: number;
107
167
  retry?: number;
168
+ duration?: number;
169
+ errors?: SerializedFailure[];
108
170
  }): void;
109
171
  /** Playswright `onEnd`: seal the run-level outcome + write artifacts. */
110
172
  onEnd(result?: {
111
173
  status?: string;
112
174
  }): Promise<void>;
113
175
  /** Builds non-authoritative counts from the runner events and ledger.
176
+ *
177
+ * The debt here is the LEGACY, reporter-observable total only
178
+ * (blocking claims + every unclaimed obligation, baselined or not).
179
+ * How much of it the adopted baseline forgave is NOT derivable here —
180
+ * the reporter never loads the baseline, the waivers or the graded
181
+ * scope — so the gate owns that number alone.
114
182
  *
115
183
  * Args:
116
184
  * ledger: selected claimed-obligation results from the real engine.
@@ -150,8 +218,29 @@ export declare class GateforgeReporter {
150
218
  * source location (its parent is root/project or absent).
151
219
  */
152
220
  private isDescribeBoundary;
153
- /** Repo-relative posix file of a test (null when unknown). */
221
+ /**
222
+ * Repo-relative posix file of a test (null when unknown), relative to
223
+ * the CANDIDATE ROOT the supervisor handed the reporter — never to
224
+ * the runner child's cwd, which for a nested project is its config
225
+ * directory and would report every identity one directory short.
226
+ */
154
227
  private repoRelativeOf;
228
+ /**
229
+ * Whether this test IS the engine's own freeze controller.
230
+ *
231
+ * The answer is the resolved ABSOLUTE location file against the
232
+ * resolved absolute control path the trusted CLI pinned — nothing
233
+ * else. A test title, a project name, a directory or a basename is
234
+ * never a sufficient identity, so a candidate test that happens to be
235
+ * called `gateforge global preparation freeze`, or that sits beside
236
+ * the control file, is still a candidate case.
237
+ *
238
+ * @param test: the reported test.
239
+ *
240
+ * @returns
241
+ * boolean: true only for the pinned control file itself.
242
+ */
243
+ private isFreezeControl;
155
244
  /** Native annotation claims of one test (`{type: 'gateforge'}`). */
156
245
  private annotationClaimsOf;
157
246
  /** Injection-key lookup for one test: `<file>#<titlePath.join('>')>`. */
@@ -1 +1 @@
1
- {"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/reporter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/reporter.ts"],"names":[],"mappings":"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"}
@@ -52,14 +52,33 @@
52
52
  * construction; a missing drain leaves no sessions open, and the
53
53
  * witness rejects every submission fail-closed.
54
54
  */
55
- import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
56
- import { join, relative, sep } from 'node:path';
55
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
56
+ import { join, relative, resolve, sep } from 'node:path';
57
57
  import { canonicalOf } from '../json.js';
58
58
  import { CLAIM_ANNOTATION_TYPE, CLAIM_INJECTIONS_FILE, ENV_OBLIGATIONS, ENV_OUTCOMES_FILE, ENV_REPORTER_FAIL_RUN, ENV_RUN_ID, ENV_STATE_DIR, ENV_WITNESS_URL, } from '../constants.js';
59
59
  import { appendSpoolEvent, spoolPathFor } from '../supervisor/spool.js';
60
+ import { failureDiagnosisOf } from '../diagnosis.js';
60
61
  import { WitnessClient } from '../fixture/witness-client.js';
61
62
  import { resolveWitnessUrl } from '../fixture/witness-client.js';
62
63
  import { claimOf, isBlocking, ledgerRowFor, parseObligationsDocument, } from './ledger.js';
64
+ /** Reads a state-dir JSON document, or null when it is absent or unusable. */
65
+ function readStateView(stateDir, name) {
66
+ try {
67
+ return JSON.parse(readFileSync(join(stateDir, name), 'utf8'));
68
+ }
69
+ catch {
70
+ return null;
71
+ }
72
+ }
73
+ /**
74
+ * The scope the CLI graded for this run. Absent or unreadable means a
75
+ * whole-repository run — the conservative reading, since anything
76
+ * narrower means the reporter observed no repository-wide debt.
77
+ */
78
+ function runScopeOf(stateDir) {
79
+ const scope = readStateView(stateDir, 'run-scope.json')?.scope;
80
+ return scope === 'named' || scope === 'changed' ? scope : 'full';
81
+ }
63
82
  /**
64
83
  * Computes the honest aggregate line (plan Phase 4 item 7, §2 row): the
65
84
  * reporter NEVER prints an overall gate pass based only on claimed rows
@@ -69,16 +88,26 @@ import { claimOf, isBlocking, ledgerRowFor, parseObligationsDocument, } from './
69
88
  * Args:
70
89
  * rows: the per-claim ledger rows.
71
90
  * unclaimedObligations: count of run obligations with no claim row.
91
+ * scope: the scope the CLI graded (a selection run never verdicts
92
+ * repository debt it did not observe).
72
93
  *
73
94
  * Returns:
74
95
  * string: the aggregate summary line.
75
96
  */
76
- export function gateSummaryLine(rows, unclaimedObligations) {
97
+ export function gateSummaryLine(rows, unclaimedObligations, scope = 'full') {
77
98
  const blocking = rows.filter((row) => isBlocking(row.verdict));
78
99
  const authority = 'final gate result: the gateforge CLI (test-gates/check), never this reporter';
79
100
  if (blocking.length > 0) {
80
101
  return `GATEFORGE GATE: FAIL (${String(blocking.length)}/${String(rows.length)} claimed obligations not satisfied; ${authority})`;
81
102
  }
103
+ if (unclaimedObligations > 0 && scope !== 'full') {
104
+ // A selection run graded its own claims and nothing else. Printing
105
+ // "NOT PASSED" here would state a verdict about debt the run never
106
+ // observed, and it would contradict the CLI exit code printed
107
+ // seconds later — two verdicts, one run, one of them wrong.
108
+ return (`GATEFORGE GATE: SELECTION (${String(rows.length)} satisfied, 0 blocking; ` +
109
+ `repository verdict not graded here — ${authority})`);
110
+ }
82
111
  if (unclaimedObligations > 0) {
83
112
  return (`GATEFORGE GATE: NOT PASSED (${String(rows.length)} claimed obligation(s) satisfied, ` +
84
113
  `${String(unclaimedObligations)} unclaimed obligation(s) still block; ${authority})`);
@@ -89,8 +118,9 @@ export function gateSummaryLine(rows, unclaimedObligations) {
89
118
  return `GATEFORGE GATE: PASS (${String(rows.length)}/${String(rows.length)} claimed obligations satisfied; ${authority})`;
90
119
  }
91
120
  /**
92
- * The gateforge reporter. No options today; the constructor signature is
93
- * the Playwright reporter contract (`(options: object)`).
121
+ * The gateforge reporter. Its constructor signature is the Playwright
122
+ * reporter contract (`(options: object)`); every path and the identity
123
+ * root arrive as options.
94
124
  */
95
125
  export class GateforgeReporter {
96
126
  rows = [];
@@ -106,8 +136,23 @@ export class GateforgeReporter {
106
136
  runnerErrors = [];
107
137
  /** The lifecycle spool file (null when the run has no state dir). */
108
138
  spoolFile;
139
+ /**
140
+ * The candidate root file identities are relative to, when the
141
+ * trusted supervisor supplied one; null means the process cwd (the
142
+ * standalone reporter's own honest root).
143
+ */
144
+ candidateRoot;
109
145
  /** Resolved run-state paths (options win, env is the legacy fallback). */
110
146
  resolved;
147
+ /**
148
+ * Absolute path of the engine's own generated preparation-freeze
149
+ * controller spec, or null when this run armed no freeze. A test
150
+ * located exactly at this path is the engine's control: it reaches
151
+ * neither the lifecycle spool, nor the outcomes document, nor the
152
+ * claim registry, so the native count and the registered expected set
153
+ * speak about the candidate's own tests only.
154
+ */
155
+ controlSpecPath;
111
156
  constructor(options = {}) {
112
157
  const stateDir = options.stateDir ?? process.env[ENV_STATE_DIR];
113
158
  const runId = options.runId ?? process.env[ENV_RUN_ID];
@@ -119,6 +164,18 @@ export class GateforgeReporter {
119
164
  outcomesPath: outcomesPath !== undefined && outcomesPath !== '' ? outcomesPath : null,
120
165
  obligationsPath: obligationsPath !== undefined && obligationsPath !== '' ? obligationsPath : null,
121
166
  };
167
+ this.candidateRoot =
168
+ options.candidateRoot !== undefined && options.candidateRoot.length > 0
169
+ ? resolve(options.candidateRoot)
170
+ : null;
171
+ // Resolved once, in this process, from TRUSTED CONSTRUCTOR DATA: the
172
+ // exact control file the CLI pinned before the run. A candidate test
173
+ // can never name it, and one that merely borrows the controller's
174
+ // title or lives in the same directory is untouched.
175
+ this.controlSpecPath =
176
+ options.controlSpecPath !== undefined && options.controlSpecPath.length > 0
177
+ ? resolve(options.controlSpecPath)
178
+ : null;
122
179
  const wired = (process.env[ENV_WITNESS_URL] ?? '') !== '' ||
123
180
  (this.resolved.stateDir ?? '') !== '';
124
181
  if (!wired) {
@@ -145,6 +202,12 @@ export class GateforgeReporter {
145
202
  * the dispatch latency (the fixture's session resolve waits up to 5s).
146
203
  */
147
204
  onTestBegin(test, result) {
205
+ // The engine's own freeze controller is NOT a candidate case: it
206
+ // opens no session, so the registered expected set never has to
207
+ // contain it and the drain never tries. The exclusion is by the
208
+ // pinned absolute control file, never by title or project name.
209
+ if (this.isFreezeControl(test))
210
+ return;
148
211
  if (this.spoolFile === null)
149
212
  return;
150
213
  const workerIndex = typeof result.workerIndex === 'number' ? result.workerIndex : 0;
@@ -165,10 +228,56 @@ export class GateforgeReporter {
165
228
  }
166
229
  /** Collects claims + test identity + the outcome row at test end (synchronous). */
167
230
  onTestEnd(test, result) {
168
- // Phase 4: capture the outcome row for trusted runner supervision
169
- // (every test, claimed or not — the expected set includes them all).
231
+ // Same absolute-file exclusion as onTestBegin: the engine's own
232
+ // controller contributes NO outcome row, so it never enters the
233
+ // runner-outcomes document supervision compares against the planned
234
+ // expected set, nor the selected-test counts it prints. A refused
235
+ // release still fails the run honestly: the runner's own final status
236
+ // becomes 'failed', the drain records the refusal as a conflict, and
237
+ // the sealed receipt is never written.
238
+ if (this.isFreezeControl(test))
239
+ return;
170
240
  const titlePath = this.titlePathOf(test);
171
241
  const file = this.repoRelativeOf(test);
242
+ const finishedAt = new Date().toISOString();
243
+ if (this.resolved.stateDir !== null && typeof result.duration === 'number') {
244
+ const diagnosticsDir = join(this.resolved.stateDir, 'diagnostics');
245
+ mkdirSync(diagnosticsDir, { recursive: true });
246
+ appendFileSync(join(diagnosticsDir, 'test-timing.jsonl'), `${JSON.stringify({
247
+ testId: test.id,
248
+ file: file ?? '',
249
+ titlePath,
250
+ status: result.status,
251
+ durationMs: result.duration,
252
+ finishedAt,
253
+ })}\n`, 'utf8');
254
+ }
255
+ if (result.status === 'timedOut' &&
256
+ typeof result.duration === 'number' &&
257
+ this.resolved.stateDir !== null) {
258
+ const timingPath = join(this.resolved.stateDir, 'diagnostics', 'adapter-timing.jsonl');
259
+ let witnessDurationMs = 0;
260
+ if (existsSync(timingPath)) {
261
+ for (const line of readFileSync(timingPath, 'utf8').split(/\r?\n/).filter(Boolean)) {
262
+ try {
263
+ const timing = JSON.parse(line);
264
+ if (timing.testId === test.id &&
265
+ timing.runId === this.resolved.runId &&
266
+ typeof timing.durationMs === 'number') {
267
+ witnessDurationMs += timing.durationMs;
268
+ }
269
+ }
270
+ catch {
271
+ continue;
272
+ }
273
+ }
274
+ }
275
+ const appDurationMs = Math.max(0, result.duration - witnessDurationMs);
276
+ console.warn(`[gateforge] timeout split for ${test.id}: app/runner ${appDurationMs}ms, ` +
277
+ `witness adapter ${witnessDurationMs}ms`);
278
+ }
279
+ // Phase 4: capture the outcome row for trusted runner supervision
280
+ // (every test, claimed or not — the expected set includes them all).
172
281
  this.runnerOutcomes.push({
173
282
  testId: test.id,
174
283
  ...(file !== null ? { file } : { file: '' }),
@@ -191,6 +300,10 @@ export class GateforgeReporter {
191
300
  project: this.projectOf(test),
192
301
  outcome: result.status,
193
302
  attempt: (typeof result.retry === 'number' ? result.retry : 0) + 1,
303
+ // Additive diagnosis for a non-passing test: the CI progress
304
+ // stream and the failures artifact are the only readers, and the
305
+ // CLI screens the message before printing or writing it.
306
+ ...(result.status === 'passed' ? {} : (failureDiagnosisOf(result.errors) ?? {})),
194
307
  });
195
308
  }
196
309
  // Claims: native annotations keep working unchanged; Phase 4 adds
@@ -256,11 +369,12 @@ export class GateforgeReporter {
256
369
  const httpRoutes = readHttpRoutes(stateDir);
257
370
  const ledger = this.ledgerRows(obligations, classifications, records, now, httpRoutes);
258
371
  writeJson(stateDir, 'ledger.json', ledger);
259
- const unclaimed = obligations === null
260
- ? 0
372
+ const unclaimedIds = obligations === null
373
+ ? new Set()
261
374
  : new Set(obligations.obligations
262
375
  .filter((entry) => !this.rows.some((row) => row.claims.includes(entry.id)))
263
- .map((entry) => entry.id)).size;
376
+ .map((entry) => entry.id));
377
+ const unclaimed = unclaimedIds.size;
264
378
  const summary = this.runSummary(ledger, obligations, unclaimed);
265
379
  writeJson(stateDir, 'run-summary.json', summary);
266
380
  console.log(`selected tests: ${String(summary.selectedTests.passed)} passed, ${String(summary.selectedTests.failed)} failed ` +
@@ -268,9 +382,11 @@ export class GateforgeReporter {
268
382
  `expected failures: ${String(summary.selectedTests.expectedFailures)})`);
269
383
  console.log(`selected claims: ${String(summary.selectedClaims.satisfied)} satisfied, ${String(summary.selectedClaims.blocking)} blocking ` +
270
384
  `(selected: ${String(summary.selectedClaims.selected)}, waived: ${String(summary.selectedClaims.waived)})`);
271
- console.log(`repository debt: ${String(summary.repositoryDebt.unclaimed)} unclaimed / ` +
272
- `${String(summary.repositoryDebt.obligations)} obligations (${String(summary.repositoryDebt.blocking)} blocking)`);
273
- this.printLedger(ledger, unclaimed);
385
+ // Debt is the gate's to grade: the reporter cannot see the waivers,
386
+ // the scope or the adopted baseline it graded against, so it names
387
+ // no debt count (one run, one number).
388
+ console.log('repository debt: graded by gateforge after the run');
389
+ this.printLedger(ledger, unclaimed, runScopeOf(stateDir));
274
390
  this.printRegistryMismatches(obligations, records);
275
391
  const blocking = ledger.some((row) => isBlocking(row.verdict));
276
392
  if (blocking && process.env[ENV_REPORTER_FAIL_RUN] === '1') {
@@ -283,6 +399,12 @@ export class GateforgeReporter {
283
399
  }
284
400
  }
285
401
  /** Builds non-authoritative counts from the runner events and ledger.
402
+ *
403
+ * The debt here is the LEGACY, reporter-observable total only
404
+ * (blocking claims + every unclaimed obligation, baselined or not).
405
+ * How much of it the adopted baseline forgave is NOT derivable here —
406
+ * the reporter never loads the baseline, the waivers or the graded
407
+ * scope — so the gate owns that number alone.
286
408
  *
287
409
  * Args:
288
410
  * ledger: selected claimed-obligation results from the real engine.
@@ -307,6 +429,10 @@ export class GateforgeReporter {
307
429
  const satisfied = ledger.filter((row) => row.verdict === 'satisfied').length;
308
430
  const blockingClaims = ledger.filter((row) => isBlocking(row.verdict));
309
431
  const waived = ledger.filter((row) => row.verdict === 'waived').length;
432
+ const distinctBlockingClaims = new Set(blockingClaims.map((row) => row.claim)).size;
433
+ // No debt split is computed here: the reporter cannot know which
434
+ // obligations the adopted baseline forgave, and a guessed split
435
+ // contradicts the gate line printed seconds later.
310
436
  return {
311
437
  schemaVersion: 1,
312
438
  selectedTests: { selected: outcomes.length, passed, failed, skipped, expectedFailures },
@@ -314,7 +440,9 @@ export class GateforgeReporter {
314
440
  repositoryDebt: {
315
441
  obligations: obligations?.obligations.length ?? 0,
316
442
  unclaimed,
317
- blocking: new Set(blockingClaims.map((row) => row.claim)).size + unclaimed,
443
+ // Legacy total, unchanged: blocking claims + every unclaimed
444
+ // obligation, baselined or not.
445
+ blocking: distinctBlockingClaims + unclaimed,
318
446
  },
319
447
  };
320
448
  }
@@ -388,14 +516,42 @@ export class GateforgeReporter {
388
516
  return parent.type !== 'describe';
389
517
  return parent.location === undefined || parent.location === null;
390
518
  }
391
- /** Repo-relative posix file of a test (null when unknown). */
519
+ /**
520
+ * Repo-relative posix file of a test (null when unknown), relative to
521
+ * the CANDIDATE ROOT the supervisor handed the reporter — never to
522
+ * the runner child's cwd, which for a nested project is its config
523
+ * directory and would report every identity one directory short.
524
+ */
392
525
  repoRelativeOf(test) {
393
526
  if (test.location?.file === undefined || test.location.file === null)
394
527
  return null;
395
528
  const raw = test.location.file;
396
529
  if (raw.length === 0)
397
530
  return null;
398
- return relative(process.cwd(), raw).split(sep).join('/');
531
+ return relative(this.candidateRoot ?? process.cwd(), raw).split(sep).join('/');
532
+ }
533
+ /**
534
+ * Whether this test IS the engine's own freeze controller.
535
+ *
536
+ * The answer is the resolved ABSOLUTE location file against the
537
+ * resolved absolute control path the trusted CLI pinned — nothing
538
+ * else. A test title, a project name, a directory or a basename is
539
+ * never a sufficient identity, so a candidate test that happens to be
540
+ * called `gateforge global preparation freeze`, or that sits beside
541
+ * the control file, is still a candidate case.
542
+ *
543
+ * @param test: the reported test.
544
+ *
545
+ * @returns
546
+ * boolean: true only for the pinned control file itself.
547
+ */
548
+ isFreezeControl(test) {
549
+ if (this.controlSpecPath === null)
550
+ return false;
551
+ const file = test.location?.file;
552
+ if (typeof file !== 'string' || file.length === 0)
553
+ return false;
554
+ return resolve(file) === this.controlSpecPath;
399
555
  }
400
556
  /** Native annotation claims of one test (`{type: 'gateforge'}`). */
401
557
  annotationClaimsOf(test) {
@@ -549,7 +705,7 @@ export class GateforgeReporter {
549
705
  * obligations block; the authoritative CLI remains the only final
550
706
  * gate result.
551
707
  */
552
- printLedger(rows, unclaimedObligations) {
708
+ printLedger(rows, unclaimedObligations, scope) {
553
709
  const width = Math.max('CLAIMED OBLIGATION'.length, ...rows.map((row) => row.claim.length));
554
710
  console.log('\n=== GATEFORGE VERDICTS ===');
555
711
  if (rows.length === 0) {
@@ -564,7 +720,7 @@ export class GateforgeReporter {
564
720
  console.log(` - records: ${row.recordIds.join(', ')}`);
565
721
  }
566
722
  }
567
- console.log(gateSummaryLine(rows, unclaimedObligations));
723
+ console.log(gateSummaryLine(rows, unclaimedObligations, scope));
568
724
  console.log('==========================\n');
569
725
  }
570
726
  /** GF-24 observability: obligations nobody claimed + records nobody claimed. */