@codyswann/lisa 4.33.8 → 4.33.9
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 +22 -0
- package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +44 -1
- package/all/copy-overwrite/scripts/lisa-work-item.mjs +361 -10
- package/dist/configs/vitest/base.d.ts +76 -11
- package/dist/configs/vitest/base.d.ts.map +1 -1
- package/dist/configs/vitest/base.js +121 -14
- package/dist/configs/vitest/base.js.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +12 -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 +11 -8
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/opencode/plugin-templates/parity-safety-net.sh +44 -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/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +17 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +1 -1
- package/plugins/lisa/hooks/parity-safety-net.sh +44 -1
- package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +17 -2
- package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +1 -1
- package/plugins/lisa-agy/hooks/parity-safety-net.sh +44 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +17 -2
- package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +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-copilot/hooks/parity-safety-net.sh +44 -1
- package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +17 -2
- package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/parity-safety-net.sh +44 -1
- package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +17 -2
- package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +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/plugins/src/base/hooks/parity-safety-net.sh +44 -1
- package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +17 -2
- package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +1 -1
- package/scripts/two-channel-couplings.json +1 -1
- package/typescript/copy-overwrite/scripts/lisa-mutation.mjs +96 -12
- package/typescript/copy-overwrite/scripts/reconcile-nightly-e2e-tracking.mjs +3 -2
|
@@ -197,6 +197,87 @@ export const OUTCOMES = Object.freeze({
|
|
|
197
197
|
noFloorApplied: "mutation-gate: no-floor-applied",
|
|
198
198
|
});
|
|
199
199
|
|
|
200
|
+
/**
|
|
201
|
+
* The outcomes under which Stryker generated mutants and produced a score.
|
|
202
|
+
*
|
|
203
|
+
* THE POINT OF #3668. Every other outcome in {@link OUTCOMES} ends a run that
|
|
204
|
+
* MEASURED NOTHING, and until this set existed the exit-0 members of that group
|
|
205
|
+
* were indistinguishable from a real pass at the only layer a merge decision
|
|
206
|
+
* consults: `🔍 Quality Checks / 🧬 Mutation Testing Gate` printed `pass` for a
|
|
207
|
+
* pull request no mutant had touched. Measured on #3664 — a 400-line change to
|
|
208
|
+
* a shipped guard script, `nothing-to-mutate`, `pass`.
|
|
209
|
+
*
|
|
210
|
+
* The file said so itself, above {@link OUTCOMES}, and had for months: "both
|
|
211
|
+
* exit 0, and only the marker says which one happened." The marker lives in a
|
|
212
|
+
* log; a log is not a control. This set is what lets the workflow render the
|
|
213
|
+
* difference.
|
|
214
|
+
*
|
|
215
|
+
* MEMBERSHIP IS THE STRYKER RUN, not the verdict. `scoped` and `wholeList` are
|
|
216
|
+
* the two outcomes that carry a `reportRun`, so they are the two under which
|
|
217
|
+
* mutants existed — whether the score then cleared the floor or not is a
|
|
218
|
+
* different question, answered by the exit code. A run that measured and FAILED
|
|
219
|
+
* is still a run that measured.
|
|
220
|
+
*/
|
|
221
|
+
export const MEASURED_OUTCOMES = Object.freeze([
|
|
222
|
+
OUTCOMES.scoped,
|
|
223
|
+
OUTCOMES.wholeList,
|
|
224
|
+
]);
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Whether an outcome means mutants were generated against this change.
|
|
228
|
+
*
|
|
229
|
+
* An unknown marker answers `false`, and that direction is deliberate: a new
|
|
230
|
+
* outcome added without being classified must not inherit "measured". Failing
|
|
231
|
+
* closed here costs a `skipping` row on a run that did measure; failing open
|
|
232
|
+
* costs exactly the false green this issue is about.
|
|
233
|
+
*
|
|
234
|
+
* @param {string} outcome - One {@link OUTCOMES} marker
|
|
235
|
+
* @returns {boolean} True when Stryker produced mutants
|
|
236
|
+
*/
|
|
237
|
+
export const measuredAnything = outcome =>
|
|
238
|
+
MEASURED_OUTCOMES.includes(String(outcome));
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Publishes the outcome where the workflow can render it, then returns the code.
|
|
242
|
+
*
|
|
243
|
+
* Every exit in {@link runGate} goes through here, and that is the property
|
|
244
|
+
* worth more than the plumbing: an exit that forgets to name its outcome is now
|
|
245
|
+
* impossible to write, rather than merely discouraged by a convention. It is
|
|
246
|
+
* the same shape the automation-runbook contract asks of a loop — end in
|
|
247
|
+
* exactly one named outcome — applied to a gate.
|
|
248
|
+
*
|
|
249
|
+
* `$GITHUB_OUTPUT` rather than a check-run publish, and the difference is
|
|
250
|
+
* forced rather than chosen: `test_mutation` in `quality.yml` declares
|
|
251
|
+
* `permissions: contents: read`, and `quality.yml` is `workflow_call`-only. A
|
|
252
|
+
* called workflow may only DOWNGRADE its caller's grant — asking for `checks:
|
|
253
|
+
* write` is a `startup_failure` for the entire run (#2049), which is why
|
|
254
|
+
* `review-evidence.yml` is a standalone workflow rather than a job. So the
|
|
255
|
+
* verdict leaves as a job output and a downstream job renders it.
|
|
256
|
+
*
|
|
257
|
+
* Best-effort, for the reason every reporting path in this repository is: a
|
|
258
|
+
* lost output line must never change the exit code the gate worked to earn.
|
|
259
|
+
*
|
|
260
|
+
* @param {string} outcome - The {@link OUTCOMES} marker this run ended on
|
|
261
|
+
* @param {number} code - The exit code to return unchanged
|
|
262
|
+
* @param {NodeJS.ProcessEnv} [env] - Environment, injectable for tests
|
|
263
|
+
* @returns {number} `code`, unchanged
|
|
264
|
+
*/
|
|
265
|
+
export const finish = (outcome, code, env = process.env) => {
|
|
266
|
+
const target = env.GITHUB_OUTPUT;
|
|
267
|
+
if (target === undefined || target === "") return code;
|
|
268
|
+
try {
|
|
269
|
+
fs.appendFileSync(
|
|
270
|
+
target,
|
|
271
|
+
`mutation_outcome=${outcome}\nmutation_measured=${measuredAnything(outcome)}\n`
|
|
272
|
+
);
|
|
273
|
+
} catch (error) {
|
|
274
|
+
console.error(
|
|
275
|
+
`[mutation-gate] outcome outputs not written: ${error.message}`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
return code;
|
|
279
|
+
};
|
|
280
|
+
|
|
200
281
|
/**
|
|
201
282
|
* Stryker config file names, in the order Stryker itself resolves them.
|
|
202
283
|
*
|
|
@@ -2080,7 +2161,7 @@ export const runGate = (cwd = process.cwd(), argv = []) => {
|
|
|
2080
2161
|
`⚪ ${OUTCOMES.disabled} — mutation.gate.json says "enabled": false. Skipping.\n` +
|
|
2081
2162
|
' Flip "enabled": true (and tune thresholds.break in stryker.conf.json) to turn it on.'
|
|
2082
2163
|
);
|
|
2083
|
-
return 0;
|
|
2164
|
+
return finish(OUTCOMES.disabled, 0);
|
|
2084
2165
|
}
|
|
2085
2166
|
|
|
2086
2167
|
const declaration = resolveMutateDeclaration(cwd);
|
|
@@ -2093,7 +2174,7 @@ export const runGate = (cwd = process.cwd(), argv = []) => {
|
|
|
2093
2174
|
` ${error instanceof Error ? error.message : String(error)}\n` +
|
|
2094
2175
|
` Fix the \`mutate\` patterns in ${declaration.source} before re-running.`
|
|
2095
2176
|
);
|
|
2096
|
-
return 1;
|
|
2177
|
+
return finish(OUTCOMES.invalidMutatePattern, 1);
|
|
2097
2178
|
}
|
|
2098
2179
|
|
|
2099
2180
|
if (countMutateTargetsInRepo(cwd, patterns) === 0) {
|
|
@@ -2105,7 +2186,7 @@ export const runGate = (cwd = process.cwd(), argv = []) => {
|
|
|
2105
2186
|
" that is switched on and wired to nothing.\n" +
|
|
2106
2187
|
" Fix the `mutate` patterns in your Stryker config, or turn the gate off."
|
|
2107
2188
|
);
|
|
2108
|
-
return 1;
|
|
2189
|
+
return finish(OUTCOMES.inertConfig, 1);
|
|
2109
2190
|
}
|
|
2110
2191
|
|
|
2111
2192
|
const unparseable = selectUninstrumentableMutateTargets(cwd, patterns);
|
|
@@ -2127,7 +2208,7 @@ export const runGate = (cwd = process.cwd(), argv = []) => {
|
|
|
2127
2208
|
" script against a payload table and asserts the blocked/allowed\n" +
|
|
2128
2209
|
" verdict — never by this gate."
|
|
2129
2210
|
);
|
|
2130
|
-
return 1;
|
|
2211
|
+
return finish(OUTCOMES.uninstrumentableTarget, 1);
|
|
2131
2212
|
}
|
|
2132
2213
|
|
|
2133
2214
|
if (argv.includes(WHOLE_LIST_FLAG)) {
|
|
@@ -2135,7 +2216,7 @@ export const runGate = (cwd = process.cwd(), argv = []) => {
|
|
|
2135
2216
|
`🧬 ${OUTCOMES.wholeList} — Stryker over every pattern in ` +
|
|
2136
2217
|
`${declaration.source}, with no diff scoping.`
|
|
2137
2218
|
);
|
|
2138
|
-
return reportRun(cwd, runStryker(cwd, []));
|
|
2219
|
+
return finish(OUTCOMES.wholeList, reportRun(cwd, runStryker(cwd, [])));
|
|
2139
2220
|
}
|
|
2140
2221
|
|
|
2141
2222
|
const base = resolveDiffBase(cwd, since);
|
|
@@ -2145,7 +2226,7 @@ export const runGate = (cwd = process.cwd(), argv = []) => {
|
|
|
2145
2226
|
" unknown ref). Skipping rather than mutating the whole repository.\n" +
|
|
2146
2227
|
" Nothing was measured; this is not a mutation score."
|
|
2147
2228
|
);
|
|
2148
|
-
return 0;
|
|
2229
|
+
return finish(OUTCOMES.noBase, 0);
|
|
2149
2230
|
}
|
|
2150
2231
|
|
|
2151
2232
|
let scope;
|
|
@@ -2168,7 +2249,7 @@ export const runGate = (cwd = process.cwd(), argv = []) => {
|
|
|
2168
2249
|
" NO mutant was generated and NO score was computed. Nothing was measured,\n" +
|
|
2169
2250
|
" so nothing passed."
|
|
2170
2251
|
);
|
|
2171
|
-
return 1;
|
|
2252
|
+
return finish(OUTCOMES.diffFailed, 1);
|
|
2172
2253
|
}
|
|
2173
2254
|
|
|
2174
2255
|
if (scope.uninstrumentable.length > 0) {
|
|
@@ -2191,7 +2272,7 @@ export const runGate = (cwd = process.cwd(), argv = []) => {
|
|
|
2191
2272
|
" on both sides. Check that one exists; nothing here did."
|
|
2192
2273
|
);
|
|
2193
2274
|
if (scope.selected.length === 0 && scope.noCurrentLines.length === 0)
|
|
2194
|
-
return 0;
|
|
2275
|
+
return finish(OUTCOMES.uninstrumentableLanguage, 0);
|
|
2195
2276
|
}
|
|
2196
2277
|
|
|
2197
2278
|
if (scope.selected.length === 0) {
|
|
@@ -2207,7 +2288,7 @@ export const runGate = (cwd = process.cwd(), argv = []) => {
|
|
|
2207
2288
|
" Stryker can place mutants only on current lines. NO mutant was generated\n" +
|
|
2208
2289
|
" and NO score was computed; this is not a measured pass."
|
|
2209
2290
|
);
|
|
2210
|
-
return 0;
|
|
2291
|
+
return finish(OUTCOMES.noCurrentLines, 0);
|
|
2211
2292
|
}
|
|
2212
2293
|
console.log(
|
|
2213
2294
|
`⚪ ${OUTCOMES.nothingToMutate}\n` +
|
|
@@ -2216,7 +2297,7 @@ export const runGate = (cwd = process.cwd(), argv = []) => {
|
|
|
2216
2297
|
" NO mutant was generated and NO score was computed. Nothing was measured,\n" +
|
|
2217
2298
|
" so nothing passed — do not read this as evidence about your tests."
|
|
2218
2299
|
);
|
|
2219
|
-
return 0;
|
|
2300
|
+
return finish(OUTCOMES.nothingToMutate, 0);
|
|
2220
2301
|
}
|
|
2221
2302
|
|
|
2222
2303
|
// `--mutate` is one comma-separated argument, so a path containing a comma
|
|
@@ -2234,7 +2315,7 @@ export const runGate = (cwd = process.cwd(), argv = []) => {
|
|
|
2234
2315
|
`${listed}\n` +
|
|
2235
2316
|
" Rename them, or exclude them in your Stryker config."
|
|
2236
2317
|
);
|
|
2237
|
-
return 1;
|
|
2318
|
+
return finish(OUTCOMES.unrepresentablePath, 1);
|
|
2238
2319
|
}
|
|
2239
2320
|
|
|
2240
2321
|
console.log(
|
|
@@ -2243,7 +2324,10 @@ export const runGate = (cwd = process.cwd(), argv = []) => {
|
|
|
2243
2324
|
);
|
|
2244
2325
|
for (const file of scope.selected) console.log(` • ${file}`);
|
|
2245
2326
|
|
|
2246
|
-
return
|
|
2327
|
+
return finish(
|
|
2328
|
+
OUTCOMES.scoped,
|
|
2329
|
+
reportRun(cwd, runStryker(cwd, scope.selected))
|
|
2330
|
+
);
|
|
2247
2331
|
};
|
|
2248
2332
|
|
|
2249
2333
|
/**
|
|
@@ -36,8 +36,9 @@ function validateAuthority(findings, trackers) {
|
|
|
36
36
|
if (!Array.isArray(findings) || findings.length !== 2) {
|
|
37
37
|
refuse(exactSuiteMessage);
|
|
38
38
|
}
|
|
39
|
-
const
|
|
40
|
-
const
|
|
39
|
+
const byLabel = (left, right) => String(left).localeCompare(String(right));
|
|
40
|
+
const labels = findings.map(finding => finding?.label).sort(byLabel);
|
|
41
|
+
const expected = [...TRACKED_SUITE_LABELS].sort(byLabel);
|
|
41
42
|
if (JSON.stringify(labels) !== JSON.stringify(expected)) {
|
|
42
43
|
refuse(exactSuiteMessage);
|
|
43
44
|
}
|