@telora/daemon 0.23.12 → 0.23.15

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 (277) hide show
  1. package/build-info.json +4 -3
  2. package/dist/backends/claude/one-shot-model.d.ts +11 -0
  3. package/dist/backends/claude/one-shot-model.d.ts.map +1 -1
  4. package/dist/backends/claude/one-shot-model.js +7 -4
  5. package/dist/backends/claude/one-shot-model.js.map +1 -1
  6. package/dist/backends/claude/one-shot-session.d.ts +15 -0
  7. package/dist/backends/claude/one-shot-session.d.ts.map +1 -0
  8. package/dist/backends/claude/one-shot-session.js +120 -0
  9. package/dist/backends/claude/one-shot-session.js.map +1 -0
  10. package/dist/ci/candidate-ref.d.ts +28 -0
  11. package/dist/ci/candidate-ref.d.ts.map +1 -0
  12. package/dist/ci/candidate-ref.js +81 -0
  13. package/dist/ci/candidate-ref.js.map +1 -0
  14. package/dist/ci/ci-sensor-loop.d.ts.map +1 -1
  15. package/dist/ci/ci-sensor-loop.js +2 -1
  16. package/dist/ci/ci-sensor-loop.js.map +1 -1
  17. package/dist/ci/ci-status-git.d.ts +9 -0
  18. package/dist/ci/ci-status-git.d.ts.map +1 -1
  19. package/dist/ci/ci-status-git.js +16 -2
  20. package/dist/ci/ci-status-git.js.map +1 -1
  21. package/dist/ci/ci-status.d.ts.map +1 -1
  22. package/dist/ci/ci-status.js +5 -1
  23. package/dist/ci/ci-status.js.map +1 -1
  24. package/dist/completion/state-cascade/state-cascade.d.ts +14 -1
  25. package/dist/completion/state-cascade/state-cascade.d.ts.map +1 -1
  26. package/dist/completion/state-cascade/state-cascade.js +26 -1
  27. package/dist/completion/state-cascade/state-cascade.js.map +1 -1
  28. package/dist/completion/team-exit/merge-phase.d.ts +11 -10
  29. package/dist/completion/team-exit/merge-phase.d.ts.map +1 -1
  30. package/dist/completion/team-exit/merge-phase.js +10 -10
  31. package/dist/completion/team-exit/merge-phase.js.map +1 -1
  32. package/dist/completion/team-teardown/team-lifecycle.d.ts +4 -3
  33. package/dist/completion/team-teardown/team-lifecycle.d.ts.map +1 -1
  34. package/dist/completion/team-teardown/team-lifecycle.js +8 -8
  35. package/dist/completion/team-teardown/team-lifecycle.js.map +1 -1
  36. package/dist/composition/attribution-base-git.d.ts.map +1 -1
  37. package/dist/composition/attribution-base-git.js +3 -1
  38. package/dist/composition/attribution-base-git.js.map +1 -1
  39. package/dist/composition/execution-git-context.d.ts.map +1 -1
  40. package/dist/composition/execution-git-context.js +2 -0
  41. package/dist/composition/execution-git-context.js.map +1 -1
  42. package/dist/composition/execution-merge-context.d.ts +11 -3
  43. package/dist/composition/execution-merge-context.d.ts.map +1 -1
  44. package/dist/composition/execution-merge-context.js +60 -12
  45. package/dist/composition/execution-merge-context.js.map +1 -1
  46. package/dist/composition/process-close-context.js +3 -3
  47. package/dist/composition/process-close-context.js.map +1 -1
  48. package/dist/composition/state-cascade-context.d.ts.map +1 -1
  49. package/dist/composition/state-cascade-context.js +10 -2
  50. package/dist/composition/state-cascade-context.js.map +1 -1
  51. package/dist/composition/team-lifecycle-context.js +3 -3
  52. package/dist/composition/team-lifecycle-context.js.map +1 -1
  53. package/dist/composition/verification-context.d.ts.map +1 -1
  54. package/dist/composition/verification-context.js +25 -27
  55. package/dist/composition/verification-context.js.map +1 -1
  56. package/dist/delivery/delivery-merge-ops.d.ts +2 -0
  57. package/dist/delivery/delivery-merge-ops.d.ts.map +1 -1
  58. package/dist/delivery/delivery-merge-ops.js +2 -2
  59. package/dist/delivery/delivery-merge-ops.js.map +1 -1
  60. package/dist/delivery/delivery-reconciler.d.ts +9 -0
  61. package/dist/delivery/delivery-reconciler.d.ts.map +1 -1
  62. package/dist/delivery/delivery-reconciler.js +42 -31
  63. package/dist/delivery/delivery-reconciler.js.map +1 -1
  64. package/dist/focus/focus-spawn-decision.d.ts.map +1 -1
  65. package/dist/focus/focus-spawn-decision.js +1 -0
  66. package/dist/focus/focus-spawn-decision.js.map +1 -1
  67. package/dist/git/index.d.ts.map +1 -1
  68. package/dist/git/index.js +2 -1
  69. package/dist/git/index.js.map +1 -1
  70. package/dist/git/merge.d.ts +12 -2
  71. package/dist/git/merge.d.ts.map +1 -1
  72. package/dist/git/merge.js +32 -33
  73. package/dist/git/merge.js.map +1 -1
  74. package/dist/merge/approval-gate-state.d.ts.map +1 -1
  75. package/dist/merge/approval-gate-state.js +8 -7
  76. package/dist/merge/approval-gate-state.js.map +1 -1
  77. package/dist/merge/candidate-rework.d.ts +44 -0
  78. package/dist/merge/candidate-rework.d.ts.map +1 -0
  79. package/dist/merge/candidate-rework.js +152 -0
  80. package/dist/merge/candidate-rework.js.map +1 -0
  81. package/dist/merge/completion-hold-record.d.ts +20 -0
  82. package/dist/merge/completion-hold-record.d.ts.map +1 -0
  83. package/dist/merge/completion-hold-record.js +130 -0
  84. package/dist/merge/completion-hold-record.js.map +1 -0
  85. package/dist/merge/completion-merge-driver.d.ts +96 -0
  86. package/dist/merge/completion-merge-driver.d.ts.map +1 -0
  87. package/dist/merge/completion-merge-driver.js +94 -0
  88. package/dist/merge/completion-merge-driver.js.map +1 -0
  89. package/dist/merge/completion-merge-gate.d.ts +71 -0
  90. package/dist/merge/completion-merge-gate.d.ts.map +1 -0
  91. package/dist/merge/completion-merge-gate.js +185 -0
  92. package/dist/merge/completion-merge-gate.js.map +1 -0
  93. package/dist/merge/completion-merge-pending.d.ts +30 -0
  94. package/dist/merge/completion-merge-pending.d.ts.map +1 -0
  95. package/dist/merge/completion-merge-pending.js +75 -0
  96. package/dist/merge/completion-merge-pending.js.map +1 -0
  97. package/dist/merge/focus-merge.d.ts +21 -6
  98. package/dist/merge/focus-merge.d.ts.map +1 -1
  99. package/dist/merge/focus-merge.js +65 -45
  100. package/dist/merge/focus-merge.js.map +1 -1
  101. package/dist/merge/focus-worktree-removal.d.ts +6 -6
  102. package/dist/merge/focus-worktree-removal.d.ts.map +1 -1
  103. package/dist/merge/focus-worktree-removal.js +10 -6
  104. package/dist/merge/focus-worktree-removal.js.map +1 -1
  105. package/dist/merge/merge-back-conflict.d.ts.map +1 -1
  106. package/dist/merge/merge-back-conflict.js +2 -0
  107. package/dist/merge/merge-back-conflict.js.map +1 -1
  108. package/dist/merge/merge-back-followup.d.ts.map +1 -1
  109. package/dist/merge/merge-back-followup.js +7 -0
  110. package/dist/merge/merge-back-followup.js.map +1 -1
  111. package/dist/merge/merge-back-loop.d.ts +36 -14
  112. package/dist/merge/merge-back-loop.d.ts.map +1 -1
  113. package/dist/merge/merge-back-loop.js +109 -66
  114. package/dist/merge/merge-back-loop.js.map +1 -1
  115. package/dist/merge/merge-gate-coverage.d.ts +9 -61
  116. package/dist/merge/merge-gate-coverage.d.ts.map +1 -1
  117. package/dist/merge/merge-gate-coverage.js +33 -167
  118. package/dist/merge/merge-gate-coverage.js.map +1 -1
  119. package/dist/nexus/escalation-bridge.d.ts.map +1 -1
  120. package/dist/nexus/escalation-bridge.js +2 -1
  121. package/dist/nexus/escalation-bridge.js.map +1 -1
  122. package/dist/nexus/file-escalation.d.ts.map +1 -1
  123. package/dist/nexus/file-escalation.js +5 -1
  124. package/dist/nexus/file-escalation.js.map +1 -1
  125. package/dist/queries/api-client/shared.js +1 -1
  126. package/dist/queries/deliveries/deliveries.d.ts.map +1 -1
  127. package/dist/queries/deliveries/deliveries.js +1 -0
  128. package/dist/queries/deliveries/deliveries.js.map +1 -1
  129. package/dist/queries/delivery-gate-state/delivery-gate-state.js +1 -1
  130. package/dist/queries/delivery-gate-state/verify-own-holds.d.ts +10 -3
  131. package/dist/queries/delivery-gate-state/verify-own-holds.d.ts.map +1 -1
  132. package/dist/queries/delivery-gate-state/verify-own-holds.js +2 -0
  133. package/dist/queries/delivery-gate-state/verify-own-holds.js.map +1 -1
  134. package/dist/queries/escalations/cause-key.d.ts.map +1 -1
  135. package/dist/queries/escalations/cause-key.js +1 -0
  136. package/dist/queries/escalations/cause-key.js.map +1 -1
  137. package/dist/queries/escalations/escalations.d.ts.map +1 -1
  138. package/dist/queries/escalations/escalations.js +2 -1
  139. package/dist/queries/escalations/escalations.js.map +1 -1
  140. package/dist/queries/focuses/focuses.d.ts +12 -2
  141. package/dist/queries/focuses/focuses.d.ts.map +1 -1
  142. package/dist/queries/focuses/focuses.js +24 -3
  143. package/dist/queries/focuses/focuses.js.map +1 -1
  144. package/dist/queries/verification/verification.js +1 -1
  145. package/dist/spawn/review-hold-park.d.ts.map +1 -1
  146. package/dist/spawn/review-hold-park.js +1 -0
  147. package/dist/spawn/review-hold-park.js.map +1 -1
  148. package/dist/supervision/agent-resolvable-retirement.d.ts +1 -1
  149. package/dist/supervision/agent-resolvable-retirement.d.ts.map +1 -1
  150. package/dist/supervision/agent-resolvable-retirement.js +2 -1
  151. package/dist/supervision/agent-resolvable-retirement.js.map +1 -1
  152. package/dist/supervision/escalation-filing.js +1 -1
  153. package/dist/supervision/incident-correlation.d.ts +3 -0
  154. package/dist/supervision/incident-correlation.d.ts.map +1 -1
  155. package/dist/supervision/incident-correlation.js +4 -0
  156. package/dist/supervision/incident-correlation.js.map +1 -1
  157. package/dist/supervision/null-role-spawn.d.ts.map +1 -1
  158. package/dist/supervision/null-role-spawn.js +1 -0
  159. package/dist/supervision/null-role-spawn.js.map +1 -1
  160. package/dist/supervision/spawn-cooldown.js +1 -1
  161. package/dist/supervision/stop-rows-work-holds.d.ts +4 -4
  162. package/dist/supervision/stop-rows-work-holds.d.ts.map +1 -1
  163. package/dist/supervision/stop-rows-work-holds.js +17 -15
  164. package/dist/supervision/stop-rows-work-holds.js.map +1 -1
  165. package/dist/testing/fixtures/api-client.js +1 -1
  166. package/dist/testing/fixtures/index.js +1 -1
  167. package/dist/testing/fixtures/judge-session.d.ts +14 -0
  168. package/dist/testing/fixtures/judge-session.d.ts.map +1 -0
  169. package/dist/testing/fixtures/judge-session.js +10 -0
  170. package/dist/testing/fixtures/judge-session.js.map +1 -0
  171. package/dist/testing/fixtures/verification-deps.d.ts +2 -1
  172. package/dist/testing/fixtures/verification-deps.d.ts.map +1 -1
  173. package/dist/testing/fixtures/verification-deps.js +4 -1
  174. package/dist/testing/fixtures/verification-deps.js.map +1 -1
  175. package/dist/testing/fixtures/vitest-failure-log.d.ts +10 -0
  176. package/dist/testing/fixtures/vitest-failure-log.d.ts.map +1 -0
  177. package/dist/testing/fixtures/vitest-failure-log.js +27 -0
  178. package/dist/testing/fixtures/vitest-failure-log.js.map +1 -0
  179. package/dist/types/index.js +1 -1
  180. package/dist/verify/attribution/verification-attribution.d.ts.map +1 -1
  181. package/dist/verify/attribution/verification-attribution.js +2 -1
  182. package/dist/verify/attribution/verification-attribution.js.map +1 -1
  183. package/dist/verify/attribution/verification-baseline-files.d.ts +29 -0
  184. package/dist/verify/attribution/verification-baseline-files.d.ts.map +1 -0
  185. package/dist/verify/attribution/verification-baseline-files.js +125 -0
  186. package/dist/verify/attribution/verification-baseline-files.js.map +1 -0
  187. package/dist/verify/attribution/verification-baseline-run.d.ts.map +1 -1
  188. package/dist/verify/attribution/verification-baseline-run.js +1 -0
  189. package/dist/verify/attribution/verification-baseline-run.js.map +1 -1
  190. package/dist/verify/attribution/verification-baseline.d.ts +15 -0
  191. package/dist/verify/attribution/verification-baseline.d.ts.map +1 -1
  192. package/dist/verify/attribution/verification-baseline.js +28 -3
  193. package/dist/verify/attribution/verification-baseline.js.map +1 -1
  194. package/dist/verify/deterministic-gate/verification-checkout.d.ts.map +1 -1
  195. package/dist/verify/deterministic-gate/verification-checkout.js +1 -0
  196. package/dist/verify/deterministic-gate/verification-checkout.js.map +1 -1
  197. package/dist/verify/deterministic-gate/verification-deterministic.d.ts +30 -0
  198. package/dist/verify/deterministic-gate/verification-deterministic.d.ts.map +1 -1
  199. package/dist/verify/deterministic-gate/verification-deterministic.js +2 -0
  200. package/dist/verify/deterministic-gate/verification-deterministic.js.map +1 -1
  201. package/dist/verify/deterministic-gate/verification-gate-stages.d.ts.map +1 -1
  202. package/dist/verify/deterministic-gate/verification-gate-stages.js +1 -0
  203. package/dist/verify/deterministic-gate/verification-gate-stages.js.map +1 -1
  204. package/dist/verify/effect-reconciliation/judge-invoker.d.ts +14 -4
  205. package/dist/verify/effect-reconciliation/judge-invoker.d.ts.map +1 -1
  206. package/dist/verify/effect-reconciliation/judge-invoker.js +30 -12
  207. package/dist/verify/effect-reconciliation/judge-invoker.js.map +1 -1
  208. package/dist/verify/effect-reconciliation/verify-reconciliation.d.ts +18 -5
  209. package/dist/verify/effect-reconciliation/verify-reconciliation.d.ts.map +1 -1
  210. package/dist/verify/effect-reconciliation/verify-reconciliation.js +75 -18
  211. package/dist/verify/effect-reconciliation/verify-reconciliation.js.map +1 -1
  212. package/dist/verify/effect-reconciliation/verify-restatement.js +1 -1
  213. package/dist/verify/engine/verification-commit-sharing.d.ts.map +1 -1
  214. package/dist/verify/engine/verification-commit-sharing.js +14 -2
  215. package/dist/verify/engine/verification-commit-sharing.js.map +1 -1
  216. package/dist/verify/engine/verification-deps.d.ts +23 -0
  217. package/dist/verify/engine/verification-deps.d.ts.map +1 -1
  218. package/dist/verify/engine/verification-deps.js +1 -1
  219. package/dist/verify/engine/verification-engine.js +1 -1
  220. package/dist/verify/failing-test-rerun/failing-test-rerunner.d.ts +34 -0
  221. package/dist/verify/failing-test-rerun/failing-test-rerunner.d.ts.map +1 -0
  222. package/dist/verify/failing-test-rerun/failing-test-rerunner.js +34 -0
  223. package/dist/verify/failing-test-rerun/failing-test-rerunner.js.map +1 -0
  224. package/dist/verify/failing-test-rerun/failing-test-retry.d.ts +80 -0
  225. package/dist/verify/failing-test-rerun/failing-test-retry.d.ts.map +1 -0
  226. package/dist/verify/failing-test-rerun/failing-test-retry.js +102 -0
  227. package/dist/verify/failing-test-rerun/failing-test-retry.js.map +1 -0
  228. package/dist/verify/failing-test-rerun/flaky-test-finding.d.ts +11 -0
  229. package/dist/verify/failing-test-rerun/flaky-test-finding.d.ts.map +1 -0
  230. package/dist/verify/failing-test-rerun/flaky-test-finding.js +63 -0
  231. package/dist/verify/failing-test-rerun/flaky-test-finding.js.map +1 -0
  232. package/dist/verify/failing-test-rerun/rerun-command.d.ts +45 -0
  233. package/dist/verify/failing-test-rerun/rerun-command.d.ts.map +1 -0
  234. package/dist/verify/failing-test-rerun/rerun-command.js +132 -0
  235. package/dist/verify/failing-test-rerun/rerun-command.js.map +1 -0
  236. package/dist/verify/failing-test-rerun/rework-recheck.d.ts +41 -0
  237. package/dist/verify/failing-test-rerun/rework-recheck.d.ts.map +1 -0
  238. package/dist/verify/failing-test-rerun/rework-recheck.js +169 -0
  239. package/dist/verify/failing-test-rerun/rework-recheck.js.map +1 -0
  240. package/dist/verify/gate-config/verification-config.d.ts.map +1 -1
  241. package/dist/verify/gate-config/verification-config.js +16 -2
  242. package/dist/verify/gate-config/verification-config.js.map +1 -1
  243. package/dist/verify/gate-config/verification-manifest.d.ts +26 -1
  244. package/dist/verify/gate-config/verification-manifest.d.ts.map +1 -1
  245. package/dist/verify/gate-config/verification-manifest.js +31 -1
  246. package/dist/verify/gate-config/verification-manifest.js.map +1 -1
  247. package/dist/verify/inspection-strategies/verification-strategies.js +1 -1
  248. package/dist/verify/inspection-strategies/verification-strategies.js.map +1 -1
  249. package/dist/verify/outcome-routing/incurable-model-hold.d.ts +9 -3
  250. package/dist/verify/outcome-routing/incurable-model-hold.d.ts.map +1 -1
  251. package/dist/verify/outcome-routing/incurable-model-hold.js +18 -5
  252. package/dist/verify/outcome-routing/incurable-model-hold.js.map +1 -1
  253. package/dist/verify/outcome-routing/repeating-model-hold.d.ts +30 -0
  254. package/dist/verify/outcome-routing/repeating-model-hold.d.ts.map +1 -0
  255. package/dist/verify/outcome-routing/repeating-model-hold.js +135 -0
  256. package/dist/verify/outcome-routing/repeating-model-hold.js.map +1 -0
  257. package/dist/verify/outcome-routing/verification-history-reset.d.ts.map +1 -1
  258. package/dist/verify/outcome-routing/verification-history-reset.js +1 -0
  259. package/dist/verify/outcome-routing/verification-history-reset.js.map +1 -1
  260. package/dist/verify/outcome-routing/verification-model-unavailable-hold.d.ts +10 -4
  261. package/dist/verify/outcome-routing/verification-model-unavailable-hold.d.ts.map +1 -1
  262. package/dist/verify/outcome-routing/verification-model-unavailable-hold.js +24 -6
  263. package/dist/verify/outcome-routing/verification-model-unavailable-hold.js.map +1 -1
  264. package/dist/verify/run-selection/default-on-guards.js +1 -1
  265. package/dist/verify/run-selection/pre-review-gate-dispatch.d.ts +4 -3
  266. package/dist/verify/run-selection/pre-review-gate-dispatch.d.ts.map +1 -1
  267. package/dist/verify/run-selection/pre-review-gate-dispatch.js +23 -7
  268. package/dist/verify/run-selection/pre-review-gate-dispatch.js.map +1 -1
  269. package/dist/verify/run-selection/verification-default-on.js +1 -1
  270. package/dist/verify/run-selection/verification-default-on.js.map +1 -1
  271. package/dist/worktree/provision.d.ts.map +1 -1
  272. package/dist/worktree/provision.js +1 -0
  273. package/dist/worktree/provision.js.map +1 -1
  274. package/dist/worktree/safety.d.ts.map +1 -1
  275. package/dist/worktree/safety.js +1 -0
  276. package/dist/worktree/safety.js.map +1 -1
  277. package/package.json +1 -1
@@ -0,0 +1,102 @@
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/failing-test-rerun/failing-test-retry.test.ts,
3
+ // packages/daemon/src/verify/failing-test-rerun/rework-recheck.test.ts
4
+ // PINNED-BY-END
5
+ import { hasStageToContinue, parseGateRunStages } from '../deterministic-gate/verification-gate-stages.js';
6
+ import { isTestStage } from '../deterministic-gate/verification-failure-parser.js';
7
+ import { planRerun, RERUN_RETRY_ATTEMPTS } from './rerun-command.js';
8
+ import { createLogger } from '../../log/daemon-log.js';
9
+ const log = createLogger({ module: 'verification' });
10
+ /** A re-run that rendered a verdict about `sha`: its outcome, else null (no verdict). */
11
+ export function rerunVerdictAt(run, sha) {
12
+ if (run.verifiedSha !== sha || run.timedOut === true || run.configUnresolved === true)
13
+ return null;
14
+ return run.outcome === 'passed' || run.outcome === 'failed' ? run.outcome : null;
15
+ }
16
+ /**
17
+ * Re-run `files` at `sha` until a run passes or the attempts run out: the
18
+ * passing attempt (1-based), 0 when every attempt failed, or null when a run
19
+ * rendered no verdict about `sha` (the caller then decides nothing from it).
20
+ * `onFailedRun` sees each failed run.
21
+ */
22
+ export async function rerunUntilPass(run, v, files, sha, attempts, onFailedRun) {
23
+ for (let attempt = 1; attempt <= attempts; attempt++) {
24
+ const result = await run(v, files, sha);
25
+ const verdict = rerunVerdictAt(result, sha);
26
+ if (verdict === null)
27
+ return null;
28
+ if (verdict === 'passed')
29
+ return attempt;
30
+ onFailedRun?.(result);
31
+ }
32
+ return 0;
33
+ }
34
+ /**
35
+ * Retry a failed run's failing tests on the same commit (module doc). Returns
36
+ * true when the retry passed the gate -- `result` is then rewritten as a pass
37
+ * carrying `flakyTests` -- and false when the red stands for attribution
38
+ * (`result` untouched).
39
+ */
40
+ export async function retryFailingTestsOnSameCommit(v, result, logText, deps) {
41
+ const sha = result.verifiedSha;
42
+ const applied = result.appliedConfig;
43
+ if (result.outcome !== 'failed' || result.timedOut || result.configUnresolved || !sha || !applied || result.rerunOf)
44
+ return false;
45
+ const plan = planRerun(logText, applied.rerun);
46
+ if (plan.kind !== 'rerun')
47
+ return false;
48
+ const staged = parseGateRunStages(logText, result.exitCode, applied.command);
49
+ if (staged?.failedStage && !isTestStage(staged.failedStage))
50
+ return false;
51
+ const passedOn = await rerunUntilPass(deps.runFailingTestRerun, v, plan.files, sha, RERUN_RETRY_ATTEMPTS);
52
+ if (!passedOn) {
53
+ log.info(`[verification] delivery ${v.deliveryId}: ${plan.ids.length} failing test(s) re-run on ${sha.slice(0, 8)} ` +
54
+ `${passedOn === 0 ? `still fail after ${RERUN_RETRY_ATTEMPTS} attempt(s)` : 'rendered no verdict'} -- the red goes to attribution`);
55
+ return false;
56
+ }
57
+ if (!(await stagesAfterRedPass(v, result, staged, deps)))
58
+ return false;
59
+ const finding = {
60
+ productId: v.productId, focusId: v.focusId, deliveryId: v.deliveryId, deliveryName: v.deliveryName,
61
+ sha, tests: plan.ids, files: plan.files, passedOnAttempt: passedOn, logPath: result.logPath ?? null, origin: 'same_commit_retry',
62
+ };
63
+ try {
64
+ await deps.fileFlakyTestFinding(v, finding);
65
+ }
66
+ catch (err) {
67
+ log.warn(`[verification] delivery ${v.deliveryId}: the flaky-test finding could not be filed (${err.message}) -- ` +
68
+ 'the red is not passed over unrecorded; it goes to attribution');
69
+ return false;
70
+ }
71
+ passAsFlake(result, finding);
72
+ log.info(`[verification] delivery ${v.deliveryId}: ${result.rationale}`);
73
+ return true;
74
+ }
75
+ /** The stages the red cut short ran green at the same commit (or there were none). */
76
+ async function stagesAfterRedPass(v, result, staged, deps) {
77
+ if (!staged || !hasStageToContinue(staged))
78
+ return true;
79
+ try {
80
+ const continued = await deps.runStageContinuation(v, staged.continuation, result);
81
+ if (continued.outcome === 'passed' && continued.verifiedSha === result.verifiedSha)
82
+ return true;
83
+ log.info(`[verification] delivery ${v.deliveryId}: the failing tests passed on re-run, but the gate stages after them ` +
84
+ `did not pass at the same commit (${continued.rationale ?? continued.outcome}) -- the red goes to attribution`);
85
+ }
86
+ catch (err) {
87
+ log.warn(`[verification] delivery ${v.deliveryId}: the continuation after a passing re-run threw: ${err.message}`);
88
+ }
89
+ return false;
90
+ }
91
+ function passAsFlake(result, finding) {
92
+ result.outcome = 'passed';
93
+ result.flakyTests = [...finding.tests];
94
+ result.newFailures = [];
95
+ delete result.failedStage;
96
+ delete result.parseFailure;
97
+ result.rationale =
98
+ `deterministic check failed ${finding.tests.length} test(s) at ${finding.sha.slice(0, 8)} that passed when re-run on the ` +
99
+ `same commit (attempt ${finding.passedOnAttempt} of ${RERUN_RETRY_ATTEMPTS}) -- a flaky test, filed against the product, ` +
100
+ `not charged to the delivery: ${finding.tests.join('; ')}`;
101
+ }
102
+ //# sourceMappingURL=failing-test-retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failing-test-retry.js","sourceRoot":"","sources":["../../../src/verify/failing-test-rerun/failing-test-retry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { FileEscalationInput } from '../../nexus/file-escalation.js';
2
+ import type { FlakyTestFinding, FlakyTestFindingFiler } from './failing-test-retry.js';
3
+ /** The machine discriminator a flaky-test finding is filed under. */
4
+ export declare const FLAKY_TEST_FINDING_KIND = "flaky_test";
5
+ /** The cause a finding is keyed by: the product and the sorted test set. Pure. */
6
+ export declare function flakyTestCauseKey(productId: string, tests: readonly string[]): string;
7
+ /** The filing a finding becomes. Pure; throws when the finding names no product. */
8
+ export declare function flakyTestFilingOf(finding: FlakyTestFinding, organizationId: string): FileEscalationInput;
9
+ /** The production filer over the escalation transport. */
10
+ export declare function buildFlakyTestFindingFiler(fileEscalation: (input: FileEscalationInput) => Promise<void>, organizationId: string): FlakyTestFindingFiler;
11
+ //# sourceMappingURL=flaky-test-finding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flaky-test-finding.d.ts","sourceRoot":"","sources":["../../../src/verify/failing-test-rerun/flaky-test-finding.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEvF,qEAAqE;AACrE,eAAO,MAAM,uBAAuB,eAAe,CAAC;AAEpD,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAGrF;AAED,oFAAoF;AACpF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,GAAG,mBAAmB,CA6BxG;AAED,0DAA0D;AAC1D,wBAAgB,0BAA0B,CACxC,cAAc,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,EAC7D,cAAc,EAAE,MAAM,GACrB,qBAAqB,CAEvB"}
@@ -0,0 +1,63 @@
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/failing-test-rerun/failing-test-retry.test.ts
3
+ // PINNED-BY-END
4
+ /**
5
+ * Filing a flaky-test finding against the product: the record that makes a
6
+ * pass-on-retry legitimate (failing-test-retry.ts).
7
+ *
8
+ * A flake is a defect in the PRODUCT's test suite, not in the delivery whose
9
+ * gate run tripped over it, so it is anchored to the product (and, as
10
+ * provenance, the focus) and carries the tests, their files, the commit and the
11
+ * attempt that passed. It enters the machine lane like every daemon filing
12
+ * (supervision/filing-route.ts: detectors report, agents decide who owns it),
13
+ * keyed by product and test set so the same flake re-observed reads as one
14
+ * cause. A verification with no product cannot be filed against one: that
15
+ * throws, and the retry then leaves the red to attribution.
16
+ *
17
+ * @module verify/failing-test-rerun/flaky-test-finding
18
+ */
19
+ import { createHash } from 'node:crypto';
20
+ import { ESCALATION_DISPOSITIONS, ESCALATION_REASONS } from '@telora/daemon-core';
21
+ /** The machine discriminator a flaky-test finding is filed under. */
22
+ export const FLAKY_TEST_FINDING_KIND = 'flaky_test';
23
+ /** The cause a finding is keyed by: the product and the sorted test set. Pure. */
24
+ export function flakyTestCauseKey(productId, tests) {
25
+ const digest = createHash('sha256').update(JSON.stringify([...tests].sort())).digest('hex').slice(0, 16);
26
+ return `${FLAKY_TEST_FINDING_KIND}::product=${productId}::tests=${digest}`;
27
+ }
28
+ /** The filing a finding becomes. Pure; throws when the finding names no product. */
29
+ export function flakyTestFilingOf(finding, organizationId) {
30
+ const productId = finding.productId;
31
+ if (!productId)
32
+ throw new Error('a flaky-test finding needs a product to be filed against; this verification names none');
33
+ const where = finding.origin === 'same_commit_retry' ? 'on the same commit' : 'when the rework commit was re-checked';
34
+ return {
35
+ organizationId,
36
+ productId,
37
+ ...(finding.focusId ? { focusId: finding.focusId } : {}),
38
+ title: `Flaky test(s): ${finding.files.join(', ')}`,
39
+ reason: `${finding.tests.length} test(s) failed in the verify gate at ${finding.sha.slice(0, 8)} and passed when re-run ${where} ` +
40
+ `(attempt ${finding.passedOnAttempt}) -- a flaky test in the product's suite, not a defect of delivery ` +
41
+ `"${finding.deliveryName}", which was not charged for it. Make the test deterministic or quarantine it: ` +
42
+ finding.tests.join('; '),
43
+ reasonType: ESCALATION_REASONS.ERROR_ENCOUNTERED,
44
+ disposition: ESCALATION_DISPOSITIONS.AGENT_RESOLVABLE,
45
+ humanAct: null,
46
+ kind: FLAKY_TEST_FINDING_KIND,
47
+ details: {
48
+ cause_key: flakyTestCauseKey(productId, finding.tests),
49
+ delivery_id: finding.deliveryId,
50
+ sha: finding.sha,
51
+ tests: finding.tests,
52
+ files: finding.files,
53
+ passed_on_attempt: finding.passedOnAttempt,
54
+ origin: finding.origin,
55
+ ...(finding.logPath ? { log_path: finding.logPath } : {}),
56
+ },
57
+ };
58
+ }
59
+ /** The production filer over the escalation transport. */
60
+ export function buildFlakyTestFindingFiler(fileEscalation, organizationId) {
61
+ return (_v, finding) => fileEscalation(flakyTestFilingOf(finding, organizationId));
62
+ }
63
+ //# sourceMappingURL=flaky-test-finding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flaky-test-finding.js","sourceRoot":"","sources":["../../../src/verify/failing-test-rerun/flaky-test-finding.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,sFAAsF;AACtF,gBAAgB;AAEhB;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAIlF,qEAAqE;AACrE,MAAM,CAAC,MAAM,uBAAuB,GAAG,YAAY,CAAC;AAEpD,kFAAkF;AAClF,MAAM,UAAU,iBAAiB,CAAC,SAAiB,EAAE,KAAwB;IAC3E,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACzG,OAAO,GAAG,uBAAuB,aAAa,SAAS,WAAW,MAAM,EAAE,CAAC;AAC7E,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,iBAAiB,CAAC,OAAyB,EAAE,cAAsB;IACjF,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IACpC,IAAI,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,wFAAwF,CAAC,CAAC;IAC1H,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,KAAK,mBAAmB,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,uCAAuC,CAAC;IACtH,OAAO;QACL,cAAc;QACd,SAAS;QACT,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxD,KAAK,EAAE,kBAAkB,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACnD,MAAM,EACJ,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,yCAAyC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,2BAA2B,KAAK,GAAG;YAC1H,YAAY,OAAO,CAAC,eAAe,qEAAqE;YACxG,IAAI,OAAO,CAAC,YAAY,iFAAiF;YACzG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QAC1B,UAAU,EAAE,kBAAkB,CAAC,iBAAiB;QAChD,WAAW,EAAE,uBAAuB,CAAC,gBAAgB;QACrD,QAAQ,EAAE,IAAI;QACd,IAAI,EAAE,uBAAuB;QAC7B,OAAO,EAAE;YACP,SAAS,EAAE,iBAAiB,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC;YACtD,WAAW,EAAE,OAAO,CAAC,UAAU;YAC/B,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,iBAAiB,EAAE,OAAO,CAAC,eAAe;YAC1C,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1D;KACF,CAAC;AACJ,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,0BAA0B,CACxC,cAA6D,EAC7D,cAAsB;IAEtB,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,cAAc,CAAC,iBAAiB,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;AACrF,CAAC"}
@@ -0,0 +1,45 @@
1
+ import type { DeclaredRerun } from '../deterministic-gate/verification-deterministic.js';
2
+ /** How many times a failing set is re-run on the same commit before it is charged. */
3
+ export declare const RERUN_RETRY_ATTEMPTS = 2;
4
+ /**
5
+ * The checkout-relative test file a failing-test id names, or null when it
6
+ * names none. Ids are `<file> > <suite path> > <test>` (vitest's reporters,
7
+ * verification-failure-parser.ts) or a bare `<file>` (a file that failed to
8
+ * load). An absolute path is made relative to the run's root (`roots`: the
9
+ * `(cwd: ...)` a gate log opens with) or to a daemon checkout directory; an
10
+ * absolute path under neither is not one this checkout can name. Pure.
11
+ */
12
+ export declare function testFileOfId(id: string, roots: readonly string[]): string | null;
13
+ /**
14
+ * The distinct test files a failure set names, sorted, or null when ANY id names
15
+ * none (module doc: a subset re-run must never leave a failing test out). Pure.
16
+ */
17
+ export declare function testFilesOf(ids: Iterable<string>, roots: readonly string[]): string[] | null;
18
+ /**
19
+ * A failing id with its file made checkout-relative, so the same test failing in
20
+ * two checkouts (the HEAD checkout and the base checkout, or two re-runs) yields
21
+ * one id. An id that names no file is returned unchanged. Pure.
22
+ */
23
+ export declare function normalizeTestId(id: string, roots: readonly string[]): string;
24
+ /** The declared re-run command with the files substituted for every `{files}`. Pure. */
25
+ export declare function renderRerunCommand(template: string, files: readonly string[]): string;
26
+ /** What a failure set is, as a re-run sees it. */
27
+ export type RerunPlan = {
28
+ kind: 'rerun';
29
+ ids: string[];
30
+ files: string[];
31
+ command: string;
32
+ } | {
33
+ kind: 'not_rerun';
34
+ reason: string;
35
+ };
36
+ /**
37
+ * Whether a failed run's failing tests are answered by the declared re-run
38
+ * command, and the command that does it. Not when the product declares none,
39
+ * when the run named no failing test, when it named more than the ceiling, or
40
+ * when any failing id names no test file. Pure.
41
+ */
42
+ export declare function planRerun(logText: string, rerun: DeclaredRerun | undefined): RerunPlan;
43
+ /** The `(cwd: <path>)` roots a deterministic run's log opens with. Pure. */
44
+ export declare function logRoots(logText: string): string[];
45
+ //# sourceMappingURL=rerun-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rerun-command.d.ts","sourceRoot":"","sources":["../../../src/verify/failing-test-rerun/rerun-command.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qDAAqD,CAAC;AAEzF,sFAAsF;AACtF,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAStC;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAUhF;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,IAAI,CAQ5F;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAM5E;AAOD,wFAAwF;AACxF,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAErF;AAED,kDAAkD;AAClD,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1C;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,SAAS,CAUtF;AAED,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAOlD"}
@@ -0,0 +1,132 @@
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/failing-test-rerun/rerun-command.test.ts
3
+ // PINNED-BY-END
4
+ /**
5
+ * The pure core of answering a small gate failure through a product's DECLARED
6
+ * per-file re-run command (telora.verify.json `rerunCommand`): which failures
7
+ * are small enough to be answered that way, which test FILES they name, and the
8
+ * command that re-runs exactly those files.
9
+ *
10
+ * Why a declared command and never a derived one: the daemon cannot know how a
11
+ * product runs one test file (vitest, jest, node:test, a workspace runner, a
12
+ * wrapper script). falsifier/falsifier-command.ts guesses that for Telora's own
13
+ * layout only; generalising its guess would run the wrong thing in another
14
+ * product and read the silence as a verdict. So the product declares the
15
+ * command, the daemon substitutes the files, and a product that declares none
16
+ * keeps paying whole-gate runs exactly as before.
17
+ *
18
+ * Why a failing id must name its FILE, or nothing is re-run: a re-run of a
19
+ * subset that silently left out one failing test would read as a pass on the
20
+ * rest. A failure set any of whose ids names no test file (a node:test TAP id
21
+ * carries only the test's description) is not answered by a re-run at all.
22
+ *
23
+ * Decisions (reasoning: docs/daemon/history-verify.md, verify/failing-test-rerun/):
24
+ * - FAILURE-SET CEILING: DEFAULT_RERUN_MAX_FAILURES (5,
25
+ * gate-config/verification-manifest.ts), overridable per product with
26
+ * `rerunMaxFailures`. A flake is one or two tests; a red of dozens is a
27
+ * change, and re-running it file by file buys nothing a whole run does not.
28
+ * - RETRY COUNT: RERUN_RETRY_ATTEMPTS (2). One retry catches most order and
29
+ * timing flakes; a second separates "flaky" from "fails half the time" at
30
+ * the cost of one more small run. Fixed, not configurable: the count is what
31
+ * makes "passed on retry" a uniform claim across products.
32
+ *
33
+ * @module verify/failing-test-rerun/rerun-command
34
+ */
35
+ import { parseFailingTests } from '../deterministic-gate/verification-failure-parser.js';
36
+ import { RERUN_FILES_PLACEHOLDER } from '../gate-config/verification-manifest.js';
37
+ /** How many times a failing set is re-run on the same commit before it is charged. */
38
+ export const RERUN_RETRY_ATTEMPTS = 2;
39
+ /** A test file path: a token ending in a JS/TS source extension. */
40
+ const TEST_FILE_PATH = /^\S+\.[cm]?[jt]sx?$/;
41
+ /** A vitest workspace project label preceding the file: `|web| tests/a.test.ts > ...`. */
42
+ const PROJECT_LABEL = /^\|[^|]+\|\s+/;
43
+ /** The daemon's own checkout directories: `<state>/verification-checkout/<key>/`. */
44
+ const DAEMON_CHECKOUT_PREFIX = /^(?:\/[^\s/]+)*\/verification-(?:checkout|baseline-wt)\/[^\s/]+\//;
45
+ /**
46
+ * The checkout-relative test file a failing-test id names, or null when it
47
+ * names none. Ids are `<file> > <suite path> > <test>` (vitest's reporters,
48
+ * verification-failure-parser.ts) or a bare `<file>` (a file that failed to
49
+ * load). An absolute path is made relative to the run's root (`roots`: the
50
+ * `(cwd: ...)` a gate log opens with) or to a daemon checkout directory; an
51
+ * absolute path under neither is not one this checkout can name. Pure.
52
+ */
53
+ export function testFileOfId(id, roots) {
54
+ const head = (id.split(' > ')[0] ?? '').replace(PROJECT_LABEL, '').trim();
55
+ if (!TEST_FILE_PATH.test(head))
56
+ return null;
57
+ if (!head.startsWith('/'))
58
+ return head.replace(/^\.\//, '');
59
+ for (const root of roots) {
60
+ const prefix = `${root.replace(/\/+$/, '')}/`;
61
+ if (prefix.length > 1 && head.startsWith(prefix))
62
+ return head.slice(prefix.length);
63
+ }
64
+ const underCheckout = head.replace(DAEMON_CHECKOUT_PREFIX, '');
65
+ return underCheckout !== head ? underCheckout : null;
66
+ }
67
+ /**
68
+ * The distinct test files a failure set names, sorted, or null when ANY id names
69
+ * none (module doc: a subset re-run must never leave a failing test out). Pure.
70
+ */
71
+ export function testFilesOf(ids, roots) {
72
+ const files = new Set();
73
+ for (const id of ids) {
74
+ const file = testFileOfId(id, roots);
75
+ if (file === null)
76
+ return null;
77
+ files.add(file);
78
+ }
79
+ return [...files].sort();
80
+ }
81
+ /**
82
+ * A failing id with its file made checkout-relative, so the same test failing in
83
+ * two checkouts (the HEAD checkout and the base checkout, or two re-runs) yields
84
+ * one id. An id that names no file is returned unchanged. Pure.
85
+ */
86
+ export function normalizeTestId(id, roots) {
87
+ const parts = id.split(' > ');
88
+ const file = testFileOfId(id, roots);
89
+ if (file === null)
90
+ return id;
91
+ const label = PROJECT_LABEL.exec(parts[0] ?? '')?.[0] ?? '';
92
+ return [`${label}${file}`, ...parts.slice(1)].join(' > ');
93
+ }
94
+ /** POSIX single-quote one shell word. Pure. */
95
+ function shellQuote(word) {
96
+ return /^[A-Za-z0-9_./@%+=:,-]+$/.test(word) ? word : `'${word.replace(/'/g, `'\\''`)}'`;
97
+ }
98
+ /** The declared re-run command with the files substituted for every `{files}`. Pure. */
99
+ export function renderRerunCommand(template, files) {
100
+ return template.split(RERUN_FILES_PLACEHOLDER).join(files.map(shellQuote).join(' '));
101
+ }
102
+ /**
103
+ * Whether a failed run's failing tests are answered by the declared re-run
104
+ * command, and the command that does it. Not when the product declares none,
105
+ * when the run named no failing test, when it named more than the ceiling, or
106
+ * when any failing id names no test file. Pure.
107
+ */
108
+ export function planRerun(logText, rerun) {
109
+ if (!rerun)
110
+ return { kind: 'not_rerun', reason: 'the product declares no rerunCommand' };
111
+ const ids = [...parseFailingTests(logText)].sort();
112
+ if (ids.length === 0)
113
+ return { kind: 'not_rerun', reason: 'the run named no failing test' };
114
+ if (ids.length > rerun.maxFailures) {
115
+ return { kind: 'not_rerun', reason: `the run failed ${ids.length} tests, more than the ${rerun.maxFailures} a re-run answers` };
116
+ }
117
+ const files = testFilesOf(ids, logRoots(logText));
118
+ if (files === null)
119
+ return { kind: 'not_rerun', reason: 'a failing test id names no test file' };
120
+ return { kind: 'rerun', ids, files, command: renderRerunCommand(rerun.command, files) };
121
+ }
122
+ /** The `(cwd: <path>)` roots a deterministic run's log opens with. Pure. */
123
+ export function logRoots(logText) {
124
+ const roots = [];
125
+ for (const line of (typeof logText === 'string' ? logText : '').split(/\r?\n/)) {
126
+ const match = /^\(cwd: (.+)\)$/.exec(line.trim());
127
+ if (match?.[1])
128
+ roots.push(match[1].trim());
129
+ }
130
+ return roots;
131
+ }
132
+ //# sourceMappingURL=rerun-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rerun-command.js","sourceRoot":"","sources":["../../../src/verify/failing-test-rerun/rerun-command.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,iFAAiF;AACjF,gBAAgB;AAEhB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,sDAAsD,CAAC;AACzF,OAAO,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAGlF,sFAAsF;AACtF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAEtC,oEAAoE;AACpE,MAAM,cAAc,GAAG,qBAAqB,CAAC;AAC7C,0FAA0F;AAC1F,MAAM,aAAa,GAAG,eAAe,CAAC;AACtC,qFAAqF;AACrF,MAAM,sBAAsB,GAAG,mEAAmE,CAAC;AAEnG;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,EAAU,EAAE,KAAwB;IAC/D,MAAM,IAAI,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1E,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC5D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,CAAC;QAC9C,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACrF,CAAC;IACD,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE,EAAE,CAAC,CAAC;IAC/D,OAAO,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC;AACvD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,GAAqB,EAAE,KAAwB;IACzE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,IAAI,GAAG,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACrC,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAC/B,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;AAC3B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,EAAU,EAAE,KAAwB;IAClE,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACrC,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5D,OAAO,CAAC,GAAG,KAAK,GAAG,IAAI,EAAE,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC5D,CAAC;AAED,+CAA+C;AAC/C,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;AAC3F,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,KAAwB;IAC3E,OAAO,QAAQ,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACvF,CAAC;AAOD;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,OAAe,EAAE,KAAgC;IACzE,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,sCAAsC,EAAE,CAAC;IACzF,MAAM,GAAG,GAAG,CAAC,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACnD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,+BAA+B,EAAE,CAAC;IAC5F,IAAI,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QACnC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,kBAAkB,GAAG,CAAC,MAAM,yBAAyB,KAAK,CAAC,WAAW,mBAAmB,EAAE,CAAC;IAClI,CAAC;IACD,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IAClD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,sCAAsC,EAAE,CAAC;IACjG,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,kBAAkB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;AAC1F,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,CAAC,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/E,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAClD,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,41 @@
1
+ import type { PendingVerification } from '../../queries/verification/verification.js';
2
+ import type { StrategyResult } from '../deterministic-gate/verification-deterministic.js';
3
+ import type { VerificationDeps } from '../engine/verification-deps.js';
4
+ /** The failing tests a delivery's last verdict charged, to be re-checked at its next commit. */
5
+ export interface ReworkRecheckRecord {
6
+ deliveryId: string;
7
+ /** The commit they failed on. */
8
+ failedAtSha: string;
9
+ tests: string[];
10
+ files: string[];
11
+ recordedAt: string;
12
+ }
13
+ /** Where re-check records are kept. Production: one JSON file per delivery under the state dir. */
14
+ export interface ReworkRecheckStore {
15
+ read: (deliveryId: string) => ReworkRecheckRecord | null;
16
+ write: (record: ReworkRecheckRecord) => void;
17
+ clear: (deliveryId: string) => void;
18
+ }
19
+ /** Validate a record read back from disk; null when it is not one. Pure. */
20
+ export declare function parseReworkRecheckRecord(value: unknown): ReworkRecheckRecord | null;
21
+ /** The file-backed store (module doc). Best-effort: a write or clear that fails is logged, never thrown. */
22
+ export declare const fileReworkRecheckStore: ReworkRecheckStore;
23
+ /**
24
+ * Keep the delivery's re-check record in step with the verdict just rendered:
25
+ * a FAILED verdict at a known commit whose charged tests the declared re-run
26
+ * command can name is recorded; a pass, or a failure it cannot name (a lint
27
+ * red, a large set, no declaration), removes the record. A run that rendered
28
+ * no verdict changes nothing. The log is read only when a re-run command is declared.
29
+ */
30
+ export declare function noteReworkRecheck(v: PendingVerification, result: StrategyResult, readLogText: () => string, store: ReworkRecheckStore, now?: Date): void;
31
+ /** The record a failed verdict leaves, or null when it charges nothing the re-run command can name. Pure. */
32
+ export declare function recheckRecordOf(v: PendingVerification, result: StrategyResult, logText: string, now: Date): ReworkRecheckRecord | null;
33
+ /** The deps the re-check reaches reality through. */
34
+ export type ReworkRecheckDeps = Pick<VerificationDeps, 'reworkRecheck' | 'resolveFocusHeadSha' | 'runFailingTestRerun' | 'fileFlakyTestFinding'>;
35
+ /**
36
+ * Re-check the delivery's previously failing tests at the focus HEAD (module
37
+ * doc): the still-red result -- the verdict for that commit, no whole gate run
38
+ * -- or null, when the whole gate must run.
39
+ */
40
+ export declare function recheckPreviouslyFailing(v: PendingVerification, deps: ReworkRecheckDeps): Promise<StrategyResult | null>;
41
+ //# sourceMappingURL=rework-recheck.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rework-recheck.d.ts","sourceRoot":"","sources":["../../../src/verify/failing-test-rerun/rework-recheck.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qDAAqD,CAAC;AAC1F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAOvE,gGAAgG;AAChG,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,mGAAmG;AACnG,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,mBAAmB,GAAG,IAAI,CAAC;IACzD,KAAK,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC7C,KAAK,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAMD,4EAA4E;AAC5E,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,GAAG,IAAI,CAMnF;AAED,4GAA4G;AAC5G,eAAO,MAAM,sBAAsB,EAAE,kBA0BpC,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,mBAAmB,EACtB,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,MAAM,MAAM,EACzB,KAAK,EAAE,kBAAkB,EACzB,GAAG,GAAE,IAAiB,GACrB,IAAI,CAMN;AAED,6GAA6G;AAC7G,wBAAgB,eAAe,CAAC,CAAC,EAAE,mBAAmB,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,mBAAmB,GAAG,IAAI,CAgBtI;AAED,qDAAqD;AACrD,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,gBAAgB,EAAE,eAAe,GAAG,qBAAqB,GAAG,qBAAqB,GAAG,sBAAsB,CAAC,CAAC;AAEjJ;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,CAAC,EAAE,mBAAmB,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAsB9H"}
@@ -0,0 +1,169 @@
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/failing-test-rerun/rework-recheck.test.ts
3
+ // PINNED-BY-END
4
+ /**
5
+ * After a rework commit, re-check the tests that failed first: a still-red
6
+ * rework goes back to coding without a whole-gate run.
7
+ *
8
+ * A delivery charged with N failing tests comes back with a commit that claims
9
+ * to fix them. The cheapest question that can refute the claim is "do those
10
+ * tests still fail?" -- and the whole gate is the most expensive way to ask it.
11
+ * So when the delivery's last verdict charged a small set of failing tests the
12
+ * product's declared re-run command can name (rerun-command.ts planRerun), the
13
+ * set is RECORDED against the delivery (noteReworkRecheck), and the next
14
+ * dispatch at a NEW commit re-runs those files first (recheckPreviouslyFailing):
15
+ * - any attempt passes => nothing is decided here; the whole gate runs, as
16
+ * before (the re-check refutes, it never vouches). A failed attempt followed
17
+ * by a pass is a flake and is filed like one.
18
+ * - every attempt (1 + RERUN_RETRY_ATTEMPTS) fails => that result IS the
19
+ * verdict for the commit: failed, attributed and routed to rework like any
20
+ * gate red, with no whole-gate run.
21
+ * - a run that renders no verdict about the commit (no checkout, a timeout,
22
+ * no rerunCommand declared there) => the whole gate runs.
23
+ *
24
+ * The record is keyed by delivery and kept under the daemon state dir, beside
25
+ * the per-commit verdict records: it outlives the gate_state clears a rework
26
+ * cycle makes, and a pass removes it. A record whose commit is the HEAD being
27
+ * judged asks nothing (that commit already has its verdict).
28
+ *
29
+ * @module verify/failing-test-rerun/rework-recheck
30
+ */
31
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
32
+ import { resolve } from 'node:path';
33
+ import { resolveGlobalStateDir } from '../../daemon-state/daemon-process.js';
34
+ import { logRoots, planRerun, RERUN_RETRY_ATTEMPTS, testFilesOf } from './rerun-command.js';
35
+ import { rerunUntilPass } from './failing-test-retry.js';
36
+ import { createLogger } from '../../log/daemon-log.js';
37
+ const log = createLogger({ module: 'verification' });
38
+ const recheckDir = () => resolve(resolveGlobalStateDir(), 'verification-rework-recheck');
39
+ const recheckPath = (deliveryId) => resolve(recheckDir(), `${deliveryId.replace(/[^a-zA-Z0-9._-]/g, '_')}.json`);
40
+ const isStringArray = (value) => Array.isArray(value) && value.every((item) => typeof item === 'string');
41
+ /** Validate a record read back from disk; null when it is not one. Pure. */
42
+ export function parseReworkRecheckRecord(value) {
43
+ if (value === null || typeof value !== 'object')
44
+ return null;
45
+ const { deliveryId, failedAtSha, tests, files, recordedAt } = value;
46
+ if (typeof deliveryId !== 'string' || typeof failedAtSha !== 'string' || failedAtSha === '' || typeof recordedAt !== 'string')
47
+ return null;
48
+ if (!isStringArray(tests) || !isStringArray(files) || files.length === 0)
49
+ return null;
50
+ return { deliveryId, failedAtSha, tests: [...tests], files: [...files], recordedAt };
51
+ }
52
+ /** The file-backed store (module doc). Best-effort: a write or clear that fails is logged, never thrown. */
53
+ export const fileReworkRecheckStore = {
54
+ read: (deliveryId) => {
55
+ const path = recheckPath(deliveryId);
56
+ if (!existsSync(path))
57
+ return null;
58
+ try {
59
+ const record = parseReworkRecheckRecord(JSON.parse(readFileSync(path, 'utf8')));
60
+ return record?.deliveryId === deliveryId ? record : null;
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ },
66
+ write: (record) => {
67
+ try {
68
+ mkdirSync(recheckDir(), { recursive: true });
69
+ writeFileSync(recheckPath(record.deliveryId), JSON.stringify(record, null, 2), { mode: 0o600 });
70
+ }
71
+ catch (err) {
72
+ log.warn(`failed to write the rework re-check record for delivery ${record.deliveryId}:`, err.message);
73
+ }
74
+ },
75
+ clear: (deliveryId) => {
76
+ try {
77
+ rmSync(recheckPath(deliveryId), { force: true });
78
+ }
79
+ catch (err) {
80
+ log.warn(`failed to clear the rework re-check record for delivery ${deliveryId}:`, err.message);
81
+ }
82
+ },
83
+ };
84
+ /**
85
+ * Keep the delivery's re-check record in step with the verdict just rendered:
86
+ * a FAILED verdict at a known commit whose charged tests the declared re-run
87
+ * command can name is recorded; a pass, or a failure it cannot name (a lint
88
+ * red, a large set, no declaration), removes the record. A run that rendered
89
+ * no verdict changes nothing. The log is read only when a re-run command is declared.
90
+ */
91
+ export function noteReworkRecheck(v, result, readLogText, store, now = new Date()) {
92
+ if (result.outcome === 'passed')
93
+ return store.clear(v.deliveryId);
94
+ if (result.outcome !== 'failed')
95
+ return;
96
+ const record = result.appliedConfig?.rerun ? recheckRecordOf(v, result, readLogText(), now) : null;
97
+ if (record)
98
+ store.write(record);
99
+ else
100
+ store.clear(v.deliveryId);
101
+ }
102
+ /** The record a failed verdict leaves, or null when it charges nothing the re-run command can name. Pure. */
103
+ export function recheckRecordOf(v, result, logText, now) {
104
+ const sha = result.verifiedSha;
105
+ const rerun = result.appliedConfig?.rerun;
106
+ if (!sha || !rerun || result.timedOut)
107
+ return null;
108
+ // Charged tests: attribution's NEW failures when it ran, else every failing test the run named.
109
+ const charged = result.newFailures;
110
+ if (charged === undefined) {
111
+ const plan = planRerun(logText, rerun);
112
+ return plan.kind === 'rerun'
113
+ ? { deliveryId: v.deliveryId, failedAtSha: sha, tests: plan.ids, files: plan.files, recordedAt: now.toISOString() }
114
+ : null;
115
+ }
116
+ if (charged.length === 0 || charged.length > rerun.maxFailures)
117
+ return null;
118
+ const files = testFilesOf(charged, logRoots(logText));
119
+ if (files === null)
120
+ return null;
121
+ return { deliveryId: v.deliveryId, failedAtSha: sha, tests: [...charged].sort(), files, recordedAt: now.toISOString() };
122
+ }
123
+ /**
124
+ * Re-check the delivery's previously failing tests at the focus HEAD (module
125
+ * doc): the still-red result -- the verdict for that commit, no whole gate run
126
+ * -- or null, when the whole gate must run.
127
+ */
128
+ export async function recheckPreviouslyFailing(v, deps) {
129
+ const record = deps.reworkRecheck.read(v.deliveryId);
130
+ if (!record || !v.focusId || !v.defaultOnSource)
131
+ return null;
132
+ const head = deps.resolveFocusHeadSha(v.focusId);
133
+ if (!head || head === record.failedAtSha)
134
+ return null;
135
+ const reds = [];
136
+ const attempts = 1 + RERUN_RETRY_ATTEMPTS;
137
+ const passedOn = await rerunUntilPass(deps.runFailingTestRerun, v, record.files, head, attempts, (run) => reds.push(run));
138
+ if (passedOn === null)
139
+ return null;
140
+ if (passedOn > 0) {
141
+ if (reds.length > 0)
142
+ await fileRecheckFlake(v, record, head, passedOn, deps);
143
+ log.info(`[verification] delivery ${v.deliveryId}: the ${record.files.length} previously failing test file(s) pass at ` +
144
+ `${head.slice(0, 8)} -- the whole gate runs`);
145
+ return null;
146
+ }
147
+ const red = reds.at(-1);
148
+ if (!red)
149
+ return null;
150
+ return {
151
+ ...red,
152
+ rationale: `the ${record.tests.length} previously failing test(s) (${record.files.join(', ')}) still fail at ${head.slice(0, 8)} on the ` +
153
+ `declared re-run command (${attempts} run(s)) -- returned to rework without a whole-gate run`,
154
+ };
155
+ }
156
+ /** A re-check that failed and then passed is a flake: filed best-effort (the whole gate runs either way). */
157
+ async function fileRecheckFlake(v, record, head, passedOnAttempt, deps) {
158
+ const finding = {
159
+ productId: v.productId, focusId: v.focusId, deliveryId: v.deliveryId, deliveryName: v.deliveryName,
160
+ sha: head, tests: record.tests, files: record.files, passedOnAttempt, logPath: null, origin: 'rework_recheck',
161
+ };
162
+ try {
163
+ await deps.fileFlakyTestFinding(v, finding);
164
+ }
165
+ catch (err) {
166
+ log.warn(`[verification] delivery ${v.deliveryId}: the flaky-test finding from the rework re-check could not be filed: ${err.message}`);
167
+ }
168
+ }
169
+ //# sourceMappingURL=rework-recheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rework-recheck.js","sourceRoot":"","sources":["../../../src/verify/failing-test-rerun/rework-recheck.ts"],"names":[],"mappings":"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"}