@codyswann/lisa 4.87.11 → 4.87.12
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/review-evidence-event.mjs +202 -0
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +8 -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 +10 -5
- 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/.prettierignore +1 -0
- package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +52 -5
- package/typescript/create-only/.github/required-checks.json +1 -0
- package/typescript/create-only/.github/workflows/review-evidence.yml +43 -1
|
@@ -75,6 +75,7 @@ scripts/check-skipped-required-checks.mjs
|
|
|
75
75
|
scripts/check-state-classification.mjs
|
|
76
76
|
scripts/check-third-party-action-pins.mjs
|
|
77
77
|
scripts/check-third-party-review-evidence.mjs
|
|
78
|
+
scripts/review-evidence-event.mjs
|
|
78
79
|
scripts/check-threshold-ratchet.mjs
|
|
79
80
|
scripts/check-verification-coverage.mjs
|
|
80
81
|
scripts/check-workflow-load-failures.mjs
|
|
@@ -1033,9 +1033,48 @@ export function loadDeclaration(rootDir) {
|
|
|
1033
1033
|
assertEnforcementMode(declaration);
|
|
1034
1034
|
assertSkipJobDeclarations(declaration.skip_job_declarations);
|
|
1035
1035
|
assertEvidenceBearingChecks(declaration.evidence_bearing_checks ?? {});
|
|
1036
|
+
registrationContexts(declaration, {
|
|
1037
|
+
contexts: declaration.required_contexts,
|
|
1038
|
+
});
|
|
1036
1039
|
return declaration;
|
|
1037
1040
|
}
|
|
1038
1041
|
|
|
1042
|
+
/**
|
|
1043
|
+
* Validate reasoned registration deferrals against the set actually judged.
|
|
1044
|
+
* This only removes absence/registration observations. GitHub still requires
|
|
1045
|
+
* each deferred check to pass; evidence-bearing checks can never be deferred.
|
|
1046
|
+
* @param {object} declaration - Reviewed repository declaration.
|
|
1047
|
+
* @param {{contexts: string[]}} requiredSet - Selected base branch or union.
|
|
1048
|
+
* @returns {string[]} Contexts whose registration must still be observed.
|
|
1049
|
+
*/
|
|
1050
|
+
export function registrationContexts(declaration, requiredSet) {
|
|
1051
|
+
const deferred = declaration.registration_deferred;
|
|
1052
|
+
if (deferred === undefined) return requiredSet.contexts;
|
|
1053
|
+
if (
|
|
1054
|
+
deferred === null ||
|
|
1055
|
+
typeof deferred !== "object" ||
|
|
1056
|
+
Array.isArray(deferred)
|
|
1057
|
+
)
|
|
1058
|
+
throw new Error(
|
|
1059
|
+
"check-skipped-required-checks: `registration_deferred` must be an object mapping required check names to non-empty reasons."
|
|
1060
|
+
);
|
|
1061
|
+
for (const [name, reason] of Object.entries(deferred)) {
|
|
1062
|
+
if (typeof reason !== "string" || reason.trim() === "")
|
|
1063
|
+
throw new Error(
|
|
1064
|
+
`check-skipped-required-checks: registration_deferred entry ${JSON.stringify(name)} needs a non-empty reason.`
|
|
1065
|
+
);
|
|
1066
|
+
if (!requiredSet.contexts.includes(name))
|
|
1067
|
+
throw new Error(
|
|
1068
|
+
`check-skipped-required-checks: registration_deferred entry ${JSON.stringify(name)} is not in the selected required set. Refusing to judge this declaration.`
|
|
1069
|
+
);
|
|
1070
|
+
if (Object.hasOwn(declaration.evidence_bearing_checks ?? {}, name))
|
|
1071
|
+
throw new Error(
|
|
1072
|
+
`check-skipped-required-checks: registration_deferred entry ${JSON.stringify(name)} is evidence-bearing. Review evidence must still be waited for and judged.`
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
return requiredSet.contexts.filter(name => !Object.hasOwn(deferred, name));
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1039
1078
|
/**
|
|
1040
1079
|
* Refuses a declaration that omits one of the guard's load-bearing snapshots.
|
|
1041
1080
|
*
|
|
@@ -1781,7 +1820,7 @@ export function evaluateAbsentRequiredChecks(
|
|
|
1781
1820
|
declaration,
|
|
1782
1821
|
options.base ?? { base: null, source: null }
|
|
1783
1822
|
);
|
|
1784
|
-
const required = requiredSet
|
|
1823
|
+
const required = registrationContexts(declaration, requiredSet);
|
|
1785
1824
|
const judgedFor =
|
|
1786
1825
|
requiredSet.source === "branch"
|
|
1787
1826
|
? ` Judged against the contexts required on base \`${requiredSet.base}\`.`
|
|
@@ -4498,7 +4537,7 @@ export function fetchSettledChecks(declaration, pr, repo, options = {}) {
|
|
|
4498
4537
|
selectRequiredContexts(declaration, { base: null, source: null });
|
|
4499
4538
|
const selectSet = options.requiredSetFor ?? (() => defaultRequiredSet);
|
|
4500
4539
|
const missing = observed =>
|
|
4501
|
-
observed.requiredSet.
|
|
4540
|
+
registrationContexts(declaration, observed.requiredSet).filter(
|
|
4502
4541
|
name => !observed.checks.some(check => check.name === name)
|
|
4503
4542
|
);
|
|
4504
4543
|
// A substitute is useful only at the head it was actually read for. Pending
|
|
@@ -4522,6 +4561,7 @@ export function fetchSettledChecks(declaration, pr, repo, options = {}) {
|
|
|
4522
4561
|
const before = initial?.headSha === undefined ? observe(pr, repo) : initial;
|
|
4523
4562
|
initial = undefined;
|
|
4524
4563
|
const beforeSet = selectSet(before);
|
|
4564
|
+
registrationContexts(declaration, beforeSet);
|
|
4525
4565
|
if (before.headSha === undefined) {
|
|
4526
4566
|
return {
|
|
4527
4567
|
checks: [],
|
|
@@ -4536,6 +4576,7 @@ export function fetchSettledChecks(declaration, pr, repo, options = {}) {
|
|
|
4536
4576
|
const checks = read(pr, repo, before.headSha);
|
|
4537
4577
|
const after = observe(pr, repo);
|
|
4538
4578
|
const afterSet = selectSet(after);
|
|
4579
|
+
registrationContexts(declaration, afterSet);
|
|
4539
4580
|
return {
|
|
4540
4581
|
checks,
|
|
4541
4582
|
headSha: before.headSha,
|
|
@@ -4571,7 +4612,12 @@ export function fetchSettledChecks(declaration, pr, repo, options = {}) {
|
|
|
4571
4612
|
}
|
|
4572
4613
|
const elapsedSeconds = Math.max(0, clock() - started) / 1000;
|
|
4573
4614
|
const unregistered = missing(observed);
|
|
4574
|
-
const total = observed.requiredSet.
|
|
4615
|
+
const total = registrationContexts(declaration, observed.requiredSet).length;
|
|
4616
|
+
const deferred = Object.keys(declaration.registration_deferred ?? {});
|
|
4617
|
+
const deferralNote =
|
|
4618
|
+
deferred.length === 0
|
|
4619
|
+
? ""
|
|
4620
|
+
: ` (${deferred.length} deferred by declaration: ${deferred.join(", ")})`;
|
|
4575
4621
|
const present = total - unregistered.length;
|
|
4576
4622
|
const expired = unregistered.length > 0;
|
|
4577
4623
|
const settled = checksSettled(declaration, observed.checks);
|
|
@@ -4589,9 +4635,10 @@ export function fetchSettledChecks(declaration, pr, repo, options = {}) {
|
|
|
4589
4635
|
total,
|
|
4590
4636
|
missing: unregistered,
|
|
4591
4637
|
expired,
|
|
4638
|
+
...(deferred.length === 0 ? {} : { deferred }),
|
|
4592
4639
|
line: expired
|
|
4593
|
-
? `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.`
|
|
4594
|
-
: `Required registration settled after ${elapsedSeconds}s; ${present} of ${total} required contexts observed.`,
|
|
4640
|
+
? `Required registration wait EXPIRED: the gate stopped waiting after ${elapsedSeconds}s; ${present} of ${total} required contexts observed${deferralNote}; missing: ${unregistered.join(", ")}. RE-RUN this job before treating this as a finding about the change.`
|
|
4641
|
+
: `Required registration settled after ${elapsedSeconds}s; ${present} of ${total} required contexts observed${deferralNote}.`,
|
|
4595
4642
|
};
|
|
4596
4643
|
return {
|
|
4597
4644
|
checks: observed.checks,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_readme": [
|
|
3
|
+
"Optional `registration_deferred` (#4622) maps exact required context names to non-empty reasons when those jobs register after the bounded five-minute wait introduced by #4527. The registration/absence probe excludes only these names; GitHub still requires them to pass before merge. Entries outside the selected base branch's required set, malformed reasons, and evidence-bearing checks are refused. Omit the key for unchanged behavior. A slow reviewer is never deferred: the review-evidence workflow re-evaluates on its terminal check_run or status event, using default-branch code and a fresh current-head read. This workflow is create-only; existing hosts must adopt the event job explicitly to receive the wake-up behavior.",
|
|
3
4
|
"Two REVIEWED SNAPSHOTS that scripts/check-skipped-required-checks.mjs makes police each other.",
|
|
4
5
|
"Neither is derivable from this repository: `required_contexts` lives in a GitHub ruleset that humans edit in an admin console, and the skip_jobs token -> job name map lives in Lisa's quality.yml, which is not vendored here.",
|
|
5
6
|
"Why it matters: GitHub counts a SKIPPED required status check as SATISFIED. A job named in `skip_jobs` still reports green against its required context having run zero steps, so the merge gate can never be red and therefore can never block anything.",
|
|
@@ -122,6 +122,9 @@ name: 🕵️ Review Evidence
|
|
|
122
122
|
on:
|
|
123
123
|
pull_request:
|
|
124
124
|
types: [opened, synchronize, reopened, ready_for_review]
|
|
125
|
+
check_run:
|
|
126
|
+
types: [completed]
|
|
127
|
+
status:
|
|
125
128
|
workflow_dispatch:
|
|
126
129
|
inputs:
|
|
127
130
|
pr:
|
|
@@ -147,11 +150,50 @@ permissions:
|
|
|
147
150
|
pull-requests: read
|
|
148
151
|
|
|
149
152
|
concurrency:
|
|
150
|
-
group: review-evidence-${{ github.event.pull_request.number || inputs.pr || github.ref }}
|
|
153
|
+
group: review-evidence-${{ github.event_name }}-${{ github.event.pull_request.number || inputs.pr || github.event.check_run.head_sha || github.event.sha || github.ref }}-${{ github.event.check_run.name || github.event.context }}-${{ github.event.check_run.app.id || github.event.creator.id }}
|
|
151
154
|
cancel-in-progress: true
|
|
152
155
|
|
|
153
156
|
jobs:
|
|
157
|
+
# Reviewer completion can outlast the bounded pull_request run. Dispatch the
|
|
158
|
+
# normal gate; the event is a wake-up and never proof that a review occurred.
|
|
159
|
+
# check_run requires this file on the default branch and GitHub suppresses
|
|
160
|
+
# Actions-created suites/heads; status supports legacy reviewer statuses.
|
|
161
|
+
recheck:
|
|
162
|
+
name: 🕵️ Re-evaluate a completed reviewer
|
|
163
|
+
if: github.event_name == 'check_run' || (github.event_name == 'status' && github.event.state != 'pending')
|
|
164
|
+
runs-on: ubuntu-latest
|
|
165
|
+
timeout-minutes: 5
|
|
166
|
+
permissions:
|
|
167
|
+
contents: read
|
|
168
|
+
checks: read
|
|
169
|
+
statuses: read
|
|
170
|
+
pull-requests: read
|
|
171
|
+
actions: write
|
|
172
|
+
steps:
|
|
173
|
+
# No head ref: never execute PR files with the dispatch token.
|
|
174
|
+
- name: 📥 Checkout default branch
|
|
175
|
+
uses: actions/checkout@v6
|
|
176
|
+
with:
|
|
177
|
+
persist-credentials: false
|
|
178
|
+
- name: 🔧 Setup Node.js
|
|
179
|
+
uses: actions/setup-node@v6
|
|
180
|
+
with:
|
|
181
|
+
package-manager-cache: false
|
|
182
|
+
node-version: ${{ vars.NODE_VERSION || '24.21.0' }}
|
|
183
|
+
- name: 🕵️ Dispatch the gate for current declared reviewer heads
|
|
184
|
+
env:
|
|
185
|
+
GH_TOKEN: ${{ github.token }}
|
|
186
|
+
run: |
|
|
187
|
+
set -eu
|
|
188
|
+
script=scripts/review-evidence-event.mjs
|
|
189
|
+
if [ ! -f "$script" ]; then
|
|
190
|
+
echo "::error::Reviewer event resolver missing; no gate was dispatched."
|
|
191
|
+
exit 1
|
|
192
|
+
fi
|
|
193
|
+
node "$script" --dispatch
|
|
194
|
+
|
|
154
195
|
vacuity:
|
|
196
|
+
if: github.event_name != 'check_run' && github.event_name != 'status'
|
|
155
197
|
# DECLARATIVE, NOT A QUESTION (#3917). This was `🕵️ Did the required review
|
|
156
198
|
# checks do any work?`, and an unsatisfied verdict exits non-zero, so the
|
|
157
199
|
# row rendered `Did the required review checks do any work? fail`. A yes/no
|