@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.
- package/build-info.json +4 -3
- package/dist/backends/claude/one-shot-model.d.ts +11 -0
- package/dist/backends/claude/one-shot-model.d.ts.map +1 -1
- package/dist/backends/claude/one-shot-model.js +7 -4
- package/dist/backends/claude/one-shot-model.js.map +1 -1
- package/dist/backends/claude/one-shot-session.d.ts +15 -0
- package/dist/backends/claude/one-shot-session.d.ts.map +1 -0
- package/dist/backends/claude/one-shot-session.js +120 -0
- package/dist/backends/claude/one-shot-session.js.map +1 -0
- package/dist/ci/candidate-ref.d.ts +28 -0
- package/dist/ci/candidate-ref.d.ts.map +1 -0
- package/dist/ci/candidate-ref.js +81 -0
- package/dist/ci/candidate-ref.js.map +1 -0
- package/dist/ci/ci-sensor-loop.d.ts.map +1 -1
- package/dist/ci/ci-sensor-loop.js +2 -1
- package/dist/ci/ci-sensor-loop.js.map +1 -1
- package/dist/ci/ci-status-git.d.ts +9 -0
- package/dist/ci/ci-status-git.d.ts.map +1 -1
- package/dist/ci/ci-status-git.js +16 -2
- package/dist/ci/ci-status-git.js.map +1 -1
- package/dist/ci/ci-status.d.ts.map +1 -1
- package/dist/ci/ci-status.js +5 -1
- package/dist/ci/ci-status.js.map +1 -1
- package/dist/completion/state-cascade/state-cascade.d.ts +14 -1
- package/dist/completion/state-cascade/state-cascade.d.ts.map +1 -1
- package/dist/completion/state-cascade/state-cascade.js +26 -1
- package/dist/completion/state-cascade/state-cascade.js.map +1 -1
- package/dist/completion/team-exit/merge-phase.d.ts +11 -10
- package/dist/completion/team-exit/merge-phase.d.ts.map +1 -1
- package/dist/completion/team-exit/merge-phase.js +10 -10
- package/dist/completion/team-exit/merge-phase.js.map +1 -1
- package/dist/completion/team-teardown/team-lifecycle.d.ts +4 -3
- package/dist/completion/team-teardown/team-lifecycle.d.ts.map +1 -1
- package/dist/completion/team-teardown/team-lifecycle.js +8 -8
- package/dist/completion/team-teardown/team-lifecycle.js.map +1 -1
- package/dist/composition/attribution-base-git.d.ts.map +1 -1
- package/dist/composition/attribution-base-git.js +3 -1
- package/dist/composition/attribution-base-git.js.map +1 -1
- package/dist/composition/execution-git-context.d.ts.map +1 -1
- package/dist/composition/execution-git-context.js +2 -0
- package/dist/composition/execution-git-context.js.map +1 -1
- package/dist/composition/execution-merge-context.d.ts +11 -3
- package/dist/composition/execution-merge-context.d.ts.map +1 -1
- package/dist/composition/execution-merge-context.js +60 -12
- package/dist/composition/execution-merge-context.js.map +1 -1
- package/dist/composition/process-close-context.js +3 -3
- package/dist/composition/process-close-context.js.map +1 -1
- package/dist/composition/state-cascade-context.d.ts.map +1 -1
- package/dist/composition/state-cascade-context.js +10 -2
- package/dist/composition/state-cascade-context.js.map +1 -1
- package/dist/composition/team-lifecycle-context.js +3 -3
- package/dist/composition/team-lifecycle-context.js.map +1 -1
- package/dist/composition/verification-context.d.ts.map +1 -1
- package/dist/composition/verification-context.js +25 -27
- package/dist/composition/verification-context.js.map +1 -1
- package/dist/delivery/delivery-merge-ops.d.ts +2 -0
- package/dist/delivery/delivery-merge-ops.d.ts.map +1 -1
- package/dist/delivery/delivery-merge-ops.js +2 -2
- package/dist/delivery/delivery-merge-ops.js.map +1 -1
- package/dist/delivery/delivery-reconciler.d.ts +9 -0
- package/dist/delivery/delivery-reconciler.d.ts.map +1 -1
- package/dist/delivery/delivery-reconciler.js +42 -31
- package/dist/delivery/delivery-reconciler.js.map +1 -1
- package/dist/focus/focus-spawn-decision.d.ts.map +1 -1
- package/dist/focus/focus-spawn-decision.js +1 -0
- package/dist/focus/focus-spawn-decision.js.map +1 -1
- package/dist/git/index.d.ts.map +1 -1
- package/dist/git/index.js +2 -1
- package/dist/git/index.js.map +1 -1
- package/dist/git/merge.d.ts +12 -2
- package/dist/git/merge.d.ts.map +1 -1
- package/dist/git/merge.js +32 -33
- package/dist/git/merge.js.map +1 -1
- package/dist/merge/approval-gate-state.d.ts.map +1 -1
- package/dist/merge/approval-gate-state.js +8 -7
- package/dist/merge/approval-gate-state.js.map +1 -1
- package/dist/merge/candidate-rework.d.ts +44 -0
- package/dist/merge/candidate-rework.d.ts.map +1 -0
- package/dist/merge/candidate-rework.js +152 -0
- package/dist/merge/candidate-rework.js.map +1 -0
- package/dist/merge/completion-hold-record.d.ts +20 -0
- package/dist/merge/completion-hold-record.d.ts.map +1 -0
- package/dist/merge/completion-hold-record.js +130 -0
- package/dist/merge/completion-hold-record.js.map +1 -0
- package/dist/merge/completion-merge-driver.d.ts +96 -0
- package/dist/merge/completion-merge-driver.d.ts.map +1 -0
- package/dist/merge/completion-merge-driver.js +94 -0
- package/dist/merge/completion-merge-driver.js.map +1 -0
- package/dist/merge/completion-merge-gate.d.ts +71 -0
- package/dist/merge/completion-merge-gate.d.ts.map +1 -0
- package/dist/merge/completion-merge-gate.js +185 -0
- package/dist/merge/completion-merge-gate.js.map +1 -0
- package/dist/merge/completion-merge-pending.d.ts +30 -0
- package/dist/merge/completion-merge-pending.d.ts.map +1 -0
- package/dist/merge/completion-merge-pending.js +75 -0
- package/dist/merge/completion-merge-pending.js.map +1 -0
- package/dist/merge/focus-merge.d.ts +21 -6
- package/dist/merge/focus-merge.d.ts.map +1 -1
- package/dist/merge/focus-merge.js +65 -45
- package/dist/merge/focus-merge.js.map +1 -1
- package/dist/merge/focus-worktree-removal.d.ts +6 -6
- package/dist/merge/focus-worktree-removal.d.ts.map +1 -1
- package/dist/merge/focus-worktree-removal.js +10 -6
- package/dist/merge/focus-worktree-removal.js.map +1 -1
- package/dist/merge/merge-back-conflict.d.ts.map +1 -1
- package/dist/merge/merge-back-conflict.js +2 -0
- package/dist/merge/merge-back-conflict.js.map +1 -1
- package/dist/merge/merge-back-followup.d.ts.map +1 -1
- package/dist/merge/merge-back-followup.js +7 -0
- package/dist/merge/merge-back-followup.js.map +1 -1
- package/dist/merge/merge-back-loop.d.ts +36 -14
- package/dist/merge/merge-back-loop.d.ts.map +1 -1
- package/dist/merge/merge-back-loop.js +109 -66
- package/dist/merge/merge-back-loop.js.map +1 -1
- package/dist/merge/merge-gate-coverage.d.ts +9 -61
- package/dist/merge/merge-gate-coverage.d.ts.map +1 -1
- package/dist/merge/merge-gate-coverage.js +33 -167
- package/dist/merge/merge-gate-coverage.js.map +1 -1
- package/dist/nexus/escalation-bridge.d.ts.map +1 -1
- package/dist/nexus/escalation-bridge.js +2 -1
- package/dist/nexus/escalation-bridge.js.map +1 -1
- package/dist/nexus/file-escalation.d.ts.map +1 -1
- package/dist/nexus/file-escalation.js +5 -1
- package/dist/nexus/file-escalation.js.map +1 -1
- package/dist/queries/api-client/shared.js +1 -1
- package/dist/queries/deliveries/deliveries.d.ts.map +1 -1
- package/dist/queries/deliveries/deliveries.js +1 -0
- package/dist/queries/deliveries/deliveries.js.map +1 -1
- package/dist/queries/delivery-gate-state/delivery-gate-state.js +1 -1
- package/dist/queries/delivery-gate-state/verify-own-holds.d.ts +10 -3
- package/dist/queries/delivery-gate-state/verify-own-holds.d.ts.map +1 -1
- package/dist/queries/delivery-gate-state/verify-own-holds.js +2 -0
- package/dist/queries/delivery-gate-state/verify-own-holds.js.map +1 -1
- package/dist/queries/escalations/cause-key.d.ts.map +1 -1
- package/dist/queries/escalations/cause-key.js +1 -0
- package/dist/queries/escalations/cause-key.js.map +1 -1
- package/dist/queries/escalations/escalations.d.ts.map +1 -1
- package/dist/queries/escalations/escalations.js +2 -1
- package/dist/queries/escalations/escalations.js.map +1 -1
- package/dist/queries/focuses/focuses.d.ts +12 -2
- package/dist/queries/focuses/focuses.d.ts.map +1 -1
- package/dist/queries/focuses/focuses.js +24 -3
- package/dist/queries/focuses/focuses.js.map +1 -1
- package/dist/queries/verification/verification.js +1 -1
- package/dist/spawn/review-hold-park.d.ts.map +1 -1
- package/dist/spawn/review-hold-park.js +1 -0
- package/dist/spawn/review-hold-park.js.map +1 -1
- package/dist/supervision/agent-resolvable-retirement.d.ts +1 -1
- package/dist/supervision/agent-resolvable-retirement.d.ts.map +1 -1
- package/dist/supervision/agent-resolvable-retirement.js +2 -1
- package/dist/supervision/agent-resolvable-retirement.js.map +1 -1
- package/dist/supervision/escalation-filing.js +1 -1
- package/dist/supervision/incident-correlation.d.ts +3 -0
- package/dist/supervision/incident-correlation.d.ts.map +1 -1
- package/dist/supervision/incident-correlation.js +4 -0
- package/dist/supervision/incident-correlation.js.map +1 -1
- package/dist/supervision/null-role-spawn.d.ts.map +1 -1
- package/dist/supervision/null-role-spawn.js +1 -0
- package/dist/supervision/null-role-spawn.js.map +1 -1
- package/dist/supervision/spawn-cooldown.js +1 -1
- package/dist/supervision/stop-rows-work-holds.d.ts +4 -4
- package/dist/supervision/stop-rows-work-holds.d.ts.map +1 -1
- package/dist/supervision/stop-rows-work-holds.js +17 -15
- package/dist/supervision/stop-rows-work-holds.js.map +1 -1
- package/dist/testing/fixtures/api-client.js +1 -1
- package/dist/testing/fixtures/index.js +1 -1
- package/dist/testing/fixtures/judge-session.d.ts +14 -0
- package/dist/testing/fixtures/judge-session.d.ts.map +1 -0
- package/dist/testing/fixtures/judge-session.js +10 -0
- package/dist/testing/fixtures/judge-session.js.map +1 -0
- package/dist/testing/fixtures/verification-deps.d.ts +2 -1
- package/dist/testing/fixtures/verification-deps.d.ts.map +1 -1
- package/dist/testing/fixtures/verification-deps.js +4 -1
- package/dist/testing/fixtures/verification-deps.js.map +1 -1
- package/dist/testing/fixtures/vitest-failure-log.d.ts +10 -0
- package/dist/testing/fixtures/vitest-failure-log.d.ts.map +1 -0
- package/dist/testing/fixtures/vitest-failure-log.js +27 -0
- package/dist/testing/fixtures/vitest-failure-log.js.map +1 -0
- package/dist/types/index.js +1 -1
- package/dist/verify/attribution/verification-attribution.d.ts.map +1 -1
- package/dist/verify/attribution/verification-attribution.js +2 -1
- package/dist/verify/attribution/verification-attribution.js.map +1 -1
- package/dist/verify/attribution/verification-baseline-files.d.ts +29 -0
- package/dist/verify/attribution/verification-baseline-files.d.ts.map +1 -0
- package/dist/verify/attribution/verification-baseline-files.js +125 -0
- package/dist/verify/attribution/verification-baseline-files.js.map +1 -0
- package/dist/verify/attribution/verification-baseline-run.d.ts.map +1 -1
- package/dist/verify/attribution/verification-baseline-run.js +1 -0
- package/dist/verify/attribution/verification-baseline-run.js.map +1 -1
- package/dist/verify/attribution/verification-baseline.d.ts +15 -0
- package/dist/verify/attribution/verification-baseline.d.ts.map +1 -1
- package/dist/verify/attribution/verification-baseline.js +28 -3
- package/dist/verify/attribution/verification-baseline.js.map +1 -1
- package/dist/verify/deterministic-gate/verification-checkout.d.ts.map +1 -1
- package/dist/verify/deterministic-gate/verification-checkout.js +1 -0
- package/dist/verify/deterministic-gate/verification-checkout.js.map +1 -1
- package/dist/verify/deterministic-gate/verification-deterministic.d.ts +30 -0
- package/dist/verify/deterministic-gate/verification-deterministic.d.ts.map +1 -1
- package/dist/verify/deterministic-gate/verification-deterministic.js +2 -0
- package/dist/verify/deterministic-gate/verification-deterministic.js.map +1 -1
- package/dist/verify/deterministic-gate/verification-gate-stages.d.ts.map +1 -1
- package/dist/verify/deterministic-gate/verification-gate-stages.js +1 -0
- package/dist/verify/deterministic-gate/verification-gate-stages.js.map +1 -1
- package/dist/verify/effect-reconciliation/judge-invoker.d.ts +14 -4
- package/dist/verify/effect-reconciliation/judge-invoker.d.ts.map +1 -1
- package/dist/verify/effect-reconciliation/judge-invoker.js +30 -12
- package/dist/verify/effect-reconciliation/judge-invoker.js.map +1 -1
- package/dist/verify/effect-reconciliation/verify-reconciliation.d.ts +18 -5
- package/dist/verify/effect-reconciliation/verify-reconciliation.d.ts.map +1 -1
- package/dist/verify/effect-reconciliation/verify-reconciliation.js +75 -18
- package/dist/verify/effect-reconciliation/verify-reconciliation.js.map +1 -1
- package/dist/verify/effect-reconciliation/verify-restatement.js +1 -1
- package/dist/verify/engine/verification-commit-sharing.d.ts.map +1 -1
- package/dist/verify/engine/verification-commit-sharing.js +14 -2
- package/dist/verify/engine/verification-commit-sharing.js.map +1 -1
- package/dist/verify/engine/verification-deps.d.ts +23 -0
- package/dist/verify/engine/verification-deps.d.ts.map +1 -1
- package/dist/verify/engine/verification-deps.js +1 -1
- package/dist/verify/engine/verification-engine.js +1 -1
- package/dist/verify/failing-test-rerun/failing-test-rerunner.d.ts +34 -0
- package/dist/verify/failing-test-rerun/failing-test-rerunner.d.ts.map +1 -0
- package/dist/verify/failing-test-rerun/failing-test-rerunner.js +34 -0
- package/dist/verify/failing-test-rerun/failing-test-rerunner.js.map +1 -0
- package/dist/verify/failing-test-rerun/failing-test-retry.d.ts +80 -0
- package/dist/verify/failing-test-rerun/failing-test-retry.d.ts.map +1 -0
- package/dist/verify/failing-test-rerun/failing-test-retry.js +102 -0
- package/dist/verify/failing-test-rerun/failing-test-retry.js.map +1 -0
- package/dist/verify/failing-test-rerun/flaky-test-finding.d.ts +11 -0
- package/dist/verify/failing-test-rerun/flaky-test-finding.d.ts.map +1 -0
- package/dist/verify/failing-test-rerun/flaky-test-finding.js +63 -0
- package/dist/verify/failing-test-rerun/flaky-test-finding.js.map +1 -0
- package/dist/verify/failing-test-rerun/rerun-command.d.ts +45 -0
- package/dist/verify/failing-test-rerun/rerun-command.d.ts.map +1 -0
- package/dist/verify/failing-test-rerun/rerun-command.js +132 -0
- package/dist/verify/failing-test-rerun/rerun-command.js.map +1 -0
- package/dist/verify/failing-test-rerun/rework-recheck.d.ts +41 -0
- package/dist/verify/failing-test-rerun/rework-recheck.d.ts.map +1 -0
- package/dist/verify/failing-test-rerun/rework-recheck.js +169 -0
- package/dist/verify/failing-test-rerun/rework-recheck.js.map +1 -0
- package/dist/verify/gate-config/verification-config.d.ts.map +1 -1
- package/dist/verify/gate-config/verification-config.js +16 -2
- package/dist/verify/gate-config/verification-config.js.map +1 -1
- package/dist/verify/gate-config/verification-manifest.d.ts +26 -1
- package/dist/verify/gate-config/verification-manifest.d.ts.map +1 -1
- package/dist/verify/gate-config/verification-manifest.js +31 -1
- package/dist/verify/gate-config/verification-manifest.js.map +1 -1
- package/dist/verify/inspection-strategies/verification-strategies.js +1 -1
- package/dist/verify/inspection-strategies/verification-strategies.js.map +1 -1
- package/dist/verify/outcome-routing/incurable-model-hold.d.ts +9 -3
- package/dist/verify/outcome-routing/incurable-model-hold.d.ts.map +1 -1
- package/dist/verify/outcome-routing/incurable-model-hold.js +18 -5
- package/dist/verify/outcome-routing/incurable-model-hold.js.map +1 -1
- package/dist/verify/outcome-routing/repeating-model-hold.d.ts +30 -0
- package/dist/verify/outcome-routing/repeating-model-hold.d.ts.map +1 -0
- package/dist/verify/outcome-routing/repeating-model-hold.js +135 -0
- package/dist/verify/outcome-routing/repeating-model-hold.js.map +1 -0
- package/dist/verify/outcome-routing/verification-history-reset.d.ts.map +1 -1
- package/dist/verify/outcome-routing/verification-history-reset.js +1 -0
- package/dist/verify/outcome-routing/verification-history-reset.js.map +1 -1
- package/dist/verify/outcome-routing/verification-model-unavailable-hold.d.ts +10 -4
- package/dist/verify/outcome-routing/verification-model-unavailable-hold.d.ts.map +1 -1
- package/dist/verify/outcome-routing/verification-model-unavailable-hold.js +24 -6
- package/dist/verify/outcome-routing/verification-model-unavailable-hold.js.map +1 -1
- package/dist/verify/run-selection/default-on-guards.js +1 -1
- package/dist/verify/run-selection/pre-review-gate-dispatch.d.ts +4 -3
- package/dist/verify/run-selection/pre-review-gate-dispatch.d.ts.map +1 -1
- package/dist/verify/run-selection/pre-review-gate-dispatch.js +23 -7
- package/dist/verify/run-selection/pre-review-gate-dispatch.js.map +1 -1
- package/dist/verify/run-selection/verification-default-on.js +1 -1
- package/dist/verify/run-selection/verification-default-on.js.map +1 -1
- package/dist/worktree/provision.d.ts.map +1 -1
- package/dist/worktree/provision.js +1 -0
- package/dist/worktree/provision.js.map +1 -1
- package/dist/worktree/safety.d.ts.map +1 -1
- package/dist/worktree/safety.js +1 -0
- package/dist/worktree/safety.js.map +1 -1
- 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":"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"}
|
|
@@ -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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"}
|