From 59223f38c07f1b4c9a0496e5f6e7f88064710cd9 Mon Sep 17 00:00:00 2001 From: Vivswan Shah <58091053+Vivswan@users.noreply.github.com> Date: Thu, 10 Sep 2026 17:08:28 -0400 Subject: [PATCH 1/2] refactor(e2e): an excluded section predicts at no grade, so its consumers stop special-casing it In test/e2e/oracle.ts an excluded section (declared, but outside the sections allowlist) carried mask-derived grades, and three consumers compensated with allowed.has("excluded") checks: preflightDeniable, the fullyGranted fold in predictOutcomes, and writeDeniedSections through writeGranted. predictSectionAt, the owner PR #114 chose for the personal-account no-op, now mints the excluded prediction whole: grades [], allowed {excluded}, mayWrite false, before the mask is consulted. The section runs at no grade (orchestrate.ts filters it out of the preflight list and the section loop skips it before any read), so every fold over its grades is vacuous by construction: preflightDeniable finds no none grade, writeGranted is vacuously true, and fullyGranted and writeDeniedSections follow. The three consumer-side checks are deleted. Tests pin the whole SectionPrediction for an excluded plain section (beside its unrestricted control), an excluded org-only section on a personal account (exclusion wins), and an excluded NO_READ section in check mode, plus the whole RunPrediction for an excluded denied section beside an active one, with the same meta active as the negative control that predicts the preflight abort. The branch includes a merge of origin/main so it sits on top of PR #114's oracle fix. --- test/e2e/oracle.test.ts | 116 +++++++++++++++++++++++++++------------- test/e2e/oracle.ts | 50 ++++++++++------- 2 files changed, 109 insertions(+), 57 deletions(-) diff --git a/test/e2e/oracle.test.ts b/test/e2e/oracle.test.ts index 29800ad9..45565a39 100644 --- a/test/e2e/oracle.test.ts +++ b/test/e2e/oracle.test.ts @@ -462,10 +462,12 @@ describe("predictSection rules", () => { expect(p.mayWrite).toBe(false); }); - test("exclusion folds before grades and witnesses", () => { + test("exclusion folds before grades and witnesses: the section predicts at NO grade", () => { // A declared section outside the `sections` allowlist never runs: the // engine reports it "excluded" before any read, so neither the denied - // grade nor the seeded witness may tighten the prediction. + // grade nor the seeded witness may tighten the prediction, and the grades + // are empty - the section runs at no grade, so preflight and the + // write-granted fold are vacuous over it without recognizing "excluded". const p = predictSection( "labels", meta({ @@ -477,45 +479,85 @@ describe("predictSection rules", () => { liveKinds: { labels: "drift-update" }, }), ); - expect([...p.allowed]).toEqual(["excluded"]); - expect(p.mayWrite).toBe(false); - // An undefined allowlist keeps today's behavior: every section runs. - const unrestricted = predictSection("labels", meta({ mode: "check" })); - expect(unrestricted.allowed.has("excluded")).toBe(false); + expect(p).toEqual({ + key: "labels", + grades: [], + allowed: new Set(["excluded"]), + mayWrite: false, + }); + // An undefined allowlist keeps today's behavior: every section runs, and + // the denied grade with its 403 style reads as a check-mode failure. + const unrestricted = predictSection( + "labels", + meta({ mask: { issues: "none" }, denialStyle: 403, mode: "check" }), + ); + expect(unrestricted).toEqual({ + key: "labels", + grades: ["none"], + allowed: new Set(["failed"]), + mayWrite: false, + }); }); - test("an excluded section does not flip fullyGranted", () => { - // The fixpoint gates (converges / apply_idempotent) quantify over the - // sections that WILL run; a denied-but-excluded section must not block - // them. - const p = predictOutcomes( + test("an excluded NO_READ section in check mode is excluded, not the read-free clean", () => { + // check_suite_preferences makes no request in check mode and is otherwise + // exactly clean; exclusion folds before that rule too, and the read-free + // preflight exemption does not need to see the section since it has no grade. + const p = predictSection( + "check_suite_preferences", meta({ - sections: ["labels", "pages"], - onlySections: ["pages"], - mask: { issues: "none" }, - mode: "apply", - policy: "warn", + sections: ["check_suite_preferences", "labels"], + onlySections: ["labels"], + mask: { checks: "none" }, + mode: "check", }), ); - expect(p.fullyGranted).toBe(true); - expect(p.preflightAborts).toBe("no"); + expect(p).toEqual({ + key: "check_suite_preferences", + grades: [], + allowed: new Set(["excluded"]), + mayWrite: false, + }); }); - test("an excluded denied section never arms the preflight barrier", () => { - // Preflight probes only ACTIVE sections, so a permission-denied section - // that the allowlist excludes cannot abort the run. - const p = predictOutcomes( - meta({ - sections: ["labels"], - onlySections: ["pages"], - mask: { issues: "none" }, - denialStyle: 403, - mode: "apply", - policy: "fail", - }), - ); - expect(p.preflightAborts).toBe("no"); - expect([...p.allowedExitCodes]).toEqual([0]); + test("an excluded denied section beside an active one: the run follows the active one alone", () => { + // apply + fail + 403 with the excluded section's read denied: preflight + // probes only the active section, so the barrier never arms, the excluded + // section is not write-denied (it writes nothing), and the fixpoint gate + // (fullyGranted) quantifies over the section that WILL run. + const excludedDenied = meta({ + sections: ["labels", "pages"], + onlySections: ["pages"], + mask: { issues: "none" }, + denialStyle: 403, + mode: "apply", + policy: "fail", + }); + expect(predictOutcomes(excludedDenied)).toEqual({ + sections: [ + { key: "labels", grades: [], allowed: new Set(["excluded"]), mayWrite: false }, + { key: "pages", grades: ["write"], allowed: new Set(["applied"]), mayWrite: true }, + ], + allowedExitCodes: new Set([0]), + noWritesInCheck: false, + writeDeniedSections: [], + fullyGranted: true, + preflightAborts: "no", + }); + + // The control: the same meta with labels ACTIVE reaches the denied read, + // and the barrier aborts the run. + expect(predictOutcomes({ ...excludedDenied, onlySections: undefined })).toEqual({ + sections: [ + { key: "labels", grades: ["none"], allowed: new Set(["failed"]), mayWrite: false }, + { key: "pages", grades: ["write"], allowed: new Set(["applied"]), mayWrite: true }, + ], + allowedExitCodes: new Set([1]), + noWritesInCheck: false, + writeDeniedSections: ["labels"], + fullyGranted: false, + preflightAborts: "yes", + }); }); test("an EMPTY allowlist is unrestricted, mirroring the engine's size > 0 gate", () => { @@ -575,8 +617,8 @@ describe("predictSection rules", () => { test("exclusion folds before the personal-account no-op", () => { // Both folds precede the grades; an excluded org-only section on a user - // owner is excluded, not applied, and keeps the mask's grades like every - // other excluded section. + // owner is excluded, not applied, and like every other excluded section + // runs at no grade - neither the mask's denial nor the no-op's write grade. const p = predictSection( "teams", meta({ @@ -589,7 +631,7 @@ describe("predictSection rules", () => { ); expect(p).toEqual({ key: "teams", - grades: ["none"], + grades: [], allowed: new Set(["excluded"]), mayWrite: false, }); diff --git a/test/e2e/oracle.ts b/test/e2e/oracle.ts index bae1e7d7..39fcdfd7 100644 --- a/test/e2e/oracle.ts +++ b/test/e2e/oracle.ts @@ -150,7 +150,11 @@ export function effectiveGrades(grant: MaskGrade, gating: ReadGating): readonly /** The predicted set of outcomes a section may land in, given the run's shape. */ export interface SectionPrediction { key: SectionKey; - /** The grades the section may run at (effectiveGrades); each contributes to `allowed`. */ + /** + * The grades the section may run at (effectiveGrades); each contributes to + * `allowed`. EMPTY for an excluded section: it runs at no grade, so every + * fold over its grades (preflight, write-granted) is vacuous by construction. + */ grades: readonly MaskGrade[]; /** The outcomes the section is allowed to report; the runner must see one. */ allowed: Set; @@ -174,26 +178,30 @@ export function predictSection(key: SectionKey, meta: ScenarioMeta): SectionPred * Two folds precede the grades because they decide whether the mask is * consulted at all: exclusion (the section never runs) and the org-only * no-op on a personal account (the section runs, but never past its - * ungated org probe). + * ungated org probe). Each mints the whole prediction here, grades included, + * so no consumer has to recognize either case: preflight and the + * write-granted fold read the grades alone. */ export function predictSectionAt( key: SectionKey, meta: ScenarioMeta, gating: ReadGating, ): SectionPrediction { - const grant = sectionGrade(key, meta.mask, meta.orgMask ?? meta.mask); - const grades = effectiveGrades(grant, gating); // A declared section outside the `sections` allowlist never runs: the engine - // reports it "excluded" before any read (orchestrate.ts), so exclusion folds - // before EVERYTHING - grades, denial semantics, and witnesses alike. An - // EMPTY allowlist means unrestricted, mirroring orchestrate.ts's size > 0 - // gate, so only a non-empty list excludes. + // classifies it "excluded" before preflight and before any read + // (orchestrate.ts's disposition filter feeds both the preflight list and + // the section loop, which renders the excluded row), so exclusion folds + // before EVERYTHING - grades, denial semantics, and witnesses alike. It runs + // at NO grade: preflight probes nothing and no write happens, so its grades + // are empty and every fold over them is vacuously satisfied. An EMPTY + // allowlist means unrestricted, mirroring orchestrate.ts's size > 0 gate, + // so only a non-empty list excludes. if ( meta.onlySections !== undefined && meta.onlySections.length > 0 && !meta.onlySections.includes(key) ) { - return { key, grades, allowed: new Set(["excluded"]), mayWrite: false }; + return { key, grades: [], allowed: new Set(["excluded"]), mayWrite: false }; } // An org-only section on a personal account no-ops regardless of mask: its // org probe (declared permission "none", so no mask key gates it) 404s and @@ -211,6 +219,8 @@ export function predictSectionAt( mayWrite: false, }; } + const grant = sectionGrade(key, meta.mask, meta.orgMask ?? meta.mask); + const grades = effectiveGrades(grant, gating); const deniedAtGatedRead = gating === "mixed" && grant === "read"; const arms = grades.flatMap((grade) => grade === "none" && deniedAtGatedRead @@ -350,8 +360,9 @@ export interface RunPrediction { /** Sections whose denied writes must never mutate state (mock rule 4). */ writeDeniedSections: SectionKey[]; /** - * True when every ACTIVE section is write-granted (convergence expected). - * Excluded sections never run, so they do not count against this. + * True when every section is write-granted (convergence expected). An + * excluded section runs at no grade, so it is vacuously write-granted and + * the quantifier effectively ranges over the sections that WILL run. */ fullyGranted: boolean; /** @@ -416,7 +427,11 @@ function foldPreflightAbort(verdicts: readonly PreflightAbort[]): PreflightAbort return verdicts.includes("possible") ? "possible" : "no"; } -/** True when the section runs write-granted for certain (its only effective grade is write). */ +/** + * True when the section runs write-granted for certain (its only effective + * grade is write). Vacuously true for an excluded section, which runs at no + * grade: nothing it could do is denied. + */ function writeGranted(section: SectionPrediction): boolean { return section.grades.every((grade) => grade === "write"); } @@ -425,13 +440,10 @@ function writeGranted(section: SectionPrediction): boolean { * Whether preflight (reads only) denies the section: grade none and the denial * reads as a permission error (403 style, or "denied" semantics). Two effective * grades make it "possible": the probe reaches the gated read only for some content. + * An excluded section carries no grade at all, so it falls through to "no" like + * any section preflight has nothing to deny on. */ export function preflightDeniable(section: SectionPrediction, meta: ScenarioMeta): PreflightAbort { - // Preflight only probes ACTIVE sections (orchestrate.ts filters by the - // allowlist first), so an excluded section can never arm the barrier. - if (section.allowed.has("excluded")) { - return "no"; - } if (NO_READ_SECTIONS.has(section.key)) { // No read endpoints: preflight probes nothing, so the barrier cannot arm. return "no"; @@ -475,9 +487,7 @@ export function predictOutcomes(meta: ScenarioMeta): RunPrediction { allowedExitCodes: exitCodes, noWritesInCheck: check, writeDeniedSections: sections.filter((s) => !writeGranted(s) && !s.mayWrite).map((s) => s.key), - // Excluded sections never run, so they cannot break convergence or - // idempotence: fullyGranted quantifies over the sections that WILL run. - fullyGranted: sections.every((s) => s.allowed.has("excluded") || writeGranted(s)), + fullyGranted: sections.every(writeGranted), preflightAborts, }; } From fe8b50572bd3b5a9eac9f9d967d311d0c9f5ac01 Mon Sep 17 00:00:00 2001 From: Vivswan Shah <58091053+Vivswan@users.noreply.github.com> Date: Thu, 10 Sep 2026 17:57:53 -0400 Subject: [PATCH 2/2] refactor(e2e): state the no-grade rule once The previous commit said "runs at no grade, so every fold is vacuous" in six places in test/e2e/oracle.ts. The one statement now lives on SectionPrediction.grades, with the orchestrate.ts disposition cross-reference and the list of folds it makes vacuous; the predictSectionAt docs point there, and the fullyGranted, writeGranted, and preflightDeniable docs drop their local copies. writeGranted's doc now says "every effective grade is write", which is what the code computes for an empty grade list too. --- test/e2e/oracle.ts | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/test/e2e/oracle.ts b/test/e2e/oracle.ts index 39fcdfd7..6a587d05 100644 --- a/test/e2e/oracle.ts +++ b/test/e2e/oracle.ts @@ -152,8 +152,13 @@ export interface SectionPrediction { key: SectionKey; /** * The grades the section may run at (effectiveGrades); each contributes to - * `allowed`. EMPTY for an excluded section: it runs at no grade, so every - * fold over its grades (preflight, write-granted) is vacuous by construction. + * `allowed`. EMPTY for an excluded section (declared, but outside a + * non-empty `sections` allowlist): orchestrate.ts's disposition filter + * classifies it before the preflight list is built and before the section + * loop reads anything, so it runs at no grade, and every fold over these + * grades (preflightDeniable, writeGranted, and through them fullyGranted + * and writeDeniedSections) is vacuous for it by construction - no consumer + * recognizes "excluded" by hand. */ grades: readonly MaskGrade[]; /** The outcomes the section is allowed to report; the runner must see one. */ @@ -178,24 +183,17 @@ export function predictSection(key: SectionKey, meta: ScenarioMeta): SectionPred * Two folds precede the grades because they decide whether the mask is * consulted at all: exclusion (the section never runs) and the org-only * no-op on a personal account (the section runs, but never past its - * ungated org probe). Each mints the whole prediction here, grades included, - * so no consumer has to recognize either case: preflight and the - * write-granted fold read the grades alone. + * ungated org probe). Each mints the whole prediction here, grades included. */ export function predictSectionAt( key: SectionKey, meta: ScenarioMeta, gating: ReadGating, ): SectionPrediction { - // A declared section outside the `sections` allowlist never runs: the engine - // classifies it "excluded" before preflight and before any read - // (orchestrate.ts's disposition filter feeds both the preflight list and - // the section loop, which renders the excluded row), so exclusion folds - // before EVERYTHING - grades, denial semantics, and witnesses alike. It runs - // at NO grade: preflight probes nothing and no write happens, so its grades - // are empty and every fold over them is vacuously satisfied. An EMPTY - // allowlist means unrestricted, mirroring orchestrate.ts's size > 0 gate, - // so only a non-empty list excludes. + // Exclusion folds before EVERYTHING - grades, denial semantics, and + // witnesses alike (see SectionPrediction.grades). An EMPTY allowlist means + // unrestricted, mirroring orchestrate.ts's size > 0 gate, so only a + // non-empty list excludes. if ( meta.onlySections !== undefined && meta.onlySections.length > 0 && @@ -359,11 +357,7 @@ export interface RunPrediction { noWritesInCheck: boolean; /** Sections whose denied writes must never mutate state (mock rule 4). */ writeDeniedSections: SectionKey[]; - /** - * True when every section is write-granted (convergence expected). An - * excluded section runs at no grade, so it is vacuously write-granted and - * the quantifier effectively ranges over the sections that WILL run. - */ + /** True when every section is write-granted (convergence expected). */ fullyGranted: boolean; /** * Whether the run aborts at the preflight barrier (apply + fail policy, a denied @@ -427,11 +421,7 @@ function foldPreflightAbort(verdicts: readonly PreflightAbort[]): PreflightAbort return verdicts.includes("possible") ? "possible" : "no"; } -/** - * True when the section runs write-granted for certain (its only effective - * grade is write). Vacuously true for an excluded section, which runs at no - * grade: nothing it could do is denied. - */ +/** True when every effective grade is write: the section runs write-granted for certain. */ function writeGranted(section: SectionPrediction): boolean { return section.grades.every((grade) => grade === "write"); } @@ -440,8 +430,6 @@ function writeGranted(section: SectionPrediction): boolean { * Whether preflight (reads only) denies the section: grade none and the denial * reads as a permission error (403 style, or "denied" semantics). Two effective * grades make it "possible": the probe reaches the gated read only for some content. - * An excluded section carries no grade at all, so it falls through to "no" like - * any section preflight has nothing to deny on. */ export function preflightDeniable(section: SectionPrediction, meta: ScenarioMeta): PreflightAbort { if (NO_READ_SECTIONS.has(section.key)) {