@codyswann/lisa 4.8.2 → 4.8.4
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-gates.mjs +212 -3
- package/all/copy-overwrite/scripts/lisa-reconcile-policy.mjs +11 -0
- package/all/copy-overwrite/scripts/lisa-run-gates.mjs +232 -67
- package/dist/cli/gate-report-cells.d.ts +1 -1
- package/dist/cli/gate-report-cells.d.ts.map +1 -1
- package/dist/cli/gate-report-cells.js +18 -2
- package/dist/cli/gate-report-cells.js.map +1 -1
- package/dist/cli/gate-report-joins.d.ts.map +1 -1
- package/dist/cli/gate-report-joins.js +36 -4
- package/dist/cli/gate-report-joins.js.map +1 -1
- package/dist/cli/gate-report-registry.d.ts +30 -0
- package/dist/cli/gate-report-registry.d.ts.map +1 -1
- package/dist/cli/gate-report-registry.js +25 -0
- package/dist/cli/gate-report-registry.js.map +1 -1
- package/dist/core/gate-declaration-drift.d.ts +18 -0
- package/dist/core/gate-declaration-drift.d.ts.map +1 -1
- package/dist/core/gate-declaration-drift.js +30 -1
- package/dist/core/gate-declaration-drift.js.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +6 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- 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 +1 -1
- 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-doctor/SKILL.md +41 -0
- package/plugins/lisa/skills/lisa-doctor/SKILL.md +41 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-doctor/SKILL.md +41 -0
- 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/skills/lisa-doctor/SKILL.md +41 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/skills/lisa-doctor/SKILL.md +41 -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/skills/lisa-doctor/SKILL.md +41 -0
- package/scripts/lisa-github-rulesets.sh +15 -5
- package/scripts/lisa-ruleset-payload.mjs +30 -0
|
@@ -128,6 +128,22 @@ export const MOMENTS = [
|
|
|
128
128
|
/** Moment families that take an `:<environment>` suffix. */
|
|
129
129
|
export const MOMENT_FAMILIES = [PRE_DEPLOY, POST_DEPLOY, CONTINUOUS];
|
|
130
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Moments where nothing posts a status a pull request can be held on.
|
|
133
|
+
*
|
|
134
|
+
* These run on the developer's machine or on the agent's, before there is a
|
|
135
|
+
* pull request for a signal to attach to. Two declarations are refused at them
|
|
136
|
+
* for the same reason — `await`, which names a signal that could never fire,
|
|
137
|
+
* and `caller_chain`, which names the shape of a check-run name that is never
|
|
138
|
+
* posted.
|
|
139
|
+
*/
|
|
140
|
+
const NO_STATUS_MOMENTS = Object.freeze([
|
|
141
|
+
COMMIT,
|
|
142
|
+
PUSH,
|
|
143
|
+
SESSION_START,
|
|
144
|
+
...TOOL_MOMENTS,
|
|
145
|
+
]);
|
|
146
|
+
|
|
131
147
|
/**
|
|
132
148
|
* Moments that gate a *state* rather than a change.
|
|
133
149
|
*
|
|
@@ -149,6 +165,25 @@ export const STATE_FAMILIES = [CONTINUOUS];
|
|
|
149
165
|
/** Keys on a gate entry that are settings rather than moments. */
|
|
150
166
|
export const GATE_FIELDS = new Set(["run", "needs", "task"]);
|
|
151
167
|
|
|
168
|
+
/**
|
|
169
|
+
* The per-declaration field naming the job chain this gate's prover sits under.
|
|
170
|
+
*
|
|
171
|
+
* A gate whose prover lives OUTSIDE the quality facade cannot state its own
|
|
172
|
+
* context without it. The facade's contexts are derived by prefixing a gate's
|
|
173
|
+
* label with the chain of jobs that reach the facade, and that chain is a fact
|
|
174
|
+
* about the CALLER, not about the gate. When a project proves one property
|
|
175
|
+
* from a workflow of its own, the caller-wide chain describes a route that
|
|
176
|
+
* never reaches that gate's prover, and the derived name is one nothing posts.
|
|
177
|
+
*
|
|
178
|
+
* Declared per MOMENT, never once for the whole gate, because the chain is a
|
|
179
|
+
* property of one moment's wiring: the same job posts
|
|
180
|
+
* `<facade> / <label>` on the pull-request path and
|
|
181
|
+
* `Release / <facade> / <label>` on the release path. A gate-level value would
|
|
182
|
+
* assert one chain for every moment, which is the depth error this field
|
|
183
|
+
* exists to let a project correct.
|
|
184
|
+
*/
|
|
185
|
+
export const CALLER_CHAIN_FIELD = "caller_chain";
|
|
186
|
+
|
|
152
187
|
/**
|
|
153
188
|
* Registry flag: this gate's task may rewrite the working tree.
|
|
154
189
|
*
|
|
@@ -2792,6 +2827,18 @@ export const POLICY_SCHEMA = Object.freeze({
|
|
|
2792
2827
|
review: Object.freeze({
|
|
2793
2828
|
required_approving_review_count: "number",
|
|
2794
2829
|
require_code_owner_review: "boolean",
|
|
2830
|
+
// Declarable precisely because GitHub fills it in when a payload omits it,
|
|
2831
|
+
// and what it fills in is not "leave it alone". Measured against the live
|
|
2832
|
+
// API on 2026-08-25: a `pull_request` rule sent without the field came
|
|
2833
|
+
// back with it `true`; flipped to `false` explicitly it came back `false`;
|
|
2834
|
+
// sent again without it, it came back `true`. So an operator who wants it
|
|
2835
|
+
// OFF has no way to hold it off — every apply silently puts it back — and
|
|
2836
|
+
// an operator who wants it ON is relying on a GitHub default that can move
|
|
2837
|
+
// under them without any declaration recording the choice. Undeclared
|
|
2838
|
+
// still means undeclared: the generator omits the field entirely rather
|
|
2839
|
+
// than defaulting it, so a project that says nothing sends exactly the
|
|
2840
|
+
// payload it sent before this existed.
|
|
2841
|
+
require_extra_approval_for_unattributed_changes: "boolean",
|
|
2795
2842
|
}),
|
|
2796
2843
|
ruleset: Object.freeze({
|
|
2797
2844
|
enforcement: ENFORCEMENTS,
|
|
@@ -3698,6 +3745,23 @@ function validateGate(id, gate) {
|
|
|
3698
3745
|
// `pull_request` with an underscore reads as a configured gate and enables
|
|
3699
3746
|
// nothing at all. Same shape as a misspelled gate id, one level down.
|
|
3700
3747
|
if (!isMoment(moment)) {
|
|
3748
|
+
// Named before the generic typo message, because the generic one reads
|
|
3749
|
+
// as "this is not a moment" and sends the author looking for a moment
|
|
3750
|
+
// spelling. The mistake is real but different: the field exists, and it
|
|
3751
|
+
// belongs one level down.
|
|
3752
|
+
if (moment === CALLER_CHAIN_FIELD) {
|
|
3753
|
+
problems.push(
|
|
3754
|
+
`gates."${id}"."${CALLER_CHAIN_FIELD}" is declared for the whole ` +
|
|
3755
|
+
`gate, but the chain of jobs a check run is posted under is a ` +
|
|
3756
|
+
`property of ONE moment's wiring, not of the gate: the same job ` +
|
|
3757
|
+
`posts "<caller> ${CONTEXT_SEPARATOR.trim()} <label>" on the ` +
|
|
3758
|
+
`pull-request path and "Release ${CONTEXT_SEPARATOR.trim()} ` +
|
|
3759
|
+
`<caller> ${CONTEXT_SEPARATOR.trim()} <label>" on the release ` +
|
|
3760
|
+
`path. Declare it inside the moment whose context it names, e.g. ` +
|
|
3761
|
+
`gates."${id}"."${PULL_REQUEST}".${CALLER_CHAIN_FIELD}.`
|
|
3762
|
+
);
|
|
3763
|
+
continue;
|
|
3764
|
+
}
|
|
3701
3765
|
if (!GATE_FIELDS.has(moment)) {
|
|
3702
3766
|
problems.push(
|
|
3703
3767
|
`gates."${id}"."${moment}" ${unknownMomentMessage(moment)} ` +
|
|
@@ -3740,7 +3804,7 @@ function validateMoment(id, moment, value, known, interceptor, gateRun) {
|
|
|
3740
3804
|
);
|
|
3741
3805
|
}
|
|
3742
3806
|
if (entry.await) {
|
|
3743
|
-
if (
|
|
3807
|
+
if (NO_STATUS_MOMENTS.includes(moment)) {
|
|
3744
3808
|
problems.push(
|
|
3745
3809
|
`gates."${id}"."${moment}" awaits "${entry.await}", but there is no ` +
|
|
3746
3810
|
`pull request yet for a signal to post against. An awaited check ` +
|
|
@@ -3756,6 +3820,7 @@ function validateMoment(id, moment, value, known, interceptor, gateRun) {
|
|
|
3756
3820
|
problems.push(...validateEvidence(id, moment, entry.evidence ?? {}));
|
|
3757
3821
|
}
|
|
3758
3822
|
problems.push(...validatePostedBy(id, moment, entry));
|
|
3823
|
+
problems.push(...validateCallerChain(id, moment, entry));
|
|
3759
3824
|
if (!known && !interceptor && !entry.await && !entry.run && !gateRun) {
|
|
3760
3825
|
problems.push(
|
|
3761
3826
|
`gates."${id}"."${moment}" names no prover and Lisa has no default task ` +
|
|
@@ -3804,6 +3869,75 @@ function validatePostedBy(id, moment, entry) {
|
|
|
3804
3869
|
return [];
|
|
3805
3870
|
}
|
|
3806
3871
|
|
|
3872
|
+
/**
|
|
3873
|
+
* Validate a declaration's caller-chain override.
|
|
3874
|
+
*
|
|
3875
|
+
* Every arm refuses at DECLARATION time something that would otherwise fail at
|
|
3876
|
+
* merge time, invisibly. A required context nothing posts does not turn a pull
|
|
3877
|
+
* request red — GitHub holds it at "Expected — Waiting for status to be
|
|
3878
|
+
* reported" indefinitely, with no failing check to read and no log to open —
|
|
3879
|
+
* so a chain that derives such a name has to be caught by the only thing that
|
|
3880
|
+
* can still say anything about it, which is the validator.
|
|
3881
|
+
*
|
|
3882
|
+
* Three ways it can derive a name nothing posts, and each is refused:
|
|
3883
|
+
*
|
|
3884
|
+
* 1. On an `await`ing moment it is a no-op. An awaited signal posts under its
|
|
3885
|
+
* own name from wherever its app runs, and no chain prefixes it — so the
|
|
3886
|
+
* author who declared the override to fix a context would keep requiring
|
|
3887
|
+
* the unchanged one while believing it had been fixed.
|
|
3888
|
+
* 2. At a moment where nothing posts a status at all there is no check-run
|
|
3889
|
+
* name for the chain to shape.
|
|
3890
|
+
* 3. A blank, non-string or empty level derives `" / <label>"` or
|
|
3891
|
+
* `"<a> / / <label>"` — strings that read as plausible and that no run has
|
|
3892
|
+
* ever posted. `declaredCallerChain` is what refuses those, and it is the
|
|
3893
|
+
* same refusal `contextsFor` applies, called here so it lands with the
|
|
3894
|
+
* declaration rather than with the derivation.
|
|
3895
|
+
* @param {string} id Gate id.
|
|
3896
|
+
* @param {string} moment Moment key.
|
|
3897
|
+
* @param {object} entry The moment entry.
|
|
3898
|
+
* @returns {string[]} Problems.
|
|
3899
|
+
*/
|
|
3900
|
+
function validateCallerChain(id, moment, entry) {
|
|
3901
|
+
const value = entry[CALLER_CHAIN_FIELD];
|
|
3902
|
+
if (value === undefined) return [];
|
|
3903
|
+
const where = `gates."${id}"."${moment}".${CALLER_CHAIN_FIELD}`;
|
|
3904
|
+
|
|
3905
|
+
if (entry.await) {
|
|
3906
|
+
return [
|
|
3907
|
+
`${where} names the chain of jobs a check run is posted under, but ` +
|
|
3908
|
+
`this moment awaits "${entry.await}" — an awaited signal posts under ` +
|
|
3909
|
+
`its own name from wherever its app runs, and no chain prefixes it. ` +
|
|
3910
|
+
`The override would change nothing: the required context would still ` +
|
|
3911
|
+
`be "${entry.await}".`,
|
|
3912
|
+
];
|
|
3913
|
+
}
|
|
3914
|
+
if (NO_STATUS_MOMENTS.includes(moment)) {
|
|
3915
|
+
return [
|
|
3916
|
+
`${where} names the chain of jobs a check run is posted under, but ` +
|
|
3917
|
+
`nothing posts a check run at "${moment}" — it runs here, before ` +
|
|
3918
|
+
`there is a pull request for a status to attach to. Declare it at ` +
|
|
3919
|
+
`"${PULL_REQUEST}", which is the moment whose context it shapes.`,
|
|
3920
|
+
];
|
|
3921
|
+
}
|
|
3922
|
+
const levels = Array.isArray(value) ? value : [value];
|
|
3923
|
+
if (levels.some(level => typeof level !== "string")) {
|
|
3924
|
+
return [
|
|
3925
|
+
`${where} is ${JSON.stringify(value)}; expected the job names that ` +
|
|
3926
|
+
`reach this gate's prover, outermost first — an array of strings, or ` +
|
|
3927
|
+
`the same chain as one "${CONTEXT_SEPARATOR.trim()}"-joined string. ` +
|
|
3928
|
+
`Read them off a completed run rather than off the YAML: a check ` +
|
|
3929
|
+
`run's name is the chain of JOB names, and the workflow's own "name:" ` +
|
|
3930
|
+
`never appears in it.`,
|
|
3931
|
+
];
|
|
3932
|
+
}
|
|
3933
|
+
try {
|
|
3934
|
+
declaredCallerChain(value);
|
|
3935
|
+
} catch (error) {
|
|
3936
|
+
return [`${where} is ${JSON.stringify(value)}: ${error.message}`];
|
|
3937
|
+
}
|
|
3938
|
+
return [];
|
|
3939
|
+
}
|
|
3940
|
+
|
|
3807
3941
|
/**
|
|
3808
3942
|
* Validate an awaited signal's evidence block.
|
|
3809
3943
|
* @param {string} id Gate id.
|
|
@@ -4197,6 +4331,7 @@ export function resolveMoment({
|
|
|
4197
4331
|
work: null,
|
|
4198
4332
|
alias: null,
|
|
4199
4333
|
evidence: null,
|
|
4334
|
+
callerChain: null,
|
|
4200
4335
|
mayRewrite: false,
|
|
4201
4336
|
costly: false,
|
|
4202
4337
|
});
|
|
@@ -4240,6 +4375,14 @@ export function resolveMoment({
|
|
|
4240
4375
|
work: definition?.work ?? null,
|
|
4241
4376
|
alias: entry.await || intercepts ? null : alias,
|
|
4242
4377
|
evidence: entry.await ? mergeEvidence(entry.evidence) : null,
|
|
4378
|
+
// Carried RAW, exactly as declared. Normalising here would move the
|
|
4379
|
+
// blank-level refusal into a function whose caller swallows throws
|
|
4380
|
+
// (`resolveMergeMoment` in the drift comparator turns any throw into
|
|
4381
|
+
// "nothing resolved"), and a chain that fails silently is the failure
|
|
4382
|
+
// this whole area exists to make loud. `validateGates` refuses a
|
|
4383
|
+
// malformed chain at declaration time; `contextsFor` refuses it again at
|
|
4384
|
+
// derivation time.
|
|
4385
|
+
callerChain: entry.await ? null : (entry[CALLER_CHAIN_FIELD] ?? null),
|
|
4243
4386
|
mayRewrite: definition?.mayRewrite === true,
|
|
4244
4387
|
costly: definition?.costly === true,
|
|
4245
4388
|
});
|
|
@@ -4383,6 +4526,53 @@ function callerChainFrom({ callerChain, workflowName }) {
|
|
|
4383
4526
|
return chain;
|
|
4384
4527
|
}
|
|
4385
4528
|
|
|
4529
|
+
/**
|
|
4530
|
+
* Normalise the chain ONE declaration overrode the caller-wide chain with.
|
|
4531
|
+
*
|
|
4532
|
+
* Deliberately routed through `callerChainFrom` rather than used as written.
|
|
4533
|
+
* An override and a caller chain shape the same string, so they must be
|
|
4534
|
+
* normalised by the same code or the two would eventually disagree about
|
|
4535
|
+
* trimming, about the separator, and about what an empty level means. The
|
|
4536
|
+
* array form is the chain; the string form is the same chain `/`-joined, so
|
|
4537
|
+
* `"A / B"` and `["A", "B"]` are one declaration written two ways.
|
|
4538
|
+
*
|
|
4539
|
+
* It REPLACES the caller's chain rather than extending it. That is the whole
|
|
4540
|
+
* point: a project declares this precisely when the caller's chain does not
|
|
4541
|
+
* reach this gate's prover, so appending would derive a route that does not
|
|
4542
|
+
* exist. The label, the registry's `previousLabels` union and the blank-level
|
|
4543
|
+
* refusal are all still applied on top, so an override cannot smuggle in a raw
|
|
4544
|
+
* string that skips them.
|
|
4545
|
+
* @param {string[]|string} value The declared chain.
|
|
4546
|
+
* @returns {string[]} The validated chain, outermost first.
|
|
4547
|
+
*/
|
|
4548
|
+
function declaredCallerChain(value) {
|
|
4549
|
+
return Array.isArray(value)
|
|
4550
|
+
? callerChainFrom({ callerChain: value })
|
|
4551
|
+
: callerChainFrom({ workflowName: String(value) });
|
|
4552
|
+
}
|
|
4553
|
+
|
|
4554
|
+
/**
|
|
4555
|
+
* The prefix one declaration's derived context carries, from its own chain.
|
|
4556
|
+
*
|
|
4557
|
+
* Exported for the SECOND derivation of the same string. `contextsFor` is not
|
|
4558
|
+
* the only thing that builds `<chain> / <label>` — the declaration-drift
|
|
4559
|
+
* comparator and the gate report each build it too, from the same registry
|
|
4560
|
+
* label, and three copies of the joining rule is three chances for a rename to
|
|
4561
|
+
* land in two of them. They call this instead, so a chain that is refused in
|
|
4562
|
+
* one place is refused in all three.
|
|
4563
|
+
*
|
|
4564
|
+
* Throws rather than falling back for the same reason `callerChainFrom` does:
|
|
4565
|
+
* a prefix nobody can determine is not a prefix to guess at, because the guess
|
|
4566
|
+
* fails silently. A caller that cannot afford to throw must report that it did
|
|
4567
|
+
* not compare, never that the comparison was clean.
|
|
4568
|
+
* @param {string[]|string} chain The declared chain, or one `/`-joined string.
|
|
4569
|
+
* @returns {string} The prefix, ready to join with a label.
|
|
4570
|
+
* @throws {Error} When the chain has an empty level.
|
|
4571
|
+
*/
|
|
4572
|
+
export function callerPrefix(chain) {
|
|
4573
|
+
return declaredCallerChain(chain).join(CONTEXT_SEPARATOR);
|
|
4574
|
+
}
|
|
4575
|
+
|
|
4386
4576
|
/**
|
|
4387
4577
|
* The branch-protection contexts a gates block implies at one moment.
|
|
4388
4578
|
*
|
|
@@ -4401,6 +4591,16 @@ function callerChainFrom({ callerChain, workflowName }) {
|
|
|
4401
4591
|
* the release path posts, and passing the wrong depth is the permanent-pending
|
|
4402
4592
|
* trap in either direction. `verifyContextsPosted` closes that loop against a
|
|
4403
4593
|
* real run rather than leaving it to reasoning.
|
|
4594
|
+
*
|
|
4595
|
+
* One declaration may override that chain for itself with `caller_chain`, and
|
|
4596
|
+
* a declaration that does not carries on deriving exactly what it always did.
|
|
4597
|
+
* The override wins over the caller's chain for that gate alone, because it is
|
|
4598
|
+
* declared for precisely the case where the caller's chain does not reach the
|
|
4599
|
+
* gate's prover — a property proved by a workflow of the project's own rather
|
|
4600
|
+
* than through the quality facade. It changes only which chain is joined; the
|
|
4601
|
+
* label, the registry's `previousLabels` union and the blank-level refusal all
|
|
4602
|
+
* still apply, so an override cannot derive a name by a different route than
|
|
4603
|
+
* every other context.
|
|
4404
4604
|
* @param {object} gates The gates block.
|
|
4405
4605
|
* @param {object} [options] Context options.
|
|
4406
4606
|
* @param {string} [options.moment] The moment to derive for.
|
|
@@ -4426,6 +4626,15 @@ export function contextsFor(gates, options = {}) {
|
|
|
4426
4626
|
// chain of jobs that reach it.
|
|
4427
4627
|
.flatMap(gate => {
|
|
4428
4628
|
if (gate.mode === "await") return [gate.awaits];
|
|
4629
|
+
// The caller's chain, UNLESS this declaration named its own. A gate
|
|
4630
|
+
// proved outside the facade is not reached through the facade's callers,
|
|
4631
|
+
// so the caller-wide chain describes a route to it that does not exist.
|
|
4632
|
+
// Everything below this line is identical either way, which is what
|
|
4633
|
+
// keeps an override from becoming a second way to spell a context.
|
|
4634
|
+
const chain =
|
|
4635
|
+
gate.callerChain === null
|
|
4636
|
+
? prefix
|
|
4637
|
+
: declaredCallerChain(gate.callerChain).join(CONTEXT_SEPARATOR);
|
|
4429
4638
|
// The gate's OWN record of what it used to be called, unioned in
|
|
4430
4639
|
// automatically. This was a `--previous` flag the caller had to
|
|
4431
4640
|
// remember, which meant nothing knew a rename was in flight and a
|
|
@@ -4433,8 +4642,8 @@ export function contextsFor(gates, options = {}) {
|
|
|
4433
4642
|
// DECLARED once, in the registry, and every derivation sees it.
|
|
4434
4643
|
const former = REGISTRY[gate.id]?.previousLabels ?? [];
|
|
4435
4644
|
return [
|
|
4436
|
-
`${
|
|
4437
|
-
...former.map(label => `${
|
|
4645
|
+
`${chain}${CONTEXT_SEPARATOR}${gate.label}`,
|
|
4646
|
+
...former.map(label => `${chain}${CONTEXT_SEPARATOR}${label}`),
|
|
4438
4647
|
];
|
|
4439
4648
|
});
|
|
4440
4649
|
|
|
@@ -342,6 +342,10 @@ export const POLICY_SOURCES = Object.freeze({
|
|
|
342
342
|
surface: "ruleset",
|
|
343
343
|
field: "require_code_owner_review",
|
|
344
344
|
},
|
|
345
|
+
"review.require_extra_approval_for_unattributed_changes": {
|
|
346
|
+
surface: "ruleset",
|
|
347
|
+
field: "require_extra_approval_for_unattributed_changes",
|
|
348
|
+
},
|
|
345
349
|
"ruleset.enforcement": { surface: "ruleset", field: "enforcement" },
|
|
346
350
|
"ruleset.include_refs": { surface: "ruleset", field: "include_refs" },
|
|
347
351
|
"ruleset.exclude_refs": { surface: "ruleset", field: "exclude_refs" },
|
|
@@ -559,6 +563,13 @@ function rulesetShape(rulesets, policyRuleset) {
|
|
|
559
563
|
pullRequest?.parameters?.required_approving_review_count,
|
|
560
564
|
require_code_owner_review:
|
|
561
565
|
pullRequest?.parameters?.require_code_owner_review,
|
|
566
|
+
// Read as a SHAPE field, not as an OR across rulesets. GitHub fills this
|
|
567
|
+
// in on every ruleset carrying a `pull_request` rule, so ORing would
|
|
568
|
+
// report the strictest ruleset's value as the answer for the one Lisa
|
|
569
|
+
// generates, and a declared `false` on that ruleset would read as matched
|
|
570
|
+
// because some other ruleset had GitHub's default.
|
|
571
|
+
require_extra_approval_for_unattributed_changes:
|
|
572
|
+
pullRequest?.parameters?.require_extra_approval_for_unattributed_changes,
|
|
562
573
|
};
|
|
563
574
|
}
|
|
564
575
|
|