@codyswann/lisa 4.84.3 → 4.84.5
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/lib/npm-update-rails-tool-identity.mjs +1 -1
- package/all/copy-overwrite/scripts/lib/npm-update-rails-tools.mjs +28 -3
- package/all/copy-overwrite/scripts/npm-updater-helper-graph.json +2 -2
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +10 -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 +9 -6
- package/dist/core/upstream-evidence-manifest.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-cdk-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-devin/.devin-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-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-devin/.devin-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-expo-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-devin/.devin-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-harper-fabric-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-devin/.devin-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-nestjs-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-devin/.devin-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-openclaw-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-devin/.devin-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-phaser-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-devin/.devin-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-rails-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-devin/.devin-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-typescript-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-devin/.devin-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/lisa-wiki-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +1 -1
- package/scripts/two-channel-couplings.json +1 -1
- package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +139 -48
- package/typescript/create-only/.github/workflows/review-evidence.yml +2 -2
|
@@ -1775,10 +1775,12 @@ export function evaluateAbsentRequiredChecks(
|
|
|
1775
1775
|
return { violations: [], checked: 0, absent: [] };
|
|
1776
1776
|
// #4484: judged against the BASE branch's own set when the declaration has
|
|
1777
1777
|
// one, and against the union — reported, never silently — when it does not.
|
|
1778
|
-
const requiredSet =
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1778
|
+
const requiredSet =
|
|
1779
|
+
options.requiredSet ??
|
|
1780
|
+
selectRequiredContexts(
|
|
1781
|
+
declaration,
|
|
1782
|
+
options.base ?? { base: null, source: null }
|
|
1783
|
+
);
|
|
1782
1784
|
const required = requiredSet.contexts;
|
|
1783
1785
|
const judgedFor =
|
|
1784
1786
|
requiredSet.source === "branch"
|
|
@@ -1816,7 +1818,10 @@ export function evaluateAbsentRequiredChecks(
|
|
|
1816
1818
|
kind: VIOLATIONS.absent,
|
|
1817
1819
|
token: name,
|
|
1818
1820
|
contexts: [name],
|
|
1819
|
-
message:
|
|
1821
|
+
message:
|
|
1822
|
+
options.registration === undefined
|
|
1823
|
+
? `\`${name}\` is ruleset-required but did not report on this pull request at all.${at}${judgedFor} 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.)`
|
|
1824
|
+
: `${options.registration.line}${at}${judgedFor}${approval} The wait ending does not prove permanent absence or a defect in this change. If a required context was renamed or removed, reconcile its declared name with CI; names are compared byte for byte.`,
|
|
1820
1825
|
})),
|
|
1821
1826
|
};
|
|
1822
1827
|
}
|
|
@@ -4152,12 +4157,23 @@ export function declaredEvidenceChecks(declaration) {
|
|
|
4152
4157
|
* @returns {boolean} True when nothing declared is still in flight
|
|
4153
4158
|
*/
|
|
4154
4159
|
export function checksSettled(declaration, checks) {
|
|
4155
|
-
return declaredEvidenceChecks(declaration).every(name =>
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4160
|
+
return declaredEvidenceChecks(declaration).every(name =>
|
|
4161
|
+
checkSettled(checks.find(check => check.name === name))
|
|
4162
|
+
);
|
|
4163
|
+
}
|
|
4164
|
+
|
|
4165
|
+
/**
|
|
4166
|
+
* Distinguishes a completed reviewer from an absent or in-flight row.
|
|
4167
|
+
*
|
|
4168
|
+
* @param {object|undefined} found - One normalized check row
|
|
4169
|
+
* @returns {boolean} Whether the row is terminal
|
|
4170
|
+
*/
|
|
4171
|
+
function checkSettled(found) {
|
|
4172
|
+
return (
|
|
4173
|
+
found !== undefined &&
|
|
4174
|
+
String(found.bucket ?? "").toLowerCase() !== "pending" &&
|
|
4175
|
+
String(found.state ?? "").toUpperCase() !== "PENDING"
|
|
4176
|
+
);
|
|
4161
4177
|
}
|
|
4162
4178
|
|
|
4163
4179
|
/**
|
|
@@ -4176,15 +4192,20 @@ function sleepSync(ms) {
|
|
|
4176
4192
|
}
|
|
4177
4193
|
|
|
4178
4194
|
/**
|
|
4179
|
-
* Reads
|
|
4195
|
+
* Reads until reviewers settle and the selected required rows register.
|
|
4196
|
+
*
|
|
4197
|
+
* Registration needs a row, including a pending or failing row; reviewer
|
|
4198
|
+
* completion is a separate observation. Both waits share one absolute deadline.
|
|
4199
|
+
* A head-bound substitute can skip an unreported reviewer's evidence wait,
|
|
4200
|
+
* but never the registration of a ruleset-required context.
|
|
4180
4201
|
*
|
|
4181
4202
|
* @param {object} declaration - The per-repo declaration
|
|
4182
4203
|
* @param {string|number} pr - Pull request number
|
|
4183
4204
|
* @param {string|undefined} repo - `OWNER/NAME`, or undefined
|
|
4184
|
-
* @param {{timeoutSeconds?: number, intervalSeconds?: number, fetch?: Function, now?: Function, sleep?: Function, headSha?: Function}} [options] -
|
|
4205
|
+
* @param {{timeoutSeconds?: number, intervalSeconds?: number, fetch?: Function, now?: Function, sleep?: Function, headSha?: Function, requiredSet?: object, substitutes?: Set<string>, substituteHead?: string}} [options] -
|
|
4185
4206
|
* Injection seams; the suite drives the whole loop through them so nothing
|
|
4186
4207
|
* here has to sleep in real time
|
|
4187
|
-
* @returns {{checks: object[], settled: boolean, headSha: string|undefined}}
|
|
4208
|
+
* @returns {{checks: object[], settled: boolean, headSha: string|undefined, registration: object}} Rows, reviewer completion, head and required-registration observations
|
|
4188
4209
|
*/
|
|
4189
4210
|
export function fetchSettledChecks(declaration, pr, repo, options = {}) {
|
|
4190
4211
|
const timeoutMs = (options.timeoutSeconds ?? SETTLE_TIMEOUT_SECONDS) * 1000;
|
|
@@ -4203,7 +4224,27 @@ export function fetchSettledChecks(declaration, pr, repo, options = {}) {
|
|
|
4203
4224
|
const resolveHead = options.headSha ?? resolveHeadSha;
|
|
4204
4225
|
const clock = options.now ?? Date.now;
|
|
4205
4226
|
const sleep = options.sleep ?? sleepSync;
|
|
4206
|
-
const
|
|
4227
|
+
const started = clock();
|
|
4228
|
+
const deadline = started + timeoutMs;
|
|
4229
|
+
const requiredSet =
|
|
4230
|
+
options.requiredSet ??
|
|
4231
|
+
selectRequiredContexts(declaration, { base: null, source: null });
|
|
4232
|
+
const missing = checks =>
|
|
4233
|
+
requiredSet.contexts.filter(
|
|
4234
|
+
name => !checks.some(check => check.name === name)
|
|
4235
|
+
);
|
|
4236
|
+
// A substitute is useful only at the head it was actually read for. Pending
|
|
4237
|
+
// rows still wait, and a missing required reviewer still needs a real row.
|
|
4238
|
+
const evidenceReady = observed =>
|
|
4239
|
+
declaredEvidenceChecks(declaration).every(name => {
|
|
4240
|
+
const found = observed.checks.find(check => check.name === name);
|
|
4241
|
+
return (
|
|
4242
|
+
checkSettled(found) ||
|
|
4243
|
+
(found === undefined &&
|
|
4244
|
+
observed.headSha === options.substituteHead &&
|
|
4245
|
+
options.substitutes?.has(name))
|
|
4246
|
+
);
|
|
4247
|
+
});
|
|
4207
4248
|
|
|
4208
4249
|
// Bracket every roster read with head reads. `gh pr checks` targets the PR's
|
|
4209
4250
|
// current head but does not return the SHA it used, and the permission-light
|
|
@@ -4233,7 +4274,9 @@ export function fetchSettledChecks(declaration, pr, repo, options = {}) {
|
|
|
4233
4274
|
|
|
4234
4275
|
let observed = snapshot();
|
|
4235
4276
|
while (
|
|
4236
|
-
(!observed.stable ||
|
|
4277
|
+
(!observed.stable ||
|
|
4278
|
+
!evidenceReady(observed) ||
|
|
4279
|
+
missing(observed.checks).length > 0) &&
|
|
4237
4280
|
clock() < deadline
|
|
4238
4281
|
) {
|
|
4239
4282
|
sleep(Math.min(intervalMs, deadline - clock()));
|
|
@@ -4244,10 +4287,36 @@ export function fetchSettledChecks(declaration, pr, repo, options = {}) {
|
|
|
4244
4287
|
`check-skipped-required-checks: the pull request head changed while its checks were being read (${observed.headSha ?? "unresolved"} -> ${observed.after ?? "unresolved"}). Refusing to attach one commit's review evidence to another; re-read the pull request after the push settles.`
|
|
4245
4288
|
);
|
|
4246
4289
|
}
|
|
4290
|
+
const elapsedSeconds = Math.max(0, clock() - started) / 1000;
|
|
4291
|
+
const unregistered = missing(observed.checks);
|
|
4292
|
+
const total = requiredSet.contexts.length;
|
|
4293
|
+
const present = total - unregistered.length;
|
|
4294
|
+
const expired = unregistered.length > 0;
|
|
4295
|
+
const settled = checksSettled(declaration, observed.checks);
|
|
4296
|
+
const evidence = {
|
|
4297
|
+
settled,
|
|
4298
|
+
// A missing reviewer covered by a head-bound substitute did not finish,
|
|
4299
|
+
// but its evidence wait was skipped rather than expired. Reporting those
|
|
4300
|
+
// as the same fact contradicts the substituted verdict.
|
|
4301
|
+
expired: !evidenceReady(observed),
|
|
4302
|
+
line: `Review evidence ${settled ? "settled" : evidenceReady(observed) ? "covered by head-bound substitutes" : "wait EXPIRED"} after ${elapsedSeconds}s.`,
|
|
4303
|
+
};
|
|
4304
|
+
const registration = {
|
|
4305
|
+
elapsedSeconds,
|
|
4306
|
+
present,
|
|
4307
|
+
total,
|
|
4308
|
+
missing: unregistered,
|
|
4309
|
+
expired,
|
|
4310
|
+
line: expired
|
|
4311
|
+
? `Required registration wait EXPIRED: the gate stopped waiting after ${elapsedSeconds}s; ${present} of ${total} required contexts observed; missing: ${unregistered.join(", ")}. RE-RUN this job before treating this as a finding about the change.`
|
|
4312
|
+
: `Required registration settled after ${elapsedSeconds}s; ${present} of ${total} required contexts observed.`,
|
|
4313
|
+
};
|
|
4247
4314
|
return {
|
|
4248
4315
|
checks: observed.checks,
|
|
4249
|
-
settled
|
|
4316
|
+
settled,
|
|
4250
4317
|
headSha: observed.headSha,
|
|
4318
|
+
registration,
|
|
4319
|
+
evidence,
|
|
4251
4320
|
};
|
|
4252
4321
|
}
|
|
4253
4322
|
|
|
@@ -4504,14 +4573,19 @@ export function inspectVacuity(argv, declaration, options = {}) {
|
|
|
4504
4573
|
const allStoodIn =
|
|
4505
4574
|
declaredReviewers.length > 0 &&
|
|
4506
4575
|
declaredReviewers.every(name => earlySubstitutes.has(name));
|
|
4507
|
-
|
|
4508
|
-
|
|
4576
|
+
// Select once: polling and the absence verdict must judge the same base.
|
|
4577
|
+
// resolveBaseRef retains its existing contract; live retargeting is #4487.
|
|
4578
|
+
const requiredSet = selectRequiredContexts(
|
|
4579
|
+
declaration,
|
|
4580
|
+
resolveBaseRef(argv, options.env ?? process.env, { pr, repo })
|
|
4581
|
+
);
|
|
4582
|
+
let read;
|
|
4583
|
+
try {
|
|
4584
|
+
read = fetchSettledChecks(declaration, pr, repo, {
|
|
4509
4585
|
timeoutSeconds: readSecondsFlag(
|
|
4510
4586
|
argv,
|
|
4511
4587
|
"--settle-timeout",
|
|
4512
|
-
|
|
4513
|
-
? SETTLE_TIMEOUT_SECONDS
|
|
4514
|
-
: 0
|
|
4588
|
+
wired || requireReviewEvidence ? SETTLE_TIMEOUT_SECONDS : 0
|
|
4515
4589
|
),
|
|
4516
4590
|
intervalSeconds: readSecondsFlag(
|
|
4517
4591
|
argv,
|
|
@@ -4522,23 +4596,10 @@ export function inspectVacuity(argv, declaration, options = {}) {
|
|
|
4522
4596
|
now: options.now,
|
|
4523
4597
|
sleep: options.sleep,
|
|
4524
4598
|
headSha: options.headSha,
|
|
4599
|
+
requiredSet,
|
|
4600
|
+
substitutes: allStoodIn ? earlySubstitutes : undefined,
|
|
4601
|
+
substituteHead: earlyHead,
|
|
4525
4602
|
});
|
|
4526
|
-
let read;
|
|
4527
|
-
try {
|
|
4528
|
-
read = settle(allStoodIn);
|
|
4529
|
-
// The wait is skipped only for a reviewer that is NOT COMING. A declared
|
|
4530
|
-
// reviewer whose row is already there but still in flight is coming, and
|
|
4531
|
-
// a substitute does not stand in for it — so wait for its answer exactly
|
|
4532
|
-
// as an ordinary run would, rather than going red on one snapshot.
|
|
4533
|
-
if (
|
|
4534
|
-
allStoodIn &&
|
|
4535
|
-
!read.settled &&
|
|
4536
|
-
declaredReviewers.some(name =>
|
|
4537
|
-
read.checks.some(check => check.name === name)
|
|
4538
|
-
)
|
|
4539
|
-
) {
|
|
4540
|
-
read = settle(false);
|
|
4541
|
-
}
|
|
4542
4603
|
} catch (error) {
|
|
4543
4604
|
return refused(vacuityRefusal({ declaration, pr, error }));
|
|
4544
4605
|
}
|
|
@@ -4549,6 +4610,9 @@ export function inspectVacuity(argv, declaration, options = {}) {
|
|
|
4549
4610
|
...refused(refusal),
|
|
4550
4611
|
headSha: read.headSha,
|
|
4551
4612
|
settled: read.settled,
|
|
4613
|
+
requiredSet,
|
|
4614
|
+
registration: read.registration,
|
|
4615
|
+
evidence: read.evidence,
|
|
4552
4616
|
};
|
|
4553
4617
|
}
|
|
4554
4618
|
const evaluated = evaluateVacuousChecks(declaration, read.checks, {
|
|
@@ -4562,9 +4626,8 @@ export function inspectVacuity(argv, declaration, options = {}) {
|
|
|
4562
4626
|
const absent = evaluateAbsentRequiredChecks(declaration, read.checks, {
|
|
4563
4627
|
headSha: read.headSha,
|
|
4564
4628
|
parkedRuns: options.parkedRuns,
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
base: resolveBaseRef(argv, options.env ?? process.env, { pr, repo }),
|
|
4629
|
+
requiredSet,
|
|
4630
|
+
registration: read.registration,
|
|
4568
4631
|
});
|
|
4569
4632
|
// The gate is evaluated on EVERY run, not only when a caller asked it to
|
|
4570
4633
|
// block. Its findings are what make a waiver visible, and a waiver that is
|
|
@@ -4572,10 +4635,9 @@ export function inspectVacuity(argv, declaration, options = {}) {
|
|
|
4572
4635
|
// repositories that have not opted in yet.
|
|
4573
4636
|
const gateOptions = {
|
|
4574
4637
|
headSha: read.headSha,
|
|
4575
|
-
//
|
|
4576
|
-
//
|
|
4577
|
-
//
|
|
4578
|
-
// downstream sees a check row that looks identical either way (#3716).
|
|
4638
|
+
// Reviewer completion stays separate from required registration. Expiry
|
|
4639
|
+
// while an ordinary required row is missing must not turn a completed
|
|
4640
|
+
// reviewer into an undetermined one (#3716, #4527).
|
|
4579
4641
|
waitExpired: read.settled === false,
|
|
4580
4642
|
// Bound to the head the checks were read at. A push between the early read
|
|
4581
4643
|
// and the settled one invalidates every substitute for the old head.
|
|
@@ -4674,7 +4736,9 @@ export function inspectVacuity(argv, declaration, options = {}) {
|
|
|
4674
4736
|
...(objection?.violations ?? []),
|
|
4675
4737
|
],
|
|
4676
4738
|
absent: absent.absent,
|
|
4677
|
-
requiredSet
|
|
4739
|
+
requiredSet,
|
|
4740
|
+
registration: read.registration,
|
|
4741
|
+
evidence: read.evidence,
|
|
4678
4742
|
gateStates: gate.states,
|
|
4679
4743
|
gateConditions: gate.conditions,
|
|
4680
4744
|
gateDescriptions: gate.descriptions,
|
|
@@ -5392,7 +5456,7 @@ function appendCleanResult(lines, result, refusal) {
|
|
|
5392
5456
|
);
|
|
5393
5457
|
if (result.pr === undefined) return;
|
|
5394
5458
|
const settleNote =
|
|
5395
|
-
result.vacuity?.settled === false
|
|
5459
|
+
(result.vacuity?.evidence?.expired ?? result.vacuity?.settled === false)
|
|
5396
5460
|
? " (One or more had not settled when the wait expired, so this is what was true at that moment.)"
|
|
5397
5461
|
: "";
|
|
5398
5462
|
lines.push(
|
|
@@ -5444,7 +5508,7 @@ function appendFindingReport(lines, result, policy, blocking) {
|
|
|
5444
5508
|
* @returns {void}
|
|
5445
5509
|
*/
|
|
5446
5510
|
function appendFindingContext(lines, result, policy) {
|
|
5447
|
-
if (result.vacuity?.settled === false) {
|
|
5511
|
+
if (result.vacuity?.evidence?.expired ?? result.vacuity?.settled === false) {
|
|
5448
5512
|
lines.push(
|
|
5449
5513
|
"",
|
|
5450
5514
|
"⏳ The settle wait EXPIRED before every declared review check reached a terminal state, so the readings above are where the wait stopped rather than where the reviewer finished. RE-RUN this job before treating any of it as a finding about the change."
|
|
@@ -5602,6 +5666,13 @@ function buildTextReport(result, policy, outcome) {
|
|
|
5602
5666
|
const lines = ["## 🔒 Required checks that prove nothing", ""];
|
|
5603
5667
|
appendVerdictReport(lines, result);
|
|
5604
5668
|
appendRequiredSetReport(lines, result.vacuity?.requiredSet);
|
|
5669
|
+
if (result.vacuity?.registration !== undefined) {
|
|
5670
|
+
lines.push(
|
|
5671
|
+
result.vacuity.evidence.line,
|
|
5672
|
+
result.vacuity.registration.line,
|
|
5673
|
+
""
|
|
5674
|
+
);
|
|
5675
|
+
}
|
|
5605
5676
|
appendSnapshotRefusal(lines, result, policy);
|
|
5606
5677
|
appendInspectionRefusal(lines, outcome);
|
|
5607
5678
|
appendOutcomeReport(lines, result.outcomes);
|
|
@@ -5622,6 +5693,26 @@ function buildTextReport(result, policy, outcome) {
|
|
|
5622
5693
|
* @returns {void}
|
|
5623
5694
|
*/
|
|
5624
5695
|
function writeTextResult(result, policy, outcome) {
|
|
5696
|
+
if (outcome.failed) {
|
|
5697
|
+
// Emit the actual verdict BEFORE any detailed error, including refusals.
|
|
5698
|
+
const reasons = [
|
|
5699
|
+
...new Set([
|
|
5700
|
+
...outcome.blocking.map(violation => violation.kind),
|
|
5701
|
+
...(outcome.refusalBlocks ? [outcome.refusal.kind] : []),
|
|
5702
|
+
...(outcome.outcomesRefusal === null
|
|
5703
|
+
? []
|
|
5704
|
+
: [outcome.outcomesRefusal.kind]),
|
|
5705
|
+
...(!result.trust.trusted && !policy.warnOnly
|
|
5706
|
+
? ["required-checks snapshot untrusted"]
|
|
5707
|
+
: []),
|
|
5708
|
+
]),
|
|
5709
|
+
];
|
|
5710
|
+
const registration = result.vacuity?.registration;
|
|
5711
|
+
const summary = registration?.expired
|
|
5712
|
+
? `${registration.line} Blocking verdict: ${reasons.join(", ")}.`
|
|
5713
|
+
: `${outcome.blocking.length} blocking finding(s): ${reasons.join(", ")}.`;
|
|
5714
|
+
process.stderr.write(`::error title=Required checks verdict::${summary}\n`);
|
|
5715
|
+
}
|
|
5625
5716
|
const report = buildTextReport(result, policy, outcome);
|
|
5626
5717
|
process.stdout.write(report);
|
|
5627
5718
|
writeStepSummary(report);
|
|
@@ -208,11 +208,11 @@ jobs:
|
|
|
208
208
|
# without the install being broken, and a green run that inspected
|
|
209
209
|
# nothing is the exact defect being checked for.
|
|
210
210
|
if [ ! -f scripts/check-skipped-required-checks.mjs ]; then
|
|
211
|
-
|
|
211
|
+
printf '::%s title=%s::%s\n' error "Vacuity prover missing" "scripts/check-skipped-required-checks.mjs does not exist, so no required check was read on this pull request. Run \`lisa apply\` to install the Lisa-managed script."
|
|
212
212
|
exit 1
|
|
213
213
|
fi
|
|
214
214
|
if [ ! -f .github/required-checks.json ]; then
|
|
215
|
-
|
|
215
|
+
printf '::%s title=%s::%s\n' error "Required-checks declaration missing" ".github/required-checks.json does not exist, so nothing names which checks are supposed to carry review evidence. It is create-only and Lisa ships a seed — run \`lisa apply\` to get it."
|
|
216
216
|
exit 1
|
|
217
217
|
fi
|
|
218
218
|
if [ -n "${VACUITY_PR:-}" ]; then
|