@codyswann/lisa 4.40.0 → 4.41.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/all/copy-overwrite/scripts/lisa-cross-worktree-guard.mjs +57 -0
- package/all/copy-overwrite/scripts/lisa-hooks/block-direct-issue-create.sh +17 -0
- package/all/copy-overwrite/scripts/lisa-hooks/block-managed-file-edits.sh +14 -0
- package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +28 -0
- package/dist/codex/scripts/block-no-verify.sh +21 -0
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +20 -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 +13 -11
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/opencode/plugin-templates/block-managed-file-edits.sh +14 -0
- package/dist/opencode/plugin-templates/block-no-verify.sh +28 -0
- 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/hooks/block-direct-issue-create.sh +17 -0
- package/plugins/lisa/hooks/block-managed-file-edits.sh +14 -0
- package/plugins/lisa/hooks/block-no-verify.agy.sh +21 -0
- package/plugins/lisa/hooks/block-no-verify.sh +28 -0
- package/plugins/lisa-agy/hooks/block-direct-issue-create.sh +17 -0
- package/plugins/lisa-agy/hooks/block-managed-file-edits.sh +14 -0
- package/plugins/lisa-agy/hooks/block-no-verify.agy.sh +21 -0
- 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-copilot/hooks/block-direct-issue-create.sh +17 -0
- package/plugins/lisa-copilot/hooks/block-managed-file-edits.sh +14 -0
- package/plugins/lisa-copilot/hooks/block-no-verify.sh +28 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/block-direct-issue-create.sh +17 -0
- package/plugins/lisa-cursor/hooks/block-managed-file-edits.sh +14 -0
- package/plugins/lisa-cursor/hooks/block-no-verify.sh +28 -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-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/block-direct-issue-create.sh +17 -0
- package/plugins/src/base/hooks/block-managed-file-edits.sh +14 -0
- package/plugins/src/base/hooks/block-no-verify.agy.sh +21 -0
- package/plugins/src/base/hooks/block-no-verify.sh +28 -0
- package/rails/copy-contents/scripts/lisa-mutation.sh +8 -1
- package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +101 -1
- package/typescript/copy-overwrite/scripts/lisa-mutation.mjs +20 -14
|
@@ -341,6 +341,7 @@ export const VIOLATIONS = Object.freeze({
|
|
|
341
341
|
whitespace: "whitespace_in_skip_token",
|
|
342
342
|
vacuous: "vacuous_required_check",
|
|
343
343
|
unproven: "unproven_required_check",
|
|
344
|
+
absent: "absent_required_check",
|
|
344
345
|
reviewWaived: "review_evidence_waived",
|
|
345
346
|
reviewUnsatisfied: "review_evidence_unsatisfied",
|
|
346
347
|
});
|
|
@@ -1411,6 +1412,92 @@ function evaluateEvidenceBearingCheck(name, entry, checks, context) {
|
|
|
1411
1412
|
};
|
|
1412
1413
|
}
|
|
1413
1414
|
|
|
1415
|
+
/**
|
|
1416
|
+
* Every required context that posted NO check-run at all.
|
|
1417
|
+
*
|
|
1418
|
+
* The third member of the family this file's header names, and the one that was
|
|
1419
|
+
* missing. **Skipped** is a check that ran and declined to prove anything;
|
|
1420
|
+
* **vacuous** is one that ran, reported success, and did no work; **absent** is
|
|
1421
|
+
* one that never reported. All three satisfy a merge gate without evidence, and
|
|
1422
|
+
* absence is the quietest because it produces no row for anyone to read.
|
|
1423
|
+
*
|
|
1424
|
+
* MEASURED (CodySwannGT/lisa#3580, on PR #3573): 15 required contexts, 2
|
|
1425
|
+
* reported, 13 absent. The three workflow runs that would have posted them sat
|
|
1426
|
+
* at `conclusion: action_required` with `created_at == updated_at` — parked,
|
|
1427
|
+
* never executed. The pull request rendered as "CI running" for 26 hours.
|
|
1428
|
+
*
|
|
1429
|
+
* **Why the vacuity arm could not see it.** {@link evaluateVacuousChecks}
|
|
1430
|
+
* iterates `evidence_bearing_checks`, which held ONE name on that repository.
|
|
1431
|
+
* `required_contexts` held fifteen. This file already knew the full required
|
|
1432
|
+
* set and spent it only on an annotation — "This context IS ruleset-required" —
|
|
1433
|
+
* attached to findings about the one. Nothing ever walked the fifteen to ask
|
|
1434
|
+
* which of them said nothing. The data was here; the question was not.
|
|
1435
|
+
*
|
|
1436
|
+
* Three properties, each load-bearing:
|
|
1437
|
+
*
|
|
1438
|
+
* - **Absent is not pending.** A check in flight has posted a row and will post
|
|
1439
|
+
* a verdict; an absent one never will. Folding them together is the confusion
|
|
1440
|
+
* that made the stall invisible, so this must not make it in reverse — only a
|
|
1441
|
+
* context with no row at all counts, whatever state the rows carry.
|
|
1442
|
+
* - **No snapshot, no finding.** Without `required_contexts` this cannot know
|
|
1443
|
+
* what was required, and a guard that invented the set would fire on every
|
|
1444
|
+
* repository that has not transcribed one. Absence of knowledge is not
|
|
1445
|
+
* knowledge of absence — the same distinction the arm exists to enforce,
|
|
1446
|
+
* pointed at itself.
|
|
1447
|
+
* - **The parked-run lookup is an ENRICHMENT, never a precondition.** It names
|
|
1448
|
+
* the approval gate when it can, because "absent" alone sends an operator
|
|
1449
|
+
* hunting a broken workflow. But a finding that only reports when it can also
|
|
1450
|
+
* explain would report nothing wherever the explanation is unavailable, which
|
|
1451
|
+
* is the silent failure this was written to end.
|
|
1452
|
+
* @param {object} declaration - The per-repo declaration
|
|
1453
|
+
* @param {ReadonlyArray<{name: string}>} checks - Rows read for the head SHA
|
|
1454
|
+
* @param {object} [options] - `headSha` to cite, `parkedRuns` to explain with
|
|
1455
|
+
* @returns {{violations: object[], checked: number, absent: string[]}} Findings
|
|
1456
|
+
*/
|
|
1457
|
+
export function evaluateAbsentRequiredChecks(
|
|
1458
|
+
declaration,
|
|
1459
|
+
checks,
|
|
1460
|
+
options = {}
|
|
1461
|
+
) {
|
|
1462
|
+
const required = declaration.required_contexts;
|
|
1463
|
+
if (!Array.isArray(required))
|
|
1464
|
+
return { violations: [], checked: 0, absent: [] };
|
|
1465
|
+
|
|
1466
|
+
const at = citeHeadSha(options.headSha);
|
|
1467
|
+
const parked = (options.parkedRuns ?? []).filter(
|
|
1468
|
+
run => String(run?.conclusion ?? "") === "action_required"
|
|
1469
|
+
);
|
|
1470
|
+
// Named once for every finding rather than per context: a parked run posts no
|
|
1471
|
+
// check-run, so nothing ties one run to one absent context. Reporting the
|
|
1472
|
+
// whole parked set against each is honest about that; pretending to a mapping
|
|
1473
|
+
// this data cannot support would be a worse answer than an imprecise one.
|
|
1474
|
+
const approval =
|
|
1475
|
+
parked.length === 0
|
|
1476
|
+
? ""
|
|
1477
|
+
: ` A workflow run for this head is parked at \`action_required\` and will never complete on its own: ${parked
|
|
1478
|
+
.map(
|
|
1479
|
+
run => `${JSON.stringify(String(run.name ?? ""))} (run ${run.id})`
|
|
1480
|
+
)
|
|
1481
|
+
.join(
|
|
1482
|
+
", "
|
|
1483
|
+
)}. Approve it with \`gh api -X POST repos/<owner>/<repo>/actions/runs/<id>/approve\`, or the context stays absent indefinitely.`;
|
|
1484
|
+
|
|
1485
|
+
const absent = required.filter(
|
|
1486
|
+
name => checks.find(check => check.name === name) === undefined
|
|
1487
|
+
);
|
|
1488
|
+
|
|
1489
|
+
return {
|
|
1490
|
+
checked: required.length,
|
|
1491
|
+
absent,
|
|
1492
|
+
violations: absent.map(name => ({
|
|
1493
|
+
kind: VIOLATIONS.absent,
|
|
1494
|
+
token: name,
|
|
1495
|
+
contexts: [name],
|
|
1496
|
+
message: `\`${name}\` is ruleset-required but did not report on this pull request at all.${at} It posted NO check-run — not pending, not failing, ABSENT. \`gh pr checks\` prints no line for it and the pull request page renders it exactly as one still in flight, so every surface says "wait" and none says "broken".${approval} (If the context was renamed, fix \`required_contexts\`; names are compared byte for byte.)`,
|
|
1497
|
+
})),
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1414
1501
|
/**
|
|
1415
1502
|
* Descriptions that WAIVE the review gate, matched whole and case-insensitively.
|
|
1416
1503
|
*
|
|
@@ -2810,6 +2897,14 @@ export function inspectVacuity(argv, declaration, options = {}) {
|
|
|
2810
2897
|
trustRequiredContexts: options.trustRequiredContexts,
|
|
2811
2898
|
headSha: read.headSha,
|
|
2812
2899
|
});
|
|
2900
|
+
// Evaluated on the same rows the vacuity arm just read, so the absence arm
|
|
2901
|
+
// costs no extra fetch. It runs on EVERY invocation for the reason the review
|
|
2902
|
+
// gate does: a finding computed only when somebody opted in is invisible on
|
|
2903
|
+
// exactly the repositories that have not opted in yet.
|
|
2904
|
+
const absent = evaluateAbsentRequiredChecks(declaration, read.checks, {
|
|
2905
|
+
headSha: read.headSha,
|
|
2906
|
+
parkedRuns: options.parkedRuns,
|
|
2907
|
+
});
|
|
2813
2908
|
// The gate is evaluated on EVERY run, not only when a caller asked it to
|
|
2814
2909
|
// block. Its findings are what make a waiver visible, and a waiver that is
|
|
2815
2910
|
// only computed when somebody opted in would be invisible on exactly the
|
|
@@ -2841,7 +2936,12 @@ export function inspectVacuity(argv, declaration, options = {}) {
|
|
|
2841
2936
|
prSource: source,
|
|
2842
2937
|
headSha: read.headSha,
|
|
2843
2938
|
checked: evaluated.checked,
|
|
2844
|
-
violations: [
|
|
2939
|
+
violations: [
|
|
2940
|
+
...evaluated.violations,
|
|
2941
|
+
...absent.violations,
|
|
2942
|
+
...gate.violations,
|
|
2943
|
+
],
|
|
2944
|
+
absent: absent.absent,
|
|
2845
2945
|
gateStates: gate.states,
|
|
2846
2946
|
gateConditions: gate.conditions,
|
|
2847
2947
|
gateDescriptions: gate.descriptions,
|
|
@@ -1388,6 +1388,15 @@ export const parseChangedLineRanges = patch => {
|
|
|
1388
1388
|
|
|
1389
1389
|
/**
|
|
1390
1390
|
* Stryker mutation ranges for one changed file.
|
|
1391
|
+
*
|
|
1392
|
+
* The diff runs against the WORKING TREE, not `base...HEAD`. Stryker mutates
|
|
1393
|
+
* the files on disk, so a window derived from committed state describes lines
|
|
1394
|
+
* that may no longer be where it says they are: any uncommitted edit above the
|
|
1395
|
+
* change shifts the numbering out from under the window, and the run then
|
|
1396
|
+
* mutates whatever now occupies those lines — measured once as 37 lines of pure
|
|
1397
|
+
* comment, scored, and reported as a verdict about the change. It could as
|
|
1398
|
+
* easily have produced a pass. Diffing what Stryker actually reads is what
|
|
1399
|
+
* makes the window and the subject the same thing.
|
|
1391
1400
|
* @param {string} cwd - Project root.
|
|
1392
1401
|
* @param {string} base - Merge-base sha.
|
|
1393
1402
|
* @param {string} file - Repository-relative path.
|
|
@@ -1395,14 +1404,7 @@ export const parseChangedLineRanges = patch => {
|
|
|
1395
1404
|
*/
|
|
1396
1405
|
export const selectChangedLineRanges = (cwd, base, file) =>
|
|
1397
1406
|
parseChangedLineRanges(
|
|
1398
|
-
git(cwd, [
|
|
1399
|
-
"diff",
|
|
1400
|
-
"--unified=0",
|
|
1401
|
-
"--diff-filter=ACMR",
|
|
1402
|
-
`${base}...HEAD`,
|
|
1403
|
-
"--",
|
|
1404
|
-
file,
|
|
1405
|
-
])
|
|
1407
|
+
git(cwd, ["diff", "--unified=0", "--diff-filter=ACMR", base, "--", file])
|
|
1406
1408
|
).map(({ start, end }) => `${file}:${start}-${end}`);
|
|
1407
1409
|
|
|
1408
1410
|
/**
|
|
@@ -1416,12 +1418,11 @@ export const selectChangedLineRanges = (cwd, base, file) =>
|
|
|
1416
1418
|
* files no mutation tool here reaches.
|
|
1417
1419
|
*/
|
|
1418
1420
|
export const selectChangedTargets = (cwd, base, patterns) => {
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
])
|
|
1421
|
+
// Working tree, not `base...HEAD`, for the same reason the line ranges are:
|
|
1422
|
+
// a target whose only change is uncommitted is invisible to committed state,
|
|
1423
|
+
// so the gate printed "0 mutate targets" and exited 0 while Stryker, pointed
|
|
1424
|
+
// at the same tree, had a changed target in front of it.
|
|
1425
|
+
const changed = git(cwd, ["diff", "--name-only", "--diff-filter=ACMRD", base])
|
|
1425
1426
|
.split("\n")
|
|
1426
1427
|
.map(file => file.trim())
|
|
1427
1428
|
.filter(Boolean);
|
|
@@ -2115,6 +2116,11 @@ export const reportRun = (cwd, result) => {
|
|
|
2115
2116
|
export const runGate = (cwd = process.cwd(), argv = []) => {
|
|
2116
2117
|
const gate = readGate(cwd);
|
|
2117
2118
|
const enabled = envFlag("MUTATION_ENABLED") ?? gate.enabled === true;
|
|
2119
|
+
// Reads like the LOCAL branch and is not: `resolveDiffBase` probes
|
|
2120
|
+
// `origin/<since>` first and only falls back to the local name, so a stale
|
|
2121
|
+
// local `main` does not drag every commit it is missing into the diff. Said
|
|
2122
|
+
// here because the line alone has already been read the other way and filed
|
|
2123
|
+
// as a defect; the behaviour is pinned by a test rather than by this note.
|
|
2118
2124
|
const since = process.env.MUTATION_SINCE || gate.since || "main";
|
|
2119
2125
|
|
|
2120
2126
|
if (!enabled) {
|