@telora/daemon 0.24.16 → 0.24.25

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 (211) hide show
  1. package/build-info.json +7 -3
  2. package/dist/ci/candidate-failure-record.d.ts +104 -0
  3. package/dist/ci/candidate-failure-record.d.ts.map +1 -0
  4. package/dist/ci/candidate-failure-record.js +135 -0
  5. package/dist/ci/candidate-failure-record.js.map +1 -0
  6. package/dist/ci/candidate-ref.d.ts.map +1 -1
  7. package/dist/ci/candidate-ref.js +10 -3
  8. package/dist/ci/candidate-ref.js.map +1 -1
  9. package/dist/ci/ci-status-git.d.ts.map +1 -1
  10. package/dist/ci/ci-status-git.js +1 -0
  11. package/dist/ci/ci-status-git.js.map +1 -1
  12. package/dist/ci/ci-status.d.ts.map +1 -1
  13. package/dist/ci/ci-status.js +1 -0
  14. package/dist/ci/ci-status.js.map +1 -1
  15. package/dist/ci/gate-ref.d.ts +10 -1
  16. package/dist/ci/gate-ref.d.ts.map +1 -1
  17. package/dist/ci/gate-ref.js +11 -4
  18. package/dist/ci/gate-ref.js.map +1 -1
  19. package/dist/ci/product-ci-repo.d.ts.map +1 -1
  20. package/dist/ci/product-ci-repo.js +3 -2
  21. package/dist/ci/product-ci-repo.js.map +1 -1
  22. package/dist/completion/review-exit/review-exit-phase.js +7 -3
  23. package/dist/completion/review-exit/review-exit-phase.js.map +1 -1
  24. package/dist/completion/review-exit/review-exit-rehoming.d.ts +27 -3
  25. package/dist/completion/review-exit/review-exit-rehoming.d.ts.map +1 -1
  26. package/dist/completion/review-exit/review-exit-rehoming.js +29 -5
  27. package/dist/completion/review-exit/review-exit-rehoming.js.map +1 -1
  28. package/dist/composition/execution-merge-context.d.ts +13 -0
  29. package/dist/composition/execution-merge-context.d.ts.map +1 -1
  30. package/dist/composition/execution-merge-context.js +76 -3
  31. package/dist/composition/execution-merge-context.js.map +1 -1
  32. package/dist/composition/focus-activity-live-sources.d.ts.map +1 -1
  33. package/dist/composition/focus-activity-live-sources.js +1 -0
  34. package/dist/composition/focus-activity-live-sources.js.map +1 -1
  35. package/dist/composition/runtime-heartbeat-context.d.ts +11 -0
  36. package/dist/composition/runtime-heartbeat-context.d.ts.map +1 -1
  37. package/dist/composition/runtime-heartbeat-context.js +26 -1
  38. package/dist/composition/runtime-heartbeat-context.js.map +1 -1
  39. package/dist/composition/team-liveness-phase-sources.d.ts.map +1 -1
  40. package/dist/composition/team-liveness-phase-sources.js +1 -0
  41. package/dist/composition/team-liveness-phase-sources.js.map +1 -1
  42. package/dist/composition/verification-context.d.ts.map +1 -1
  43. package/dist/composition/verification-context.js +34 -4
  44. package/dist/composition/verification-context.js.map +1 -1
  45. package/dist/focus/control-state.d.ts +12 -6
  46. package/dist/focus/control-state.d.ts.map +1 -1
  47. package/dist/focus/control-state.js +16 -9
  48. package/dist/focus/control-state.js.map +1 -1
  49. package/dist/focus/heartbeat-metadata.d.ts +5 -0
  50. package/dist/focus/heartbeat-metadata.d.ts.map +1 -1
  51. package/dist/focus/heartbeat-metadata.js +12 -0
  52. package/dist/focus/heartbeat-metadata.js.map +1 -1
  53. package/dist/focus/heartbeat.d.ts.map +1 -1
  54. package/dist/focus/heartbeat.js +1 -0
  55. package/dist/focus/heartbeat.js.map +1 -1
  56. package/dist/focus/host-pressure.d.ts +77 -0
  57. package/dist/focus/host-pressure.d.ts.map +1 -0
  58. package/dist/focus/host-pressure.js +83 -0
  59. package/dist/focus/host-pressure.js.map +1 -0
  60. package/dist/focus-stage-lifecycle/index.js +1 -1
  61. package/dist/focus-stage-lifecycle/index.js.map +1 -1
  62. package/dist/focus-stage-lifecycle/wedge-watchdog.d.ts +17 -7
  63. package/dist/focus-stage-lifecycle/wedge-watchdog.d.ts.map +1 -1
  64. package/dist/focus-stage-lifecycle/wedge-watchdog.js +58 -28
  65. package/dist/focus-stage-lifecycle/wedge-watchdog.js.map +1 -1
  66. package/dist/git/types.js +1 -1
  67. package/dist/merge/candidate-ci-failures.d.ts +72 -0
  68. package/dist/merge/candidate-ci-failures.d.ts.map +1 -0
  69. package/dist/merge/candidate-ci-failures.js +240 -0
  70. package/dist/merge/candidate-ci-failures.js.map +1 -0
  71. package/dist/merge/candidate-red-record.d.ts +44 -0
  72. package/dist/merge/candidate-red-record.d.ts.map +1 -0
  73. package/dist/merge/candidate-red-record.js +98 -0
  74. package/dist/merge/candidate-red-record.js.map +1 -0
  75. package/dist/merge/candidate-rework.d.ts +4 -3
  76. package/dist/merge/candidate-rework.d.ts.map +1 -1
  77. package/dist/merge/candidate-rework.js +46 -14
  78. package/dist/merge/candidate-rework.js.map +1 -1
  79. package/dist/merge/candidate-tip-attribution.d.ts +135 -0
  80. package/dist/merge/candidate-tip-attribution.d.ts.map +1 -0
  81. package/dist/merge/candidate-tip-attribution.js +319 -0
  82. package/dist/merge/candidate-tip-attribution.js.map +1 -0
  83. package/dist/merge/completion-merge-driver.d.ts +49 -5
  84. package/dist/merge/completion-merge-driver.d.ts.map +1 -1
  85. package/dist/merge/completion-merge-driver.js +56 -2
  86. package/dist/merge/completion-merge-driver.js.map +1 -1
  87. package/dist/merge/completion-merge-gate.d.ts +4 -1
  88. package/dist/merge/completion-merge-gate.d.ts.map +1 -1
  89. package/dist/merge/completion-merge-gate.js +9 -8
  90. package/dist/merge/completion-merge-gate.js.map +1 -1
  91. package/dist/queries/api-client/shared.d.ts.map +1 -1
  92. package/dist/queries/api-client/shared.js +6 -1
  93. package/dist/queries/api-client/shared.js.map +1 -1
  94. package/dist/queries/candidate-ci-reds/candidate-ci-reds.d.ts +35 -0
  95. package/dist/queries/candidate-ci-reds/candidate-ci-reds.d.ts.map +1 -0
  96. package/dist/queries/candidate-ci-reds/candidate-ci-reds.js +42 -0
  97. package/dist/queries/candidate-ci-reds/candidate-ci-reds.js.map +1 -0
  98. package/dist/queries/candidate-ci-reds/gate-escape-files.d.ts +16 -0
  99. package/dist/queries/candidate-ci-reds/gate-escape-files.d.ts.map +1 -0
  100. package/dist/queries/candidate-ci-reds/gate-escape-files.js +51 -0
  101. package/dist/queries/candidate-ci-reds/gate-escape-files.js.map +1 -0
  102. package/dist/queries/focuses/ended-arm.d.ts +55 -7
  103. package/dist/queries/focuses/ended-arm.d.ts.map +1 -1
  104. package/dist/queries/focuses/ended-arm.js +59 -8
  105. package/dist/queries/focuses/ended-arm.js.map +1 -1
  106. package/dist/queries/focuses/focus-discovery.d.ts +18 -8
  107. package/dist/queries/focuses/focus-discovery.d.ts.map +1 -1
  108. package/dist/queries/focuses/focus-discovery.js +1 -0
  109. package/dist/queries/focuses/focus-discovery.js.map +1 -1
  110. package/dist/queries/sessions/daemon-connection.d.ts.map +1 -1
  111. package/dist/queries/sessions/daemon-connection.js +1 -0
  112. package/dist/queries/sessions/daemon-connection.js.map +1 -1
  113. package/dist/queries/verification/verification.js +1 -1
  114. package/dist/queries/wire-schemas/schemas.d.ts +2 -0
  115. package/dist/queries/wire-schemas/schemas.d.ts.map +1 -1
  116. package/dist/queries/wire-schemas/schemas.js +10 -3
  117. package/dist/queries/wire-schemas/schemas.js.map +1 -1
  118. package/dist/resolvers/delivery-acceptance-criteria.d.ts +23 -1
  119. package/dist/resolvers/delivery-acceptance-criteria.d.ts.map +1 -1
  120. package/dist/resolvers/delivery-acceptance-criteria.js +52 -8
  121. package/dist/resolvers/delivery-acceptance-criteria.js.map +1 -1
  122. package/dist/shell/unified-shell.d.ts +25 -0
  123. package/dist/shell/unified-shell.d.ts.map +1 -1
  124. package/dist/shell/unified-shell.js +26 -80
  125. package/dist/shell/unified-shell.js.map +1 -1
  126. package/dist/shell/update-restart.d.ts +47 -0
  127. package/dist/shell/update-restart.d.ts.map +1 -0
  128. package/dist/shell/update-restart.js +124 -0
  129. package/dist/shell/update-restart.js.map +1 -0
  130. package/dist/supervision/escalation-suppression.js +1 -1
  131. package/dist/supervision/recorded-new-intent.d.ts +49 -0
  132. package/dist/supervision/recorded-new-intent.d.ts.map +1 -1
  133. package/dist/supervision/recorded-new-intent.js +10 -2
  134. package/dist/supervision/recorded-new-intent.js.map +1 -1
  135. package/dist/testing/fixtures/api-client.js +1 -1
  136. package/dist/testing/fixtures/completion-judge-stubs.d.ts +7 -2
  137. package/dist/testing/fixtures/completion-judge-stubs.d.ts.map +1 -1
  138. package/dist/testing/fixtures/completion-judge-stubs.js +11 -1
  139. package/dist/testing/fixtures/completion-judge-stubs.js.map +1 -1
  140. package/dist/testing/fixtures/index.js +1 -1
  141. package/dist/testing/fixtures/merge-log-capture.d.ts.map +1 -1
  142. package/dist/testing/fixtures/merge-log-capture.js +1 -0
  143. package/dist/testing/fixtures/merge-log-capture.js.map +1 -1
  144. package/dist/testing/fixtures/recording-api-client.js +1 -1
  145. package/dist/testing/fixtures/verification-deps.d.ts +7 -0
  146. package/dist/testing/fixtures/verification-deps.d.ts.map +1 -1
  147. package/dist/testing/fixtures/verification-deps.js +17 -1
  148. package/dist/testing/fixtures/verification-deps.js.map +1 -1
  149. package/dist/testing/fixtures/vitest-failure-log.d.ts.map +1 -1
  150. package/dist/testing/fixtures/vitest-failure-log.js +2 -1
  151. package/dist/testing/fixtures/vitest-failure-log.js.map +1 -1
  152. package/dist/update/auto-update.d.ts +11 -5
  153. package/dist/update/auto-update.d.ts.map +1 -1
  154. package/dist/update/auto-update.js +11 -5
  155. package/dist/update/auto-update.js.map +1 -1
  156. package/dist/update/restart-contract.d.ts +62 -0
  157. package/dist/update/restart-contract.d.ts.map +1 -0
  158. package/dist/update/restart-contract.js +137 -0
  159. package/dist/update/restart-contract.js.map +1 -0
  160. package/dist/verify/attribution/verification-baseline-run.d.ts.map +1 -1
  161. package/dist/verify/attribution/verification-baseline-run.js +1 -0
  162. package/dist/verify/attribution/verification-baseline-run.js.map +1 -1
  163. package/dist/verify/ci-gate/ci-gate-dispatch.d.ts +3 -0
  164. package/dist/verify/ci-gate/ci-gate-dispatch.d.ts.map +1 -1
  165. package/dist/verify/ci-gate/ci-gate-dispatch.js +31 -9
  166. package/dist/verify/ci-gate/ci-gate-dispatch.js.map +1 -1
  167. package/dist/verify/ci-gate/ci-gate-wait-record.d.ts +6 -0
  168. package/dist/verify/ci-gate/ci-gate-wait-record.d.ts.map +1 -1
  169. package/dist/verify/ci-gate/ci-gate-wait-record.js +1 -0
  170. package/dist/verify/ci-gate/ci-gate-wait-record.js.map +1 -1
  171. package/dist/verify/deterministic-gate/verification-checkout.d.ts +12 -2
  172. package/dist/verify/deterministic-gate/verification-checkout.d.ts.map +1 -1
  173. package/dist/verify/deterministic-gate/verification-checkout.js +18 -3
  174. package/dist/verify/deterministic-gate/verification-checkout.js.map +1 -1
  175. package/dist/verify/deterministic-gate/verification-deterministic.js +1 -1
  176. package/dist/verify/deterministic-gate/verification-failure-parser.d.ts +30 -9
  177. package/dist/verify/deterministic-gate/verification-failure-parser.d.ts.map +1 -1
  178. package/dist/verify/deterministic-gate/verification-failure-parser.js +52 -8
  179. package/dist/verify/deterministic-gate/verification-failure-parser.js.map +1 -1
  180. package/dist/verify/engine/verification-commit-sharing.d.ts.map +1 -1
  181. package/dist/verify/engine/verification-commit-sharing.js +6 -2
  182. package/dist/verify/engine/verification-commit-sharing.js.map +1 -1
  183. package/dist/verify/engine/verification-deps.d.ts +9 -0
  184. package/dist/verify/engine/verification-deps.d.ts.map +1 -1
  185. package/dist/verify/engine/verification-deps.js +1 -1
  186. package/dist/verify/engine/verification-engine.d.ts.map +1 -1
  187. package/dist/verify/engine/verification-engine.js +4 -1
  188. package/dist/verify/engine/verification-engine.js.map +1 -1
  189. package/dist/verify/escaped-tests/escaped-pass-store.d.ts +19 -0
  190. package/dist/verify/escaped-tests/escaped-pass-store.d.ts.map +1 -0
  191. package/dist/verify/escaped-tests/escaped-pass-store.js +90 -0
  192. package/dist/verify/escaped-tests/escaped-pass-store.js.map +1 -0
  193. package/dist/verify/escaped-tests/escaped-test-run.d.ts +39 -0
  194. package/dist/verify/escaped-tests/escaped-test-run.d.ts.map +1 -0
  195. package/dist/verify/escaped-tests/escaped-test-run.js +156 -0
  196. package/dist/verify/escaped-tests/escaped-test-run.js.map +1 -0
  197. package/dist/verify/escaped-tests/escaped-test-set.d.ts +65 -0
  198. package/dist/verify/escaped-tests/escaped-test-set.d.ts.map +1 -0
  199. package/dist/verify/escaped-tests/escaped-test-set.js +95 -0
  200. package/dist/verify/escaped-tests/escaped-test-set.js.map +1 -0
  201. package/dist/verify/escaped-tests/escaped-tests-source.d.ts +44 -0
  202. package/dist/verify/escaped-tests/escaped-tests-source.d.ts.map +1 -0
  203. package/dist/verify/escaped-tests/escaped-tests-source.js +142 -0
  204. package/dist/verify/escaped-tests/escaped-tests-source.js.map +1 -0
  205. package/dist/verify/gate-config/verification-manifest.d.ts +12 -1
  206. package/dist/verify/gate-config/verification-manifest.d.ts.map +1 -1
  207. package/dist/verify/gate-config/verification-manifest.js +12 -1
  208. package/dist/verify/gate-config/verification-manifest.js.map +1 -1
  209. package/dist/verify/gate-verdicts/commit-verdict-record.js +1 -1
  210. package/package.json +2 -2
  211. package/scripts/telora-daemon-wrapper.sh +16 -7
@@ -0,0 +1,156 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/verify/ci-gate/ci-gate-dispatch.test.ts,
3
+ // packages/daemon/src/verify/escaped-tests/escaped-gate-run.test.ts,
4
+ // packages/daemon/src/verify/escaped-tests/escaped-test-run.test.ts,
5
+ // packages/daemon/src/verify/escaped-tests/escaped-tests-source.test.ts
6
+ // PINNED-BY-END
7
+ /**
8
+ * How escaped test files run INSIDE a gate run, and what the gate log then says
9
+ * about them. Pure.
10
+ *
11
+ * The gate run executes the gate command and THEN the product's declared re-run
12
+ * command over the escaped files (verify/failing-test-rerun/rerun-command.ts
13
+ * renderRerunCommand), in the same checkout and environment, as one POSIX shell
14
+ * script ({@link composeGateWithEscapedTests}). The local runner spawns it
15
+ * (`sh -c`); a CI-run gate ships it as the spec's command (scripts/ci-gate-run.sh
16
+ * runs it with bash). Either way the added run's output lands in the gate log
17
+ * after a marker block:
18
+ *
19
+ * (escaped tests: 2 file(s) a gate escape recorded)
20
+ * (escaped test: <first file>)
21
+ * (escaped test: <second file>)
22
+ * ...the re-run command's output...
23
+ * (escaped tests exit: 1; gate exit: 0)
24
+ *
25
+ * EXIT RULE: the script exits with the gate's own code when the gate failed,
26
+ * else with the added run's code -- so a failing escaped test fails the run, and
27
+ * every downstream reader (the failure parser, the same-commit flake retry,
28
+ * per-file base attribution, the rework Bug) sees its failures in the log with
29
+ * no path of its own. One exception is settled by the daemon after the run
30
+ * ({@link settleEscapedRun}): an added run that exited non-zero while naming NO
31
+ * failing test could not run the files (a file the re-run command's runner does
32
+ * not include, a crashed command). That is not evidence about the change, so it
33
+ * does not fail a gate that passed -- the stance per-file base attribution takes
34
+ * on an inconclusive run (verify/attribution/verification-baseline-files.ts).
35
+ *
36
+ * The gate's IDENTITY is untouched by any of this: the run applies and records
37
+ * the ORIGINAL gate command (appliedConfig, the per-commit verdict key, the CI
38
+ * spec key), so a verdict rendered with added files is read back under the
39
+ * gate's normal key.
40
+ *
41
+ * @module verify/escaped-tests/escaped-test-run
42
+ */
43
+ import { parseFailingTests } from '../deterministic-gate/verification-failure-parser.js';
44
+ import { logRoots, renderRerunCommand, testFileOfId } from '../failing-test-rerun/rerun-command.js';
45
+ /** The marker line opening the added run's section of a gate log. */
46
+ export function escapedTestsMarker(count) {
47
+ return `(escaped tests: ${count} file(s) a gate escape recorded)`;
48
+ }
49
+ const MARKER_RE = /^\(escaped tests: (\d+) file\(s\) a gate escape recorded\)$/;
50
+ const FILE_RE = /^\(escaped test: (.+)\)$/;
51
+ const EXIT_RE = /^\(escaped tests exit: (\d+); gate exit: (\d+)\)$/;
52
+ // eslint-disable-next-line no-control-regex -- strips terminal colour codes from the log
53
+ const ANSI_RE = /\u001b\[[0-9;]*m/g;
54
+ /** POSIX single-quote one shell word, always. */
55
+ function quote(word) {
56
+ return `'${word.replace(/'/g, `'\\''`)}'`;
57
+ }
58
+ /**
59
+ * The script that runs `gateCommand`, then the re-run command over `files`, and
60
+ * exits by the module's exit rule. Each command runs in its own subshell, on its
61
+ * own lines, so neither can end the other (an `exit`, a trailing comment). The
62
+ * added run's stderr joins its stdout, so its whole output lands in the log
63
+ * between the marker block and the exit line, in order, however the two streams
64
+ * of the run are read. Pure.
65
+ */
66
+ export function composeGateWithEscapedTests(gateCommand, rerunTemplate, files) {
67
+ const markers = [escapedTestsMarker(files.length), ...files.map((f) => `(escaped test: ${f})`)].map(quote).join(' ');
68
+ return [
69
+ '(',
70
+ gateCommand,
71
+ ')',
72
+ '__telora_gate_rc=$?',
73
+ `printf '%s\\n' ${markers}`,
74
+ '(',
75
+ renderRerunCommand(rerunTemplate, files),
76
+ ') 2>&1',
77
+ '__telora_escaped_rc=$?',
78
+ `printf '(escaped tests exit: %s; gate exit: %s)\\n' "$__telora_escaped_rc" "$__telora_gate_rc"`,
79
+ 'if [ "$__telora_gate_rc" -ne 0 ]; then exit "$__telora_gate_rc"; fi',
80
+ 'exit "$__telora_escaped_rc"',
81
+ ].join('\n');
82
+ }
83
+ /** The escaped-run section of a gate log, or null when the run added no files. Pure. */
84
+ export function readEscapedRun(logText) {
85
+ const lines = (typeof logText === 'string' ? logText : '').replace(ANSI_RE, '').split(/\r?\n/);
86
+ const start = lines.findIndex((line) => MARKER_RE.test(line.trim()));
87
+ if (start < 0)
88
+ return null;
89
+ const count = Number(MARKER_RE.exec(lines[start].trim())[1]);
90
+ const files = [];
91
+ let at = start + 1;
92
+ while (files.length < count && at < lines.length) {
93
+ const match = FILE_RE.exec(lines[at].trim());
94
+ if (!match)
95
+ break;
96
+ files.push(match[1]);
97
+ at++;
98
+ }
99
+ const end = lines.findIndex((line, index) => index >= at && EXIT_RE.test(line.trim()));
100
+ const exit = end >= 0 ? EXIT_RE.exec(lines[end].trim()) : null;
101
+ return {
102
+ files,
103
+ output: lines.slice(at, end >= 0 ? end : lines.length).join('\n'),
104
+ escapedExit: exit ? Number(exit[1]) : null,
105
+ gateExit: exit ? Number(exit[2]) : null,
106
+ };
107
+ }
108
+ /** True when the added run exited non-zero while naming no failing test (module doc). Pure. */
109
+ function isInconclusive(run) {
110
+ return run.escapedExit !== null && run.escapedExit !== 0 && parseFailingTests(run.output).size === 0;
111
+ }
112
+ /**
113
+ * The files the added run PASSED: every file when it exited 0; else every file
114
+ * none of whose failing tests is in its output -- provided each failing test
115
+ * names its file. No file when the run was cut short, could not run the files,
116
+ * or named a failure no file can be told from. Pure.
117
+ */
118
+ export function passedEscapedFiles(logText) {
119
+ const run = readEscapedRun(logText);
120
+ if (run === null || run.escapedExit === null)
121
+ return [];
122
+ if (run.escapedExit === 0)
123
+ return [...run.files];
124
+ const ids = parseFailingTests(run.output);
125
+ if (ids.size === 0)
126
+ return [];
127
+ const roots = logRoots(logText);
128
+ const failing = new Set();
129
+ for (const id of ids) {
130
+ const file = testFileOfId(id, roots);
131
+ if (file === null)
132
+ return [];
133
+ failing.add(file);
134
+ }
135
+ return run.files.filter((file) => !failing.has(file));
136
+ }
137
+ /**
138
+ * The run's result with the one exception of the exit rule applied (module
139
+ * doc): a run whose gate passed and whose added run only failed INCONCLUSIVELY
140
+ * is the gate's pass. Every other result is returned unchanged. Pure.
141
+ */
142
+ export function settleEscapedRun(result, logText) {
143
+ if (result.outcome !== 'failed' || result.timedOut)
144
+ return result;
145
+ const run = readEscapedRun(logText);
146
+ if (run === null || run.gateExit !== 0 || !isInconclusive(run))
147
+ return result;
148
+ return {
149
+ ...result,
150
+ outcome: 'passed',
151
+ exitCode: 0,
152
+ rationale: `deterministic check passed (exit 0); the ${run.files.length} escaped test file(s) it added could not be run by the `
153
+ + `declared re-run command (exit ${run.escapedExit}, no failing test named) -- not charged to the change`,
154
+ };
155
+ }
156
+ //# sourceMappingURL=escaped-test-run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escaped-test-run.js","sourceRoot":"","sources":["../../../src/verify/escaped-tests/escaped-test-run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Which escaped test files a product's next gate run adds: the pure rule.
3
+ *
4
+ * A GATE ESCAPE is a red integration candidate whose approved head the verify
5
+ * gate had passed (queries/candidate-ci-reds/). The test files CI named there
6
+ * are a gap in the product's own test selection -- not one focus's -- so the
7
+ * set is per product: every focus of the product runs them.
8
+ *
9
+ * The rule:
10
+ * - A file is IN the set while it has passed fewer than
11
+ * ESCAPED_FILE_REQUIRED_PASSES gate runs on distinct commits SINCE its
12
+ * newest escape. A pass recorded before that escape does not count, so a
13
+ * newer escape of the file restarts its count.
14
+ * - The set is the newest-escaped files first, at most ESCAPED_SET_MAX_FILES.
15
+ * - A path that is not a plain repo-relative path (absolute, `..`, a control
16
+ * character) is never in it: it is handed to a shell command.
17
+ *
18
+ * Passes are kept per product, host-local ({@link EscapedPassState}; the durable
19
+ * store, which reads a missing or unreadable record as no passes, is
20
+ * escaped-pass-store.ts): the rule fails toward running a test, never toward
21
+ * dropping it. The record keeps EVERY file's passes -- at most
22
+ * ESCAPED_FILE_REQUIRED_PASSES each, so it grows only with the product's distinct
23
+ * escaped files -- and never evicts one, so a file that met the exit rule stays
24
+ * out of the set until a newer escape names it again.
25
+ *
26
+ * @module verify/escaped-tests/escaped-test-set
27
+ */
28
+ /** Gate runs on distinct commits an escaped file must pass, since its newest escape, to leave the set. */
29
+ export declare const ESCAPED_FILE_REQUIRED_PASSES = 3;
30
+ /** The most escaped files one gate run adds. */
31
+ export declare const ESCAPED_SET_MAX_FILES = 10;
32
+ /** One escaped file as the product's escape record lists it. */
33
+ export interface EscapedFile {
34
+ /** Repo-relative test file path. */
35
+ file: string;
36
+ /** When the newest escape naming the file was recorded (ISO 8601). */
37
+ lastEscapeAt: string;
38
+ }
39
+ /** One gate run an escaped file passed: the commit it judged, and when (epoch ms). */
40
+ export interface EscapedPass {
41
+ sha: string;
42
+ at: number;
43
+ }
44
+ /** A product's recorded passes, per file. */
45
+ export interface EscapedPassState {
46
+ files: Record<string, EscapedPass[]>;
47
+ }
48
+ /** A path safe to hand a re-run command as one test file: repo-relative, no `..`, no control characters. Pure. */
49
+ export declare function isRunnableTestPath(file: string): boolean;
50
+ /** Distinct-commit passes of `file` recorded after `lastEscapeAt` (an unparseable instant counts every pass as before it). Pure. */
51
+ export declare function passesSinceEscape(state: EscapedPassState, file: string, lastEscapeAt: string): number;
52
+ /**
53
+ * The files a gate run adds (module doc): runnable paths, newest escape first,
54
+ * fewer than the required passes since that escape, at most the cap. Pure.
55
+ */
56
+ export declare function escapedTestSet(escapes: readonly EscapedFile[], state: EscapedPassState): string[];
57
+ /**
58
+ * The state with one pass recorded for each of `files` at commit `sha`, at
59
+ * `now`. A commit already recorded for a file counts once (its instant moves to
60
+ * `now`). Each file keeps its newest ESCAPED_FILE_REQUIRED_PASSES passes -- only
61
+ * passes after the newest escape count, and no more are needed -- and every
62
+ * other file's record is kept as it was: none is ever evicted (module doc). Pure.
63
+ */
64
+ export declare function recordEscapedPasses(state: EscapedPassState, files: readonly string[], sha: string, now: number): EscapedPassState;
65
+ //# sourceMappingURL=escaped-test-set.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escaped-test-set.d.ts","sourceRoot":"","sources":["../../../src/verify/escaped-tests/escaped-test-set.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,0GAA0G;AAC1G,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAE9C,gDAAgD;AAChD,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC,gEAAgE;AAChE,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,sFAAsF;AACtF,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,6CAA6C;AAC7C,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;CACtC;AAED,kHAAkH;AAClH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMxD;AAED,oIAAoI;AACpI,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAKrG;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,EAAE,KAAK,EAAE,gBAAgB,GAAG,MAAM,EAAE,CAcjG;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAOjI"}
@@ -0,0 +1,95 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/verify/ci-gate/ci-gate-dispatch.test.ts,
3
+ // packages/daemon/src/verify/escaped-tests/escaped-gate-run.test.ts,
4
+ // packages/daemon/src/verify/escaped-tests/escaped-pass-store.test.ts,
5
+ // packages/daemon/src/verify/escaped-tests/escaped-test-set.test.ts,
6
+ // packages/daemon/src/verify/escaped-tests/escaped-tests-source.test.ts
7
+ // PINNED-BY-END
8
+ /**
9
+ * Which escaped test files a product's next gate run adds: the pure rule.
10
+ *
11
+ * A GATE ESCAPE is a red integration candidate whose approved head the verify
12
+ * gate had passed (queries/candidate-ci-reds/). The test files CI named there
13
+ * are a gap in the product's own test selection -- not one focus's -- so the
14
+ * set is per product: every focus of the product runs them.
15
+ *
16
+ * The rule:
17
+ * - A file is IN the set while it has passed fewer than
18
+ * ESCAPED_FILE_REQUIRED_PASSES gate runs on distinct commits SINCE its
19
+ * newest escape. A pass recorded before that escape does not count, so a
20
+ * newer escape of the file restarts its count.
21
+ * - The set is the newest-escaped files first, at most ESCAPED_SET_MAX_FILES.
22
+ * - A path that is not a plain repo-relative path (absolute, `..`, a control
23
+ * character) is never in it: it is handed to a shell command.
24
+ *
25
+ * Passes are kept per product, host-local ({@link EscapedPassState}; the durable
26
+ * store, which reads a missing or unreadable record as no passes, is
27
+ * escaped-pass-store.ts): the rule fails toward running a test, never toward
28
+ * dropping it. The record keeps EVERY file's passes -- at most
29
+ * ESCAPED_FILE_REQUIRED_PASSES each, so it grows only with the product's distinct
30
+ * escaped files -- and never evicts one, so a file that met the exit rule stays
31
+ * out of the set until a newer escape names it again.
32
+ *
33
+ * @module verify/escaped-tests/escaped-test-set
34
+ */
35
+ /** Gate runs on distinct commits an escaped file must pass, since its newest escape, to leave the set. */
36
+ export const ESCAPED_FILE_REQUIRED_PASSES = 3;
37
+ /** The most escaped files one gate run adds. */
38
+ export const ESCAPED_SET_MAX_FILES = 10;
39
+ /** A path safe to hand a re-run command as one test file: repo-relative, no `..`, no control characters. Pure. */
40
+ export function isRunnableTestPath(file) {
41
+ if (file.length === 0 || file.length > 1000 || file.trim() !== file)
42
+ return false;
43
+ // eslint-disable-next-line no-control-regex -- a control character in a path handed to a shell is exactly what is refused.
44
+ if (/[\u0000-\u001f\u007f]/.test(file))
45
+ return false;
46
+ if (file.startsWith('/') || file.startsWith('-'))
47
+ return false;
48
+ return !file.split('/').some((segment) => segment === '..' || segment === '');
49
+ }
50
+ /** Distinct-commit passes of `file` recorded after `lastEscapeAt` (an unparseable instant counts every pass as before it). Pure. */
51
+ export function passesSinceEscape(state, file, lastEscapeAt) {
52
+ const since = Date.parse(lastEscapeAt);
53
+ const passes = state.files[file] ?? [];
54
+ if (!Number.isFinite(since))
55
+ return 0;
56
+ return new Set(passes.filter((p) => p.at > since).map((p) => p.sha)).size;
57
+ }
58
+ /**
59
+ * The files a gate run adds (module doc): runnable paths, newest escape first,
60
+ * fewer than the required passes since that escape, at most the cap. Pure.
61
+ */
62
+ export function escapedTestSet(escapes, state) {
63
+ const seen = new Set();
64
+ const ordered = escapes
65
+ .map((e, index) => ({ ...e, index, t: Date.parse(e.lastEscapeAt) }))
66
+ .sort((a, b) => (Number.isFinite(b.t) ? b.t : 0) - (Number.isFinite(a.t) ? a.t : 0) || a.index - b.index);
67
+ const out = [];
68
+ for (const escape of ordered) {
69
+ if (seen.has(escape.file) || !isRunnableTestPath(escape.file))
70
+ continue;
71
+ seen.add(escape.file);
72
+ if (passesSinceEscape(state, escape.file, escape.lastEscapeAt) >= ESCAPED_FILE_REQUIRED_PASSES)
73
+ continue;
74
+ out.push(escape.file);
75
+ if (out.length >= ESCAPED_SET_MAX_FILES)
76
+ break;
77
+ }
78
+ return out;
79
+ }
80
+ /**
81
+ * The state with one pass recorded for each of `files` at commit `sha`, at
82
+ * `now`. A commit already recorded for a file counts once (its instant moves to
83
+ * `now`). Each file keeps its newest ESCAPED_FILE_REQUIRED_PASSES passes -- only
84
+ * passes after the newest escape count, and no more are needed -- and every
85
+ * other file's record is kept as it was: none is ever evicted (module doc). Pure.
86
+ */
87
+ export function recordEscapedPasses(state, files, sha, now) {
88
+ const next = { ...state.files };
89
+ for (const file of files) {
90
+ const kept = (next[file] ?? []).filter((p) => p.sha !== sha);
91
+ next[file] = [...kept, { sha, at: now }].sort((a, b) => b.at - a.at).slice(0, ESCAPED_FILE_REQUIRED_PASSES);
92
+ }
93
+ return { files: next };
94
+ }
95
+ //# sourceMappingURL=escaped-test-set.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escaped-test-set.js","sourceRoot":"","sources":["../../../src/verify/escaped-tests/escaped-test-set.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { DeclaredRerun, StrategyResult } from '../deterministic-gate/verification-deterministic.js';
2
+ import { type EscapedFile } from './escaped-test-set.js';
3
+ import type { EscapedPassStore } from './escaped-pass-store.js';
4
+ /** How long a gate run waits for the product's escapes before it runs without them. */
5
+ export declare const ESCAPED_SET_READ_TIMEOUT_MS = 10000;
6
+ /** The collaborators, bound at the composition root (composition/verification-context.ts). */
7
+ export interface EscapedTestsDeps {
8
+ /** The product's escaped files, newest escape first (product-api); throws on a failed read. */
9
+ listEscapes: (productId: string) => Promise<EscapedFile[]>;
10
+ passes: EscapedPassStore;
11
+ now: () => number;
12
+ /** How long the escape read may take before the run goes ahead with no added files. */
13
+ readTimeoutMs: number;
14
+ /** Which of `files` exist at commit `sha` of the repo at `repoPath` (a CI-run gate has no checkout here). */
15
+ filesAtCommit: (repoPath: string, sha: string, files: readonly string[]) => string[];
16
+ }
17
+ /** The gate a run applies, as far as adding files is concerned. */
18
+ type GateToExtend = {
19
+ command: string;
20
+ rerun?: DeclaredRerun | undefined;
21
+ };
22
+ /** The product's escaped set (module doc); [] on any failed or slow read. */
23
+ export declare function escapedFilesFor(productId: string | null, deps: EscapedTestsDeps): Promise<string[]>;
24
+ /** The files of `files` present in the checkout at `checkoutPath` (never a path outside it). */
25
+ export declare function filesInCheckout(checkoutPath: string, files: readonly string[]): string[];
26
+ /** The command a LOCAL gate run of `productId` spawns in the checkout at `checkoutPath` (module doc). */
27
+ export declare function localGateCommand(productId: string | null, gate: GateToExtend, checkoutPath: string, deps: EscapedTestsDeps): Promise<string>;
28
+ /**
29
+ * The command a CI-run gate of `productId` at `sha` ships in its spec (module
30
+ * doc), and the escaped files it adds ([] when the command is the gate's own).
31
+ */
32
+ export declare function ciGateCommand(productId: string | null, gate: GateToExtend, repoPath: string, sha: string, deps: EscapedTestsDeps): Promise<{
33
+ command: string;
34
+ files: string[];
35
+ }>;
36
+ /**
37
+ * A gate run's result with its escaped files judged (module doc): the exit
38
+ * rule's exception applied, and the files it passed counted. A product that
39
+ * declares no re-run command added no files, and its result is returned as is,
40
+ * with no log read.
41
+ */
42
+ export declare function judgeEscapedTests(productId: string | null, result: StrategyResult, readLogText: (source: StrategyResult) => string, deps: EscapedTestsDeps): StrategyResult;
43
+ export {};
44
+ //# sourceMappingURL=escaped-tests-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escaped-tests-source.d.ts","sourceRoot":"","sources":["../../../src/verify/escaped-tests/escaped-tests-source.ts"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,qDAAqD,CAAC;AAEzG,OAAO,EAAuC,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAE9F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAIhE,uFAAuF;AACvF,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAElD,8FAA8F;AAC9F,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC3D,MAAM,EAAE,gBAAgB,CAAC;IACzB,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,uFAAuF;IACvF,aAAa,EAAE,MAAM,CAAC;IACtB,6GAA6G;IAC7G,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC;CACtF;AAED,mEAAmE;AACnE,KAAK,YAAY,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,aAAa,GAAG,SAAS,CAAA;CAAE,CAAC;AAY3E,6EAA6E;AAC7E,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAUzG;AAED,gGAAgG;AAChG,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAMxF;AASD,yGAAyG;AACzG,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,IAAI,EAAE,YAAY,EAClB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,MAAM,CAAC,CAIjB;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,IAAI,EAAE,YAAY,EAClB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAc/C;AAoBD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,MAAM,EAC/C,IAAI,EAAE,gBAAgB,GACrB,cAAc,CAOhB"}
@@ -0,0 +1,142 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/verify/escaped-tests/escaped-gate-run.test.ts,
3
+ // packages/daemon/src/verify/escaped-tests/escaped-tests-source.test.ts
4
+ // PINNED-BY-END
5
+ /**
6
+ * Escaped test files in a product's gate runs: what a gate run adds, and what
7
+ * its verdict counts.
8
+ *
9
+ * ADDED, ONLY TO THE GATE RUN OF A FOCUS HEAD (the local strategy's run of a
10
+ * default-on candidate, verify/deterministic-gate/verification-checkout.ts, and
11
+ * the CI hand-over of one, verify/ci-gate/ci-gate-dispatch.ts) -- never to a
12
+ * re-run, a continuation, a base run or a rework re-check. A run adds files only
13
+ * when the judged commit's manifest declares a `rerunCommand` and the product's
14
+ * escaped set (escaped-test-set.ts) holds files that exist at the judged commit;
15
+ * the gate command it then spawns is escaped-test-run.ts
16
+ * composeGateWithEscapedTests. With no `rerunCommand`, or no such file, the run
17
+ * spawns exactly the gate command, as before.
18
+ *
19
+ * The set is read ONCE per gate run, fail-soft: a failed or slow read of the
20
+ * product's escapes (bounded by `readTimeoutMs`) adds no files and is logged --
21
+ * the gate is never blocked or failed by it.
22
+ *
23
+ * COUNTED, when a run rendered a verdict about its commit (verify/engine/
24
+ * verification-commit-sharing.ts judges every local and CI run through
25
+ * {@link judgeEscapedTests}): each added file the run passed
26
+ * (escaped-test-run.ts passedEscapedFiles) gets one pass for the judged commit
27
+ * in the product's host-local record (escaped-pass-store.ts); a commit counts
28
+ * once. The same step applies the exit rule's one exception
29
+ * (escaped-test-run.ts settleEscapedRun).
30
+ *
31
+ * @module verify/escaped-tests/escaped-tests-source
32
+ */
33
+ import { existsSync } from 'node:fs';
34
+ import { resolve, sep } from 'node:path';
35
+ import { createLogger } from '../../log/daemon-log.js';
36
+ import { escapedTestSet, recordEscapedPasses } from './escaped-test-set.js';
37
+ import { composeGateWithEscapedTests, passedEscapedFiles, settleEscapedRun } from './escaped-test-run.js';
38
+ const log = createLogger({ module: 'verification' });
39
+ /** How long a gate run waits for the product's escapes before it runs without them. */
40
+ export const ESCAPED_SET_READ_TIMEOUT_MS = 10_000;
41
+ /** `promise`, or a rejection once `ms` has passed. */
42
+ function withTimeout(promise, ms) {
43
+ let timer;
44
+ const timeout = new Promise((_, reject) => {
45
+ timer = setTimeout(() => reject(new Error(`no answer within ${ms} ms`)), ms);
46
+ timer.unref?.();
47
+ });
48
+ return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
49
+ }
50
+ /** The product's escaped set (module doc); [] on any failed or slow read. */
51
+ export async function escapedFilesFor(productId, deps) {
52
+ if (!productId)
53
+ return [];
54
+ try {
55
+ const escapes = await withTimeout(deps.listEscapes(productId), deps.readTimeoutMs);
56
+ return escapedTestSet(escapes, deps.passes.read(productId));
57
+ }
58
+ catch (err) {
59
+ log.warn(`[verification] escaped tests: the gate escapes of product ${productId} could not be read `
60
+ + `(${err.message}); this gate run adds no escaped test files`);
61
+ return [];
62
+ }
63
+ }
64
+ /** The files of `files` present in the checkout at `checkoutPath` (never a path outside it). */
65
+ export function filesInCheckout(checkoutPath, files) {
66
+ const root = resolve(checkoutPath);
67
+ return files.filter((file) => {
68
+ const path = resolve(root, file);
69
+ return path.startsWith(root + sep) && existsSync(path);
70
+ });
71
+ }
72
+ /** The command a gate run spawns: the gate's own, plus the escaped `files` when there are any and a re-run command names them. */
73
+ function extendGate(gate, files, where) {
74
+ if (!gate.rerun || files.length === 0)
75
+ return gate.command;
76
+ log.info(`[verification] escaped tests: ${where} adds ${files.length} test file(s) a gate escape recorded: ${files.join(', ')}`);
77
+ return composeGateWithEscapedTests(gate.command, gate.rerun.command, files);
78
+ }
79
+ /** The command a LOCAL gate run of `productId` spawns in the checkout at `checkoutPath` (module doc). */
80
+ export async function localGateCommand(productId, gate, checkoutPath, deps) {
81
+ if (!gate.rerun)
82
+ return gate.command;
83
+ const files = filesInCheckout(checkoutPath, await escapedFilesFor(productId, deps));
84
+ return extendGate(gate, files, `the gate run in ${checkoutPath}`);
85
+ }
86
+ /**
87
+ * The command a CI-run gate of `productId` at `sha` ships in its spec (module
88
+ * doc), and the escaped files it adds ([] when the command is the gate's own).
89
+ */
90
+ export async function ciGateCommand(productId, gate, repoPath, sha, deps) {
91
+ const unchanged = { command: gate.command, files: [] };
92
+ if (!gate.rerun)
93
+ return unchanged;
94
+ const wanted = await escapedFilesFor(productId, deps);
95
+ if (wanted.length === 0)
96
+ return unchanged;
97
+ let present;
98
+ try {
99
+ present = new Set(deps.filesAtCommit(repoPath, sha, wanted));
100
+ }
101
+ catch (err) {
102
+ log.warn(`[verification] escaped tests: could not list the escaped files at ${sha.slice(0, 8)} (${err.message}); none added`);
103
+ return unchanged;
104
+ }
105
+ const files = wanted.filter((f) => present.has(f));
106
+ return { command: extendGate(gate, files, `the CI gate run of ${sha.slice(0, 8)}`), files };
107
+ }
108
+ /** True when `result` is a gate run's verdict about its commit (no re-run, timeout or unavailable checkout). */
109
+ function renderedGateVerdict(result) {
110
+ return (result.outcome === 'passed' || result.outcome === 'failed')
111
+ && typeof result.verifiedSha === 'string' && result.verifiedSha.length > 0
112
+ && result.timedOut !== true && result.renderedBeforeKill === undefined
113
+ && result.rerunOf === undefined && result.checkoutUnavailable !== true && result.configUnresolved !== true;
114
+ }
115
+ /** Record one pass at the judged commit for each added file the run passed (module doc). */
116
+ function notePasses(productId, result, logText, deps) {
117
+ const passed = passedEscapedFiles(logText);
118
+ if (passed.length === 0)
119
+ return;
120
+ const next = recordEscapedPasses(deps.passes.read(productId), passed, result.verifiedSha, deps.now());
121
+ if (deps.passes.write(productId, next)) {
122
+ log.info(`[verification] escaped tests: ${passed.length} escaped test file(s) passed at ${result.verifiedSha.slice(0, 8)}: ${passed.join(', ')}`);
123
+ }
124
+ }
125
+ /**
126
+ * A gate run's result with its escaped files judged (module doc): the exit
127
+ * rule's exception applied, and the files it passed counted. A product that
128
+ * declares no re-run command added no files, and its result is returned as is,
129
+ * with no log read.
130
+ */
131
+ export function judgeEscapedTests(productId, result, readLogText, deps) {
132
+ if (!result.appliedConfig?.rerun || !renderedGateVerdict(result))
133
+ return result;
134
+ const logText = readLogText(result);
135
+ const settled = settleEscapedRun(result, logText);
136
+ if (settled !== result)
137
+ log.info(`[verification] escaped tests: ${settled.rationale}`);
138
+ if (productId)
139
+ notePasses(productId, result, logText, deps);
140
+ return settled;
141
+ }
142
+ //# sourceMappingURL=escaped-tests-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escaped-tests-source.js","sourceRoot":"","sources":["../../../src/verify/escaped-tests/escaped-tests-source.ts"],"names":[],"mappings":"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"}
@@ -74,6 +74,17 @@ export interface VerifyManifest {
74
74
  * docs/runbook-verify-gate-in-ci.md.
75
75
  */
76
76
  gateRunner?: GateRunnerName;
77
+ /**
78
+ * Optional: `true` declares that the product's CI publishes a FAILURE RECORD
79
+ * for a failed run (refs/ci-failures/<sha>/<run>/<job>, ci/candidate-failure-record.ts;
80
+ * contract: docs/runbook-ci-log-diagnosis.md, "The failure record"). Declared,
81
+ * a red integration candidate whose record has not landed yet waits a bounded
82
+ * window for it before its rework is routed, even on the first record the
83
+ * product ever publishes (merge/candidate-ci-failures.ts awaitsFailureRecord).
84
+ * Absent or `false` => no wait unless this host has held one of the product's
85
+ * records before.
86
+ */
87
+ ciFailureRecords?: boolean;
77
88
  }
78
89
  /** The completion judges a manifest may name. */
79
90
  export declare const COMPLETION_JUDGE_NAMES: readonly ["ci", "local"];
@@ -118,7 +129,7 @@ export declare function gateRunsOnCi(manifest: Pick<VerifyManifest, 'gateRunner'
118
129
  * `rerunMaxFailures` / carries an unknown `completionJudge`, a
119
130
  * `completionCommand` that is not a non-empty string, a non-positive
120
131
  * `completionTimeoutMs`, or a completion command beside `"ci"` /
121
- * carries an unknown `gateRunner`.
132
+ * carries an unknown `gateRunner` / carries a non-boolean `ciFailureRecords`.
122
133
  */
123
134
  export declare function readVerifyManifestFrom(repoPath: string | undefined, fs: VerifyManifestFs): VerifyManifest | undefined;
124
135
  /** Read the repo's verification manifest from the working tree on disk (see readVerifyManifestFrom). */
@@ -1 +1 @@
1
- {"version":3,"file":"verification-manifest.d.ts","sourceRoot":"","sources":["../../../src/verify/gate-config/verification-manifest.ts"],"names":[],"mappings":"AA8DA;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,uBAAuB,CAAC;AAE7D;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;;;;OAWG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,+FAA+F;IAC/F,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,+EAA+E;IAC/E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B;AAED,iDAAiD;AACjD,eAAO,MAAM,sBAAsB,0BAA2B,CAAC;AAC/D,MAAM,MAAM,mBAAmB,GAAG,OAAO,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAExE,uDAAuD;AACvD,eAAO,MAAM,iBAAiB,0BAA2B,CAAC;AAC1D,MAAM,MAAM,cAAc,GAAG,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAE9D,0EAA0E;AAC1E,eAAO,MAAM,uBAAuB,YAAY,CAAC;AAEjD,wGAAwG;AACxG,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qDAAqD;IACrD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CAChC;AAQD,qFAAqF;AACrF,MAAM,MAAM,oBAAoB,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,KAAK,cAAc,GAAG,SAAS,CAAC;AAEhG,0DAA0D;AAC1D,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAElE;AAsFD,8GAA8G;AAC9G,wBAAgB,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC,GAAG,SAAS,GAAG,OAAO,CAE9F;AAYD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,EAAE,EAAE,gBAAgB,GAAG,cAAc,GAAG,SAAS,CAqDrH;AAED,wGAAwG;AACxG,eAAO,MAAM,kBAAkB,EAAE,oBAAoF,CAAC;AAEtH;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,GAAG,SAAS,CAOzG"}
1
+ {"version":3,"file":"verification-manifest.d.ts","sourceRoot":"","sources":["../../../src/verify/gate-config/verification-manifest.ts"],"names":[],"mappings":"AA8DA;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,uBAAuB,CAAC;AAE7D;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;;;;OAWG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,+FAA+F;IAC/F,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,+EAA+E;IAC/E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B;;;;;;;;;OASG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,iDAAiD;AACjD,eAAO,MAAM,sBAAsB,0BAA2B,CAAC;AAC/D,MAAM,MAAM,mBAAmB,GAAG,OAAO,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAExE,uDAAuD;AACvD,eAAO,MAAM,iBAAiB,0BAA2B,CAAC;AAC1D,MAAM,MAAM,cAAc,GAAG,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAE9D,0EAA0E;AAC1E,eAAO,MAAM,uBAAuB,YAAY,CAAC;AAEjD,wGAAwG;AACxG,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qDAAqD;IACrD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CAChC;AAQD,qFAAqF;AACrF,MAAM,MAAM,oBAAoB,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,KAAK,cAAc,GAAG,SAAS,CAAC;AAEhG,0DAA0D;AAC1D,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAElE;AAsFD,8GAA8G;AAC9G,wBAAgB,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC,GAAG,SAAS,GAAG,OAAO,CAE9F;AAoBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,EAAE,EAAE,gBAAgB,GAAG,cAAc,GAAG,SAAS,CAuDrH;AAED,wGAAwG;AACxG,eAAO,MAAM,kBAAkB,EAAE,oBAAoF,CAAC;AAEtH;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,GAAG,SAAS,CAOzG"}
@@ -172,6 +172,15 @@ function optionalGateRunner(raw, path) {
172
172
  }
173
173
  return { gateRunner: runner };
174
174
  }
175
+ /** Validate the optional failure-record declaration (present-but-not-boolean is malformed, like every key). */
176
+ function optionalCiFailureRecords(raw, path) {
177
+ const value = raw.ciFailureRecords;
178
+ if (value === undefined)
179
+ return {};
180
+ if (typeof value !== 'boolean')
181
+ manifestError(path, `"ciFailureRecords" must be true or false (got ${JSON.stringify(value)})`);
182
+ return { ciFailureRecords: value };
183
+ }
175
184
  /**
176
185
  * Read the repo's verification manifest through `fs`.
177
186
  *
@@ -187,7 +196,7 @@ function optionalGateRunner(raw, path) {
187
196
  * `rerunMaxFailures` / carries an unknown `completionJudge`, a
188
197
  * `completionCommand` that is not a non-empty string, a non-positive
189
198
  * `completionTimeoutMs`, or a completion command beside `"ci"` /
190
- * carries an unknown `gateRunner`.
199
+ * carries an unknown `gateRunner` / carries a non-boolean `ciFailureRecords`.
191
200
  */
192
201
  export function readVerifyManifestFrom(repoPath, fs) {
193
202
  if (!repoPath)
@@ -228,6 +237,7 @@ export function readVerifyManifestFrom(repoPath, fs) {
228
237
  const rerun = optionalRerun(raw, path);
229
238
  const completion = optionalCompletion(raw, path);
230
239
  const gateRunner = optionalGateRunner(raw, path);
240
+ const ciFailureRecords = optionalCiFailureRecords(raw, path);
231
241
  return {
232
242
  command,
233
243
  ...(timeoutMs !== undefined ? { timeoutMs } : {}),
@@ -236,6 +246,7 @@ export function readVerifyManifestFrom(repoPath, fs) {
236
246
  ...rerun,
237
247
  ...completion,
238
248
  ...gateRunner,
249
+ ...ciFailureRecords,
239
250
  };
240
251
  }
241
252
  /** Read the repo's verification manifest from the working tree on disk (see readVerifyManifestFrom). */