@codyswann/lisa 4.42.7 → 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/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/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/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 +15 -1
- 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/package.json +4 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- 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-cursor/.claude-plugin/plugin.json +1 -1
- 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-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/scripts/check-test-case-executed.mjs +184 -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
|
+
}
|