@codyswann/lisa 4.42.6 → 4.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/all/copy-overwrite/scripts/check-orphaned-branches.mjs +7 -0
- package/all/copy-overwrite/scripts/lib/process-tree-runner.mjs +12 -2
- package/all/copy-overwrite/scripts/lisa-cross-worktree-guard.mjs +4 -0
- package/all/copy-overwrite/scripts/lisa-environment-prepare.mjs +2 -0
- package/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.mjs +31 -3
- package/all/copy-overwrite/scripts/lisa-run-gates.mjs +4 -0
- package/all/copy-overwrite/scripts/lisa-work-item.mjs +13 -0
- package/dist/cli/doctor-readiness-delivery.d.ts.map +1 -1
- package/dist/cli/doctor-readiness-delivery.js +13 -6
- package/dist/cli/doctor-readiness-delivery.js.map +1 -1
- package/dist/cli/doctor-readiness-deploy-outcome.d.ts +9 -0
- package/dist/cli/doctor-readiness-deploy-outcome.d.ts.map +1 -0
- package/dist/cli/doctor-readiness-deploy-outcome.js +87 -0
- package/dist/cli/doctor-readiness-deploy-outcome.js.map +1 -0
- package/dist/cli/doctor-readiness-workflows.d.ts +8 -0
- package/dist/cli/doctor-readiness-workflows.d.ts.map +1 -1
- package/dist/cli/doctor-readiness-workflows.js +1 -0
- package/dist/cli/doctor-readiness-workflows.js.map +1 -1
- package/dist/configs/vitest/base.d.ts +1 -1
- package/dist/configs/vitest/base.d.ts.map +1 -1
- package/dist/configs/vitest/base.js +1 -0
- package/dist/configs/vitest/base.js.map +1 -1
- package/dist/configs/vitest/cdk.d.ts +2 -2
- package/dist/configs/vitest/cdk.d.ts.map +1 -1
- package/dist/configs/vitest/cdk.js +5 -2
- package/dist/configs/vitest/cdk.js.map +1 -1
- package/dist/configs/vitest/coverage-reports-directory.d.ts +47 -0
- package/dist/configs/vitest/coverage-reports-directory.d.ts.map +1 -0
- package/dist/configs/vitest/coverage-reports-directory.js +131 -0
- package/dist/configs/vitest/coverage-reports-directory.js.map +1 -0
- package/dist/configs/vitest/harper-fabric.d.ts +2 -2
- package/dist/configs/vitest/harper-fabric.d.ts.map +1 -1
- package/dist/configs/vitest/harper-fabric.js +5 -2
- package/dist/configs/vitest/harper-fabric.js.map +1 -1
- package/dist/configs/vitest/nestjs.d.ts +2 -2
- package/dist/configs/vitest/nestjs.d.ts.map +1 -1
- package/dist/configs/vitest/nestjs.js +5 -2
- package/dist/configs/vitest/nestjs.js.map +1 -1
- package/dist/configs/vitest/phaser.d.ts +2 -2
- package/dist/configs/vitest/phaser.d.ts.map +1 -1
- package/dist/configs/vitest/phaser.js +5 -2
- package/dist/configs/vitest/phaser.js.map +1 -1
- package/dist/configs/vitest/typescript.d.ts +2 -2
- package/dist/configs/vitest/typescript.d.ts.map +1 -1
- package/dist/configs/vitest/typescript.js +5 -2
- package/dist/configs/vitest/typescript.js.map +1 -1
- package/dist/core/deploy-release-dependence.d.ts +72 -0
- package/dist/core/deploy-release-dependence.d.ts.map +1 -0
- package/dist/core/deploy-release-dependence.js +236 -0
- package/dist/core/deploy-release-dependence.js.map +1 -0
- package/dist/core/github-actions-condition.d.ts +78 -0
- package/dist/core/github-actions-condition.d.ts.map +1 -0
- package/dist/core/github-actions-condition.js +307 -0
- package/dist/core/github-actions-condition.js.map +1 -0
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +24 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +45 -24
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/migrations/deploy-outcome-guard-edit.d.ts +11 -0
- package/dist/migrations/deploy-outcome-guard-edit.d.ts.map +1 -0
- package/dist/migrations/deploy-outcome-guard-edit.js +184 -0
- package/dist/migrations/deploy-outcome-guard-edit.js.map +1 -0
- package/dist/migrations/deploy-outcome-guard-yaml.d.ts +91 -0
- package/dist/migrations/deploy-outcome-guard-yaml.d.ts.map +1 -0
- package/dist/migrations/deploy-outcome-guard-yaml.js +180 -0
- package/dist/migrations/deploy-outcome-guard-yaml.js.map +1 -0
- package/dist/migrations/ensure-deploy-outcome-guard.d.ts +41 -0
- package/dist/migrations/ensure-deploy-outcome-guard.d.ts.map +1 -0
- package/dist/migrations/ensure-deploy-outcome-guard.js +238 -0
- package/dist/migrations/ensure-deploy-outcome-guard.js.map +1 -0
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +3 -0
- package/dist/migrations/index.js.map +1 -1
- package/expo/copy-overwrite/scripts/bdd/baseline.mjs +26 -7
- package/package.json +5 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +11 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/bootstrap-store.mjs +3 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/preflight-secrets.mjs +3 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/providers.mjs +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-workstation/scripts/workstation.mjs +3 -0
- package/plugins/lisa/hooks/threshold-ratchet.mjs +6 -0
- package/plugins/lisa/hooks/worktree-binding-guard.mjs +31 -3
- package/plugins/lisa/scripts/design-variable-ids.mjs +2 -0
- package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +11 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/bootstrap-store.mjs +3 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/preflight-secrets.mjs +3 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/providers.mjs +4 -0
- package/plugins/lisa/skills/lisa-setup-workstation/scripts/workstation.mjs +3 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/design-variable-ids.mjs +2 -0
- package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +11 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/bootstrap-store.mjs +3 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/preflight-secrets.mjs +3 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/providers.mjs +4 -0
- package/plugins/lisa-agy/skills/lisa-setup-workstation/scripts/workstation.mjs +3 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/threshold-ratchet.mjs +6 -0
- package/plugins/lisa-copilot/hooks/worktree-binding-guard.mjs +31 -3
- package/plugins/lisa-copilot/scripts/design-variable-ids.mjs +2 -0
- package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +11 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/bootstrap-store.mjs +3 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/preflight-secrets.mjs +3 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/providers.mjs +4 -0
- package/plugins/lisa-copilot/skills/lisa-setup-workstation/scripts/workstation.mjs +3 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/threshold-ratchet.mjs +6 -0
- package/plugins/lisa-cursor/hooks/worktree-binding-guard.mjs +31 -3
- package/plugins/lisa-cursor/scripts/design-variable-ids.mjs +2 -0
- package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +11 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/bootstrap-store.mjs +3 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/preflight-secrets.mjs +3 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/providers.mjs +4 -0
- package/plugins/lisa-cursor/skills/lisa-setup-workstation/scripts/workstation.mjs +3 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/scripts/ingest-git.mjs +2 -0
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/scripts/ingest-git.mjs +2 -0
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/scripts/ingest-git.mjs +2 -0
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/scripts/ingest-git.mjs +2 -0
- package/plugins/src/base/hooks/threshold-ratchet.mjs +6 -0
- package/plugins/src/base/hooks/worktree-binding-guard.mjs +31 -3
- package/plugins/src/base/scripts/design-variable-ids.mjs +2 -0
- package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +11 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/bootstrap-store.mjs +3 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/preflight-secrets.mjs +3 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/providers.mjs +4 -0
- package/plugins/src/base/skills/lisa-setup-workstation/scripts/workstation.mjs +3 -0
- package/plugins/src/wiki/scripts/ingest-git.mjs +2 -0
- package/rails/copy-overwrite/scripts/check-threshold-ratchet.mjs +6 -0
- package/scripts/check-merge-driver-registration.mjs +2 -0
- package/scripts/check-probe-absence-direction.mjs +611 -0
- package/scripts/check-test-case-executed.mjs +184 -0
- package/scripts/install-generated-artifact-merge-driver.mjs +7 -1
- package/scripts/lib/shell-guard-refusal-coverage.mjs +18 -19
- package/typescript/copy-overwrite/scripts/check-threshold-ratchet.mjs +6 -0
- package/typescript/copy-overwrite/scripts/lisa-mutation.mjs +4 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Proves that a NAMED test case actually EXECUTED, from a vitest JSON report.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this exists
|
|
6
|
+
*
|
|
7
|
+
* An assertion about a configuration is not an assertion about execution, and
|
|
8
|
+
* CodySwannGT/lisa#3935 is what that gap costs. A 100,000-entry temp-scan
|
|
9
|
+
* benchmark was moved off the pre-push gate on the stated grounds that CI still
|
|
10
|
+
* ran it. It did not: the case is `it.runIf(process.platform === "darwin")` and
|
|
11
|
+
* every CI runner is `ubuntu-latest`, so CI skipped it, and had always skipped
|
|
12
|
+
* it. The check written to guard the move asserted that the pull-request task
|
|
13
|
+
* did not EXCLUDE the file — true, and hollow, because a platform guard one
|
|
14
|
+
* layer down skipped the case anyway. Not excluding a file is not running it.
|
|
15
|
+
*
|
|
16
|
+
* A skipped case is a green case. Nothing in the pipeline reports it, because
|
|
17
|
+
* from the runner's point of view nothing went wrong. That is the whole failure
|
|
18
|
+
* mode: the surface reads as covered while running nothing.
|
|
19
|
+
*
|
|
20
|
+
* So this script asserts the only thing that settles the question — that the
|
|
21
|
+
* case appears in the report, that it PASSED rather than being skipped or
|
|
22
|
+
* todo-ed, and that it consumed real wall-clock time. A lane that runs it is
|
|
23
|
+
* then a lane that can PROVE it ran it, and a platform guard that silently
|
|
24
|
+
* removes the case turns a green job red with the reason on screen.
|
|
25
|
+
*
|
|
26
|
+
* ## Vitest's report vocabulary, as observed
|
|
27
|
+
*
|
|
28
|
+
* Measured on vitest 4.1.9 against a deliberate `it.runIf(false)` probe: a
|
|
29
|
+
* guarded-off case is present in `testResults[].assertionResults[]` with
|
|
30
|
+
* `status: "skipped"` and NO `duration` field, and is counted in
|
|
31
|
+
* `numPendingTests`. A case whose whole FILE was never collected — an
|
|
32
|
+
* `--exclude` glob, a moved file, a typo in the path — is absent from the
|
|
33
|
+
* report entirely. Both are execution failures and both are reported here,
|
|
34
|
+
* separately, because they have different fixes: one is a platform or flag
|
|
35
|
+
* guard, the other is a path or exclusion.
|
|
36
|
+
*
|
|
37
|
+
* `fullName` joins `ancestorTitles` to the title with a SPACE. `vitest list`
|
|
38
|
+
* prints the same case with ` > ` between them, and the two are easy to
|
|
39
|
+
* confuse — measured, both forms, on the same suite. A caller that passes the
|
|
40
|
+
* wrong form gets a case that is absent for a reason having nothing to do with
|
|
41
|
+
* execution, so an ABSENT failure PRINTS the names the report did contain
|
|
42
|
+
* rather than asserting the case was never collected. A gate whose diagnosis is
|
|
43
|
+
* confidently wrong costs more than one that says less.
|
|
44
|
+
*
|
|
45
|
+
* ## Usage
|
|
46
|
+
*
|
|
47
|
+
* node scripts/check-test-case-executed.mjs \
|
|
48
|
+
* --report <vitest --reporter=json --outputFile target> \
|
|
49
|
+
* --name "<full test name>" [--name "<another>"]
|
|
50
|
+
*
|
|
51
|
+
* Exit 0 only when EVERY named case is present, passed, and timed. Any other
|
|
52
|
+
* outcome exits 1 and names which case and which way it failed.
|
|
53
|
+
* @module scripts/check-test-case-executed
|
|
54
|
+
*/
|
|
55
|
+
import { readFileSync } from "node:fs";
|
|
56
|
+
|
|
57
|
+
import { invokedAsScript } from "./lib/invoked-as-script.mjs";
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Statuses vitest reports for a case that did not actually run.
|
|
61
|
+
*
|
|
62
|
+
* A Map rather than an object literal because the key is a status string read
|
|
63
|
+
* out of a JSON file. On an object, a report carrying `"constructor"` or
|
|
64
|
+
* `"toString"` resolves to something inherited from `Object.prototype` and the
|
|
65
|
+
* message below stringifies it as though it were advice — an absurd input, but
|
|
66
|
+
* this gate's whole job is to be right about a file it did not write.
|
|
67
|
+
*/
|
|
68
|
+
const NON_EXECUTED_STATUS_ADVICE = new Map([
|
|
69
|
+
[
|
|
70
|
+
"skipped",
|
|
71
|
+
"the case was SKIPPED — a platform guard, a `runIf` condition, or an `it.skip` removed it; a skipped case is a green case and proves nothing",
|
|
72
|
+
],
|
|
73
|
+
["todo", "the case is marked TODO — it has no body to execute"],
|
|
74
|
+
[
|
|
75
|
+
"failed",
|
|
76
|
+
"the case RAN and FAILED — read the failure above; this gate only reports that it was not a passing execution",
|
|
77
|
+
],
|
|
78
|
+
]);
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Parse `--report <path>` and one or more `--name <full test name>` flags.
|
|
82
|
+
* @param {readonly string[]} argv - Arguments after the script path
|
|
83
|
+
* @returns {{report: string, names: readonly string[]}} Validated invocation
|
|
84
|
+
*/
|
|
85
|
+
export function parseArguments(argv) {
|
|
86
|
+
const names = [];
|
|
87
|
+
let report;
|
|
88
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
89
|
+
const flag = argv[index];
|
|
90
|
+
const value = argv[index + 1];
|
|
91
|
+
if (flag !== "--report" && flag !== "--name")
|
|
92
|
+
throw new Error(`Unknown argument: ${String(flag)}`);
|
|
93
|
+
if (value === undefined || value.startsWith("--"))
|
|
94
|
+
throw new Error(`${flag} requires a value`);
|
|
95
|
+
if (flag === "--report") report = value;
|
|
96
|
+
else names.push(value);
|
|
97
|
+
index += 1;
|
|
98
|
+
}
|
|
99
|
+
if (report === undefined) throw new Error("--report is required");
|
|
100
|
+
if (names.length === 0) throw new Error("At least one --name is required");
|
|
101
|
+
return { report, names };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Read every case the report contains, keyed by its full name.
|
|
106
|
+
* @param {string} reportPath - Path to a vitest JSON report
|
|
107
|
+
* @returns {Map<string, {status: string, duration?: number}>} Reported cases
|
|
108
|
+
*/
|
|
109
|
+
export function reportedCases(reportPath) {
|
|
110
|
+
let parsed;
|
|
111
|
+
try {
|
|
112
|
+
parsed = JSON.parse(readFileSync(reportPath, "utf8"));
|
|
113
|
+
} catch (cause) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`Cannot read the vitest JSON report at ${reportPath}. A missing report means the run never produced one, which is itself proof the case did not execute. Cause: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
const files = Array.isArray(parsed?.testResults) ? parsed.testResults : [];
|
|
119
|
+
return new Map(
|
|
120
|
+
files.flatMap(file =>
|
|
121
|
+
(Array.isArray(file?.assertionResults) ? file.assertionResults : []).map(
|
|
122
|
+
assertion => [
|
|
123
|
+
String(assertion?.fullName),
|
|
124
|
+
{
|
|
125
|
+
status: String(assertion?.status),
|
|
126
|
+
...(typeof assertion?.duration === "number"
|
|
127
|
+
? { duration: assertion.duration }
|
|
128
|
+
: {}),
|
|
129
|
+
},
|
|
130
|
+
]
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Explain why one named case does not count as executed, if it does not.
|
|
138
|
+
* @param {string} name - Full test name the lane claims to run
|
|
139
|
+
* @param {{status: string, duration?: number} | undefined} reported - Its report entry
|
|
140
|
+
* @param {readonly string[]} [available] - Every name the report did contain
|
|
141
|
+
* @returns {string | undefined} Operator-readable failure, or undefined when it ran
|
|
142
|
+
*/
|
|
143
|
+
export function executionFailure(name, reported, available = []) {
|
|
144
|
+
if (reported === undefined)
|
|
145
|
+
return `"${name}" is ABSENT from the report — nothing in this lane ran it. Either its file was excluded, moved or renamed, or this name is written wrong: vitest's JSON reporter joins suite titles to the case title with a SPACE, while \`vitest list\` prints them with " > ". The report contained ${available.length === 0 ? "no cases at all" : available.map(reportedName => `"${reportedName}"`).join(", ")}.`;
|
|
146
|
+
if (reported.status !== "passed")
|
|
147
|
+
return `"${name}" did not execute: ${NON_EXECUTED_STATUS_ADVICE.get(reported.status) ?? `the runner reported status "${reported.status}"`}.`;
|
|
148
|
+
if (typeof reported.duration !== "number" || reported.duration <= 0)
|
|
149
|
+
return `"${name}" is reported as passed but consumed no measurable time, so there is no evidence it did any work.`;
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Prove every named case executed, printing the measured time for each.
|
|
155
|
+
* @param {readonly string[]} argv - Arguments after the script path
|
|
156
|
+
* @returns {readonly string[]} Failures, empty when every case executed
|
|
157
|
+
*/
|
|
158
|
+
export function checkTestCasesExecuted(argv) {
|
|
159
|
+
const { names, report } = parseArguments(argv);
|
|
160
|
+
const cases = reportedCases(report);
|
|
161
|
+
const available = [...cases.keys()];
|
|
162
|
+
return names.flatMap(name => {
|
|
163
|
+
const failure = executionFailure(name, cases.get(name), available);
|
|
164
|
+
if (failure !== undefined) return [failure];
|
|
165
|
+
process.stdout.write(
|
|
166
|
+
`EXECUTED ${name} — ${(cases.get(name)?.duration ?? 0).toFixed(1)}ms\n`
|
|
167
|
+
);
|
|
168
|
+
return [];
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (invokedAsScript(import.meta.url)) {
|
|
173
|
+
try {
|
|
174
|
+
const failures = checkTestCasesExecuted(process.argv.slice(2));
|
|
175
|
+
for (const failure of failures)
|
|
176
|
+
process.stderr.write(`NOT EXECUTED ${failure}\n`);
|
|
177
|
+
process.exit(failures.length === 0 ? 0 : 1);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
process.stderr.write(
|
|
180
|
+
`${error instanceof Error ? error.message : String(error)}\n`
|
|
181
|
+
);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -116,7 +116,13 @@ function mappedPaths(root) {
|
|
|
116
116
|
}
|
|
117
117
|
if (patterns.length === 0) return [];
|
|
118
118
|
const attr = git(["check-attr", "merge", "--", ...patterns], root);
|
|
119
|
-
|
|
119
|
+
// probe-direction: fail-closed — when `check-attr` cannot be asked, fall back
|
|
120
|
+
// to the patterns `.gitattributes` actually declares. Returning [] instead
|
|
121
|
+
// made the caller print "nothing in this working tree maps to the driver —
|
|
122
|
+
// nothing to register" and exit 0, so a git that failed was reported as a
|
|
123
|
+
// checkout that needs nothing. The fallback costs a redundant registration;
|
|
124
|
+
// the empty list cost a silently skipped one.
|
|
125
|
+
if (!attr.ok) return patterns;
|
|
120
126
|
return attr.stdout
|
|
121
127
|
.split("\n")
|
|
122
128
|
.filter(line => line.endsWith(`merge: ${GENERATED_ARTIFACT_DRIVER}`))
|
|
@@ -117,26 +117,25 @@ export function guardPopulation(root) {
|
|
|
117
117
|
.filter(file => file.length > 0)
|
|
118
118
|
.filter(file => !NOT_GUARDS.some(prefix => file.startsWith(prefix)))
|
|
119
119
|
.sort((left, right) => left.localeCompare(right));
|
|
120
|
-
return tracked.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
120
|
+
return tracked.map(file => {
|
|
121
|
+
// probe-direction: fail-closed — an unreadable TRACKED guard RAISES rather
|
|
122
|
+
// than dropping out of the roster. `git ls-files` has already proved the
|
|
123
|
+
// path is tracked, so a read failure is "could not ask", and the previous
|
|
124
|
+
// `catch { return []; }` spent it as "this guard does not exist" — which
|
|
125
|
+
// silently shrinks the population the coverage check is measured against,
|
|
126
|
+
// so a guard stops being covered with nothing saying so. This is the
|
|
127
|
+
// population, not a sample: it must be all of it or an error.
|
|
128
|
+
const bytes = readFileSync(path.join(root, file));
|
|
127
129
|
const source = bytes.toString("utf8");
|
|
128
|
-
return
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
/\bexit[ \t]+2\b/u.test(source),
|
|
138
|
-
},
|
|
139
|
-
];
|
|
130
|
+
return {
|
|
131
|
+
path: file,
|
|
132
|
+
sha256: createHash("sha256").update(bytes).digest("hex"),
|
|
133
|
+
size: bytes.length,
|
|
134
|
+
canRefuse: /\bexit[ \t]+[1-9]\d*\b/u.test(source),
|
|
135
|
+
toolBoundary:
|
|
136
|
+
(file.includes("/hooks/") || /tool_input|"file_path"/u.test(source)) &&
|
|
137
|
+
/\bexit[ \t]+2\b/u.test(source),
|
|
138
|
+
};
|
|
140
139
|
});
|
|
141
140
|
}
|
|
142
141
|
|
|
@@ -124,6 +124,10 @@ function git(args, cwd) {
|
|
|
124
124
|
timeout: 30_000,
|
|
125
125
|
});
|
|
126
126
|
} catch {
|
|
127
|
+
// probe-direction: fail-closed — null never reaches a verdict as "no
|
|
128
|
+
// weakening found". `changePlan` turns it into `undeterminable`, which
|
|
129
|
+
// REFUSES, and `isPromotion` falls to the strict reading that keeps the
|
|
130
|
+
// allow list pinned to the baseline.
|
|
127
131
|
return null;
|
|
128
132
|
}
|
|
129
133
|
}
|
|
@@ -169,6 +173,8 @@ function resolvePlan(mode, root, baseRef, onlyFiles) {
|
|
|
169
173
|
try {
|
|
170
174
|
return fs.readFileSync(path.join(root, f), "utf-8");
|
|
171
175
|
} catch {
|
|
176
|
+
// probe-direction: fail-closed — an unreadable current file compares as
|
|
177
|
+
// absent against the baseline, which is the strict side of the ratchet.
|
|
172
178
|
return null;
|
|
173
179
|
}
|
|
174
180
|
},
|
|
@@ -1349,6 +1349,10 @@ export const selectUninstrumentableMutateTargets = (cwd, patterns) => {
|
|
|
1349
1349
|
.filter(file => file && isMutateTarget(file, patterns))
|
|
1350
1350
|
.filter(file => !isStrykerParseable(file));
|
|
1351
1351
|
} catch {
|
|
1352
|
+
// probe-direction: neutral — this pre-empts a Stryker crash with a better
|
|
1353
|
+
// message; it does not authorise anything. When the index cannot be read the
|
|
1354
|
+
// run proceeds and Stryker still aborts on the same file, so the empty list
|
|
1355
|
+
// costs a worse error, not a weaker gate.
|
|
1352
1356
|
// Same reasoning as the count above: an unreadable index says nothing
|
|
1353
1357
|
// about the config, and blocking a push on it would be a lie about why.
|
|
1354
1358
|
return [];
|