From acaee82abf54ffbe2f50bb50718438f59d33a3ee Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 12:27:32 +0900 Subject: [PATCH 01/13] test(operations): require exact workflow disablement plan --- test/workflow-registry-disable-plan.test.ts | 219 ++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 test/workflow-registry-disable-plan.test.ts diff --git a/test/workflow-registry-disable-plan.test.ts b/test/workflow-registry-disable-plan.test.ts new file mode 100644 index 000000000..8ac8bedc2 --- /dev/null +++ b/test/workflow-registry-disable-plan.test.ts @@ -0,0 +1,219 @@ +import { describe, expect, it, vi } from "vitest"; +import { + buildWorkflowDisablementPlan, + executeWorkflowDisablement, +} from "../scripts/workflow-registry-disable-plan.mjs"; + +const REPOSITORY = "ContextualWisdomLab/noema"; +const DEFAULT_BRANCH_SHA = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; +const ORPHAN = { + workflow_id: 410, + workflow_path: ".github/workflows/one-shot-old-repair.yml", + workflow_state: "active", + classification: "active_orphan", +}; + +function authoritativeAudit(overrides: Record = {}) { + return { + schema_version: 1, + repository_full_name: REPOSITORY, + default_branch_sha: DEFAULT_BRANCH_SHA, + observed_at: "2026-08-14T03:30:00.000Z", + pagination_receipts: [{ page: 1, itemCount: 1, hasNext: false }], + status: "FAIL", + failures: [ + { + code: "active_orphan_workflow", + workflow_id: ORPHAN.workflow_id, + detail: "bounded test orphan", + }, + ], + workflows: [ORPHAN], + ...overrides, + }; +} + +function matchingLiveRegistry() { + return [ + { + id: ORPHAN.workflow_id, + path: ORPHAN.workflow_path, + state: "active", + }, + ]; +} + +describe("workflow registry disablement planning", () => { + it("plans only exact active-orphan identities from authoritative evidence", () => { + const result = buildWorkflowDisablementPlan({ + audit: authoritativeAudit(), + expectedRepository: REPOSITORY, + expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, + liveWorkflows: matchingLiveRegistry(), + }); + + expect(result).toEqual({ + status: "PASS", + repository_full_name: REPOSITORY, + default_branch_sha: DEFAULT_BRANCH_SHA, + disablements: [ + { + workflow_id: ORPHAN.workflow_id, + workflow_path: ORPHAN.workflow_path, + expected_state: "active", + }, + ], + failures: [], + }); + }); + + it.each([ + { + name: "repository drift", + audit: authoritativeAudit({ repository_full_name: "ContextualWisdomLab/other" }), + expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, + liveWorkflows: matchingLiveRegistry(), + }, + { + name: "default-branch drift", + audit: authoritativeAudit(), + expectedDefaultBranchSha: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + liveWorkflows: matchingLiveRegistry(), + }, + { + name: "non-orphan audit failure", + audit: authoritativeAudit({ + failures: [ + ...authoritativeAudit().failures, + { code: "workflow_pagination_incomplete", detail: "missing page" }, + ], + }), + expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, + liveWorkflows: matchingLiveRegistry(), + }, + { + name: "live state changed", + audit: authoritativeAudit(), + expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, + liveWorkflows: [ + { + id: ORPHAN.workflow_id, + path: ORPHAN.workflow_path, + state: "disabled_manually", + }, + ], + }, + { + name: "live identity changed", + audit: authoritativeAudit(), + expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, + liveWorkflows: [ + { + id: ORPHAN.workflow_id, + path: ".github/workflows/current.yml", + state: "active", + }, + ], + }, + ])("fails closed on $name", ({ audit, expectedDefaultBranchSha, liveWorkflows }) => { + const result = buildWorkflowDisablementPlan({ + audit, + expectedRepository: REPOSITORY, + expectedDefaultBranchSha, + liveWorkflows, + }); + + expect(result.status).toBe("FAIL"); + expect(result.disablements).toEqual([]); + expect(result.failures.length).toBeGreaterThan(0); + }); +}); + +describe("single workflow disablement execution", () => { + it("revalidates the exact id, path, and active state immediately before disablement", async () => { + const plan = buildWorkflowDisablementPlan({ + audit: authoritativeAudit(), + expectedRepository: REPOSITORY, + expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, + liveWorkflows: matchingLiveRegistry(), + }); + const candidate = plan.disablements[0]!; + const revalidateWorkflow = vi.fn(async () => matchingLiveRegistry()[0]); + const disableWorkflow = vi.fn(async () => undefined); + + await expect( + executeWorkflowDisablement({ + plan, + candidate, + revalidateWorkflow, + disableWorkflow, + }), + ).resolves.toEqual({ + repository_full_name: REPOSITORY, + workflow_id: ORPHAN.workflow_id, + workflow_path: ORPHAN.workflow_path, + prior_state: "active", + mutation: "disable", + }); + + expect(revalidateWorkflow).toHaveBeenCalledWith({ + repository: REPOSITORY, + workflowId: ORPHAN.workflow_id, + }); + expect(disableWorkflow).toHaveBeenCalledWith({ + repository: REPOSITORY, + workflowId: ORPHAN.workflow_id, + }); + }); + + it("does not mutate when immediate revalidation no longer matches the plan", async () => { + const plan = buildWorkflowDisablementPlan({ + audit: authoritativeAudit(), + expectedRepository: REPOSITORY, + expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, + liveWorkflows: matchingLiveRegistry(), + }); + const candidate = plan.disablements[0]!; + const disableWorkflow = vi.fn(async () => undefined); + + await expect( + executeWorkflowDisablement({ + plan, + candidate, + revalidateWorkflow: async () => ({ + id: ORPHAN.workflow_id, + path: ORPHAN.workflow_path, + state: "disabled_manually", + }), + disableWorkflow, + }), + ).rejects.toThrow("workflow identity changed before disablement"); + + expect(disableWorkflow).not.toHaveBeenCalled(); + }); + + it("rejects candidates that are not part of a passing plan", async () => { + const plan = buildWorkflowDisablementPlan({ + audit: authoritativeAudit(), + expectedRepository: REPOSITORY, + expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, + liveWorkflows: matchingLiveRegistry(), + }); + const disableWorkflow = vi.fn(async () => undefined); + + await expect( + executeWorkflowDisablement({ + plan, + candidate: { + workflow_id: 999, + workflow_path: ".github/workflows/not-planned.yml", + expected_state: "active", + }, + revalidateWorkflow: async () => ({ id: 999, path: "x", state: "active" }), + disableWorkflow, + }), + ).rejects.toThrow("candidate is not part of the exact disablement plan"); + + expect(disableWorkflow).not.toHaveBeenCalled(); + }); +}); From d15d17b267ef04267a4f6e2c1d94d4070bceb7c9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 12:30:46 +0900 Subject: [PATCH 02/13] feat(operations): add exact orphan disablement planner --- scripts/workflow-registry-disable-plan.mjs | 161 +++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 scripts/workflow-registry-disable-plan.mjs diff --git a/scripts/workflow-registry-disable-plan.mjs b/scripts/workflow-registry-disable-plan.mjs new file mode 100644 index 000000000..f72108744 --- /dev/null +++ b/scripts/workflow-registry-disable-plan.mjs @@ -0,0 +1,161 @@ +const EXPECTED_REPOSITORY = "ContextualWisdomLab/noema"; +const LOWERCASE_SHA_40 = /^[0-9a-f]{40}$/; + +function failedPlan(repository, defaultBranchSha, code, detail) { + return { + status: "FAIL", + repository_full_name: repository ?? null, + default_branch_sha: defaultBranchSha ?? null, + disablements: [], + failures: [{ code, detail }], + }; +} + +/** + * Build a fail-closed disablement plan from one exact workflow-registry audit and + * an immediately refreshed live registry snapshot. Active-orphan findings are the + * only audit failures that can authorize a plan; every other failure invalidates it. + * + * @param {object} input exact audit, expected protected-main identity, and live registry + * @returns {object} bounded plan containing only exact active-orphan identities + */ +export function buildWorkflowDisablementPlan(input) { + const audit = input?.audit; + const repository = input?.expectedRepository; + const defaultBranchSha = input?.expectedDefaultBranchSha; + const liveWorkflows = Array.isArray(input?.liveWorkflows) ? input.liveWorkflows : []; + + if (repository !== EXPECTED_REPOSITORY || audit?.repository_full_name !== repository) { + return failedPlan( + repository, + defaultBranchSha, + "repository_identity_invalid", + `Workflow disablement evidence must be bound to exact repository ${EXPECTED_REPOSITORY}.`, + ); + } + + if ( + !LOWERCASE_SHA_40.test(defaultBranchSha ?? "") || + audit?.default_branch_sha !== defaultBranchSha + ) { + return failedPlan( + repository, + defaultBranchSha, + "default_branch_identity_changed", + "Workflow disablement evidence is not bound to the exact protected-main commit.", + ); + } + + if ( + !Array.isArray(audit?.failures) || + !Array.isArray(audit?.workflows) || + !Array.isArray(input?.liveWorkflows) + ) { + return failedPlan( + repository, + defaultBranchSha, + "disablement_evidence_invalid", + "Workflow disablement planning requires complete audit failures, workflow records, and live registry records.", + ); + } + + if (audit.failures.some((failure) => failure?.code !== "active_orphan_workflow")) { + return failedPlan( + repository, + defaultBranchSha, + "disablement_audit_not_authoritative", + "Workflow disablement is blocked while the registry audit contains a non-orphan failure.", + ); + } + + const candidates = audit.workflows.filter( + (workflow) => workflow?.classification === "active_orphan", + ); + const disablements = []; + + for (const candidate of candidates) { + const matches = liveWorkflows.filter((workflow) => workflow?.id === candidate?.workflow_id); + const live = matches.length === 1 ? matches[0] : null; + if ( + !Number.isSafeInteger(candidate?.workflow_id) || + candidate.workflow_id <= 0 || + typeof candidate?.workflow_path !== "string" || + !candidate.workflow_path.startsWith(".github/workflows/") || + candidate.workflow_state !== "active" || + live?.path !== candidate.workflow_path || + live?.state !== "active" + ) { + return failedPlan( + repository, + defaultBranchSha, + "workflow_identity_changed", + "An audited active-orphan workflow no longer has one exact active live registry identity.", + ); + } + + disablements.push({ + workflow_id: candidate.workflow_id, + workflow_path: candidate.workflow_path, + expected_state: "active", + }); + } + + disablements.sort((left, right) => left.workflow_id - right.workflow_id); + return { + status: "PASS", + repository_full_name: repository, + default_branch_sha: defaultBranchSha, + disablements, + failures: [], + }; +} + +/** + * Disable exactly one candidate after revalidating its live workflow ID, path, and + * state. Callers supply the authorized mutation primitive; this module never owns + * credentials, transport, or batch mutation authority. + * + * @param {object} input passing plan, candidate, live reader, and disable callback + * @returns {Promise} immutable description of the single completed mutation + */ +export async function executeWorkflowDisablement(input) { + const plan = input?.plan; + const candidate = input?.candidate; + const planned = Array.isArray(plan?.disablements) + ? plan.disablements.find( + (item) => + item.workflow_id === candidate?.workflow_id && + item.workflow_path === candidate?.workflow_path && + item.expected_state === "active", + ) + : undefined; + + if (plan?.status !== "PASS" || !planned) { + throw new Error("candidate is not part of the exact disablement plan"); + } + + const live = await input.revalidateWorkflow({ + repository: plan.repository_full_name, + workflowId: planned.workflow_id, + }); + if ( + live?.id !== planned.workflow_id || + live?.path !== planned.workflow_path || + live?.state !== "active" + ) { + throw new Error("workflow identity changed before disablement"); + } + + await input.disableWorkflow({ + repository: plan.repository_full_name, + workflowId: planned.workflow_id, + }); + + return { + repository_full_name: plan.repository_full_name, + workflow_id: planned.workflow_id, + workflow_path: planned.workflow_path, + prior_state: "active", + mutation: "disable", + }; +} From 33d89f480856c6b93b019af75e8827110462f21d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 12:31:09 +0900 Subject: [PATCH 03/13] test(coverage): include workflow disablement planner --- vitest.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/vitest.config.ts b/vitest.config.ts index fef24afa4..1109f0f35 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -10,6 +10,7 @@ export default defineConfig({ "scripts/normalize-commercial-readiness-evidence.mjs", "scripts/prepare-agent-pr-message.mjs", "scripts/workflow-registry-audit.mjs", + "scripts/workflow-registry-disable-plan.mjs", "scripts/lib/external-scheduler-evidence-audit.mjs", "scripts/external-scheduler-evidence-audit.mjs", ], From cb480504bb826ad69b2faa913e35aa2f02dcf743 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 12:33:34 +0900 Subject: [PATCH 04/13] test(operations): harden disablement evidence invariants --- test/workflow-registry-disable-plan.test.ts | 281 ++++++++++++++------ 1 file changed, 199 insertions(+), 82 deletions(-) diff --git a/test/workflow-registry-disable-plan.test.ts b/test/workflow-registry-disable-plan.test.ts index 8ac8bedc2..44620fe5c 100644 --- a/test/workflow-registry-disable-plan.test.ts +++ b/test/workflow-registry-disable-plan.test.ts @@ -43,16 +43,19 @@ function matchingLiveRegistry() { ]; } +function plan(overrides: Record = {}) { + return buildWorkflowDisablementPlan({ + audit: authoritativeAudit(), + expectedRepository: REPOSITORY, + expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, + liveWorkflows: matchingLiveRegistry(), + ...overrides, + }); +} + describe("workflow registry disablement planning", () => { it("plans only exact active-orphan identities from authoritative evidence", () => { - const result = buildWorkflowDisablementPlan({ - audit: authoritativeAudit(), - expectedRepository: REPOSITORY, - expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, - liveWorkflows: matchingLiveRegistry(), - }); - - expect(result).toEqual({ + expect(plan()).toEqual({ status: "PASS", repository_full_name: REPOSITORY, default_branch_sha: DEFAULT_BRANCH_SHA, @@ -70,80 +73,162 @@ describe("workflow registry disablement planning", () => { it.each([ { name: "repository drift", - audit: authoritativeAudit({ repository_full_name: "ContextualWisdomLab/other" }), - expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, - liveWorkflows: matchingLiveRegistry(), + overrides: { + audit: authoritativeAudit({ repository_full_name: "ContextualWisdomLab/other" }), + }, + }, + { + name: "unsupported expected repository", + overrides: { + expectedRepository: "ContextualWisdomLab/other", + audit: authoritativeAudit({ repository_full_name: "ContextualWisdomLab/other" }), + }, }, { name: "default-branch drift", - audit: authoritativeAudit(), - expectedDefaultBranchSha: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - liveWorkflows: matchingLiveRegistry(), + overrides: { + expectedDefaultBranchSha: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + }, + }, + { + name: "invalid default-branch identity", + overrides: { expectedDefaultBranchSha: "not-a-sha" }, + }, + { + name: "missing audit failures", + overrides: { audit: authoritativeAudit({ failures: undefined }) }, + }, + { + name: "missing audit workflows", + overrides: { audit: authoritativeAudit({ workflows: undefined }) }, + }, + { + name: "missing live registry", + overrides: { liveWorkflows: undefined }, }, { name: "non-orphan audit failure", - audit: authoritativeAudit({ - failures: [ - ...authoritativeAudit().failures, - { code: "workflow_pagination_incomplete", detail: "missing page" }, - ], - }), - expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, - liveWorkflows: matchingLiveRegistry(), + overrides: { + audit: authoritativeAudit({ + failures: [ + ...authoritativeAudit().failures, + { code: "workflow_pagination_incomplete", detail: "missing page" }, + ], + }), + }, }, { name: "live state changed", - audit: authoritativeAudit(), - expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, - liveWorkflows: [ - { - id: ORPHAN.workflow_id, - path: ORPHAN.workflow_path, - state: "disabled_manually", - }, - ], + overrides: { + liveWorkflows: [ + { + id: ORPHAN.workflow_id, + path: ORPHAN.workflow_path, + state: "disabled_manually", + }, + ], + }, }, { name: "live identity changed", - audit: authoritativeAudit(), - expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, - liveWorkflows: [ - { - id: ORPHAN.workflow_id, - path: ".github/workflows/current.yml", - state: "active", - }, - ], + overrides: { + liveWorkflows: [ + { + id: ORPHAN.workflow_id, + path: ".github/workflows/current.yml", + state: "active", + }, + ], + }, + }, + { + name: "live identity missing", + overrides: { liveWorkflows: [] }, }, - ])("fails closed on $name", ({ audit, expectedDefaultBranchSha, liveWorkflows }) => { - const result = buildWorkflowDisablementPlan({ - audit, - expectedRepository: REPOSITORY, - expectedDefaultBranchSha, - liveWorkflows, + { + name: "duplicate live workflow id", + overrides: { liveWorkflows: [...matchingLiveRegistry(), ...matchingLiveRegistry()] }, + }, + { + name: "live path is reused by another workflow id", + overrides: { + liveWorkflows: [ + ...matchingLiveRegistry(), + { id: 411, path: ORPHAN.workflow_path, state: "active" }, + ], + }, + }, + ])("fails closed on $name", ({ overrides }) => { + const result = plan(overrides); + expect(result.status).toBe("FAIL"); + expect(result.disablements).toEqual([]); + expect(result.failures.length).toBeGreaterThan(0); + }); + + it.each([ + { name: "unsafe workflow id", workflow: { ...ORPHAN, workflow_id: 0 } }, + { name: "non-string workflow path", workflow: { ...ORPHAN, workflow_path: 123 } }, + { name: "out-of-scope workflow path", workflow: { ...ORPHAN, workflow_path: "README.md" } }, + { name: "non-active audit state", workflow: { ...ORPHAN, workflow_state: "disabled_manually" } }, + ])("rejects malformed active-orphan evidence: $name", ({ workflow }) => { + const result = plan({ + audit: authoritativeAudit({ workflows: [workflow] }), }); + expect(result.status).toBe("FAIL"); + expect(result.disablements).toEqual([]); + }); + it("rejects an active-orphan candidate without its matching audit failure", () => { + const result = plan({ audit: authoritativeAudit({ failures: [] }) }); expect(result.status).toBe("FAIL"); expect(result.disablements).toEqual([]); - expect(result.failures.length).toBeGreaterThan(0); + }); + + it("rejects an active-orphan failure that has no candidate workflow", () => { + const result = plan({ audit: authoritativeAudit({ workflows: [] }) }); + expect(result.status).toBe("FAIL"); + expect(result.disablements).toEqual([]); + }); + + it("sorts a multi-orphan plan deterministically by workflow id", () => { + const second = { + workflow_id: 409, + workflow_path: ".github/workflows/older-one-shot.yml", + workflow_state: "active", + classification: "active_orphan", + }; + const result = plan({ + audit: authoritativeAudit({ + workflows: [ORPHAN, second], + failures: [ + { code: "active_orphan_workflow", workflow_id: ORPHAN.workflow_id }, + { code: "active_orphan_workflow", workflow_id: second.workflow_id }, + ], + }), + liveWorkflows: [ + ...matchingLiveRegistry(), + { id: second.workflow_id, path: second.workflow_path, state: "active" }, + ], + }); + + expect(result.status).toBe("PASS"); + expect(result.disablements.map((item: { workflow_id: number }) => item.workflow_id)).toEqual([ + 409, + 410, + ]); }); }); describe("single workflow disablement execution", () => { it("revalidates the exact id, path, and active state immediately before disablement", async () => { - const plan = buildWorkflowDisablementPlan({ - audit: authoritativeAudit(), - expectedRepository: REPOSITORY, - expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, - liveWorkflows: matchingLiveRegistry(), - }); - const candidate = plan.disablements[0]!; + const currentPlan = plan(); + const candidate = currentPlan.disablements[0]!; const revalidateWorkflow = vi.fn(async () => matchingLiveRegistry()[0]); const disableWorkflow = vi.fn(async () => undefined); await expect( executeWorkflowDisablement({ - plan, + plan: currentPlan, candidate, revalidateWorkflow, disableWorkflow, @@ -166,25 +251,26 @@ describe("single workflow disablement execution", () => { }); }); - it("does not mutate when immediate revalidation no longer matches the plan", async () => { - const plan = buildWorkflowDisablementPlan({ - audit: authoritativeAudit(), - expectedRepository: REPOSITORY, - expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, - liveWorkflows: matchingLiveRegistry(), - }); - const candidate = plan.disablements[0]!; + it.each([ + { name: "id changed", live: { id: 999, path: ORPHAN.workflow_path, state: "active" } }, + { + name: "path changed", + live: { id: ORPHAN.workflow_id, path: ".github/workflows/current.yml", state: "active" }, + }, + { + name: "state changed", + live: { id: ORPHAN.workflow_id, path: ORPHAN.workflow_path, state: "disabled_manually" }, + }, + ])("does not mutate when immediate revalidation has $name", async ({ live }) => { + const currentPlan = plan(); + const candidate = currentPlan.disablements[0]!; const disableWorkflow = vi.fn(async () => undefined); await expect( executeWorkflowDisablement({ - plan, + plan: currentPlan, candidate, - revalidateWorkflow: async () => ({ - id: ORPHAN.workflow_id, - path: ORPHAN.workflow_path, - state: "disabled_manually", - }), + revalidateWorkflow: async () => live, disableWorkflow, }), ).rejects.toThrow("workflow identity changed before disablement"); @@ -192,28 +278,59 @@ describe("single workflow disablement execution", () => { expect(disableWorkflow).not.toHaveBeenCalled(); }); - it("rejects candidates that are not part of a passing plan", async () => { - const plan = buildWorkflowDisablementPlan({ - audit: authoritativeAudit(), - expectedRepository: REPOSITORY, - expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, - liveWorkflows: matchingLiveRegistry(), - }); + it.each([ + { + name: "unplanned workflow id", + candidate: { + workflow_id: 999, + workflow_path: ORPHAN.workflow_path, + expected_state: "active", + }, + }, + { + name: "unplanned workflow path", + candidate: { + workflow_id: ORPHAN.workflow_id, + workflow_path: ".github/workflows/not-planned.yml", + expected_state: "active", + }, + }, + { + name: "unplanned expected state", + candidate: { + workflow_id: ORPHAN.workflow_id, + workflow_path: ORPHAN.workflow_path, + expected_state: "disabled_manually", + }, + }, + ])("rejects $name", async ({ candidate }) => { const disableWorkflow = vi.fn(async () => undefined); + await expect( + executeWorkflowDisablement({ + plan: plan(), + candidate, + revalidateWorkflow: async () => matchingLiveRegistry()[0], + disableWorkflow, + }), + ).rejects.toThrow("candidate is not part of the exact disablement plan"); + expect(disableWorkflow).not.toHaveBeenCalled(); + }); + it("rejects candidates from a non-passing plan", async () => { + const failed = plan({ expectedRepository: "ContextualWisdomLab/other" }); + const disableWorkflow = vi.fn(async () => undefined); await expect( executeWorkflowDisablement({ - plan, + plan: failed, candidate: { - workflow_id: 999, - workflow_path: ".github/workflows/not-planned.yml", + workflow_id: ORPHAN.workflow_id, + workflow_path: ORPHAN.workflow_path, expected_state: "active", }, - revalidateWorkflow: async () => ({ id: 999, path: "x", state: "active" }), + revalidateWorkflow: async () => matchingLiveRegistry()[0], disableWorkflow, }), ).rejects.toThrow("candidate is not part of the exact disablement plan"); - expect(disableWorkflow).not.toHaveBeenCalled(); }); -}); +}); \ No newline at end of file From 87f7b5af67f34e2f5a58a3cb74882a22494f8d33 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 12:36:32 +0900 Subject: [PATCH 05/13] feat(operations): close orphan disablement evidence gaps --- scripts/workflow-registry-disable-plan.mjs | 60 ++++++++++++++++++---- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/scripts/workflow-registry-disable-plan.mjs b/scripts/workflow-registry-disable-plan.mjs index f72108744..7ece15cdf 100644 --- a/scripts/workflow-registry-disable-plan.mjs +++ b/scripts/workflow-registry-disable-plan.mjs @@ -71,25 +71,63 @@ export function buildWorkflowDisablementPlan(input) { const candidates = audit.workflows.filter( (workflow) => workflow?.classification === "active_orphan", ); - const disablements = []; + const candidateIds = candidates.map((workflow) => workflow?.workflow_id).sort(); + const failureIds = audit.failures.map((failure) => failure?.workflow_id).sort(); + if (JSON.stringify(candidateIds) !== JSON.stringify(failureIds)) { + return failedPlan( + repository, + defaultBranchSha, + "active_orphan_evidence_inconsistent", + "Every active-orphan workflow must have exactly one matching active-orphan audit failure and vice versa.", + ); + } + const liveIds = liveWorkflows.map((workflow) => workflow?.id); + if (new Set(liveIds).size !== liveIds.length) { + return failedPlan( + repository, + defaultBranchSha, + "workflow_identity_changed", + "The live workflow registry contains a duplicate workflow ID.", + ); + } + const livePaths = liveWorkflows.map((workflow) => workflow?.path); + if (new Set(livePaths).size !== livePaths.length) { + return failedPlan( + repository, + defaultBranchSha, + "workflow_identity_changed", + "The live workflow registry contains a reused workflow path.", + ); + } + + const disablements = []; for (const candidate of candidates) { - const matches = liveWorkflows.filter((workflow) => workflow?.id === candidate?.workflow_id); - const live = matches.length === 1 ? matches[0] : null; if ( !Number.isSafeInteger(candidate?.workflow_id) || candidate.workflow_id <= 0 || typeof candidate?.workflow_path !== "string" || !candidate.workflow_path.startsWith(".github/workflows/") || - candidate.workflow_state !== "active" || - live?.path !== candidate.workflow_path || - live?.state !== "active" + candidate.workflow_state !== "active" ) { return failedPlan( repository, defaultBranchSha, "workflow_identity_changed", - "An audited active-orphan workflow no longer has one exact active live registry identity.", + "An audited active-orphan workflow does not have a safe exact identity.", + ); + } + + const live = liveWorkflows.find( + (workflow) => + workflow?.id === candidate.workflow_id && workflow?.path === candidate.workflow_path, + ); + if (!live || live.state !== "active") { + return failedPlan( + repository, + defaultBranchSha, + "workflow_identity_changed", + "An audited active-orphan workflow no longer has the exact active live registry identity.", ); } @@ -121,11 +159,15 @@ export function buildWorkflowDisablementPlan(input) { export async function executeWorkflowDisablement(input) { const plan = input?.plan; const candidate = input?.candidate; + if (candidate?.expected_state !== "active") { + throw new Error("candidate is not part of the exact disablement plan"); + } + const planned = Array.isArray(plan?.disablements) ? plan.disablements.find( (item) => - item.workflow_id === candidate?.workflow_id && - item.workflow_path === candidate?.workflow_path && + item.workflow_id === candidate.workflow_id && + item.workflow_path === candidate.workflow_path && item.expected_state === "active", ) : undefined; From e4d9792a7e10eb910c68ac7d1b2f1505942ac8d7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 12:38:38 +0900 Subject: [PATCH 06/13] test(coverage): exercise fail-closed planner boundaries --- test/workflow-registry-disable-plan.test.ts | 34 +++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/workflow-registry-disable-plan.test.ts b/test/workflow-registry-disable-plan.test.ts index 44620fe5c..e799ccdd5 100644 --- a/test/workflow-registry-disable-plan.test.ts +++ b/test/workflow-registry-disable-plan.test.ts @@ -70,6 +70,15 @@ describe("workflow registry disablement planning", () => { }); }); + it("fails closed with null evidence identities when planning input is absent", () => { + const result = buildWorkflowDisablementPlan(undefined); + expect(result.status).toBe("FAIL"); + expect(result.repository_full_name).toBeNull(); + expect(result.default_branch_sha).toBeNull(); + expect(result.disablements).toEqual([]); + expect(result.failures[0]?.code).toBe("repository_identity_invalid"); + }); + it.each([ { name: "repository drift", @@ -90,6 +99,10 @@ describe("workflow registry disablement planning", () => { expectedDefaultBranchSha: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", }, }, + { + name: "missing default-branch identity", + overrides: { expectedDefaultBranchSha: undefined }, + }, { name: "invalid default-branch identity", overrides: { expectedDefaultBranchSha: "not-a-sha" }, @@ -333,4 +346,25 @@ describe("single workflow disablement execution", () => { ).rejects.toThrow("candidate is not part of the exact disablement plan"); expect(disableWorkflow).not.toHaveBeenCalled(); }); + + it("rejects a malformed passing plan that has no disablement inventory", async () => { + const disableWorkflow = vi.fn(async () => undefined); + await expect( + executeWorkflowDisablement({ + plan: { + status: "PASS", + repository_full_name: REPOSITORY, + default_branch_sha: DEFAULT_BRANCH_SHA, + }, + candidate: { + workflow_id: ORPHAN.workflow_id, + workflow_path: ORPHAN.workflow_path, + expected_state: "active", + }, + revalidateWorkflow: async () => matchingLiveRegistry()[0], + disableWorkflow, + }), + ).rejects.toThrow("candidate is not part of the exact disablement plan"); + expect(disableWorkflow).not.toHaveBeenCalled(); + }); }); \ No newline at end of file From 5ca590fd1b890452390c9bf5d1ba4a516300a3ab Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 17:38:08 +0900 Subject: [PATCH 07/13] test(operations): reject forged workflow disablement authority --- ...ow-registry-disable-plan-hardening.test.ts | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 test/workflow-registry-disable-plan-hardening.test.ts diff --git a/test/workflow-registry-disable-plan-hardening.test.ts b/test/workflow-registry-disable-plan-hardening.test.ts new file mode 100644 index 000000000..d64ad5779 --- /dev/null +++ b/test/workflow-registry-disable-plan-hardening.test.ts @@ -0,0 +1,162 @@ +import { describe, expect, it, vi } from "vitest"; +import { + buildWorkflowDisablementPlan, + executeWorkflowDisablement, +} from "../scripts/workflow-registry-disable-plan.mjs"; + +const REPOSITORY = "ContextualWisdomLab/noema"; +const DEFAULT_BRANCH_SHA = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; +const ORPHAN = { + workflow_id: 410, + workflow_path: ".github/workflows/one-shot-old-repair.yml", + workflow_state: "active", + classification: "active_orphan", +}; + +function audit(overrides: Record = {}) { + return { + schema_version: 1, + repository_full_name: REPOSITORY, + default_branch_sha: DEFAULT_BRANCH_SHA, + observed_at: "2026-08-14T03:30:00.000Z", + pagination_receipts: [{ page: 1, itemCount: 1, hasNext: false }], + status: "FAIL", + failures: [ + { + code: "active_orphan_workflow", + workflow_id: ORPHAN.workflow_id, + detail: "bounded test orphan", + }, + ], + workflows: [ORPHAN], + ...overrides, + }; +} + +function build(overrides: Record = {}) { + return buildWorkflowDisablementPlan({ + audit: audit(), + expectedRepository: REPOSITORY, + expectedDefaultBranchSha: DEFAULT_BRANCH_SHA, + liveWorkflows: [ + { + id: ORPHAN.workflow_id, + path: ORPHAN.workflow_path, + state: "active", + }, + ], + ...overrides, + }); +} + +describe("workflow disablement authority hardening", () => { + it.each([ + { name: "missing schema version", audit: audit({ schema_version: undefined }) }, + { name: "unsupported schema version", audit: audit({ schema_version: 2 }) }, + { name: "non-failing audit status", audit: audit({ status: "PASS" }) }, + { name: "missing observation time", audit: audit({ observed_at: undefined }) }, + { name: "invalid observation time", audit: audit({ observed_at: "not-a-date" }) }, + { name: "missing pagination receipts", audit: audit({ pagination_receipts: undefined }) }, + { name: "empty pagination receipts", audit: audit({ pagination_receipts: [] }) }, + { + name: "incomplete pagination", + audit: audit({ pagination_receipts: [{ page: 1, itemCount: 100, hasNext: true }] }), + }, + { + name: "non-sequential pagination", + audit: audit({ + pagination_receipts: [ + { page: 1, itemCount: 100, hasNext: true }, + { page: 3, itemCount: 1, hasNext: false }, + ], + }), + }, + ])("rejects $name", ({ audit: untrustedAudit }) => { + const result = build({ audit: untrustedAudit }); + expect(result.status).toBe("FAIL"); + expect(result.disablements).toEqual([]); + }); + + it.each([ + ".github/workflows/../current.yml", + ".github/workflows//orphan.yml", + ".github/workflows/orphan.txt", + ".github/workflows/orphan.yml/child", + ".github/workflows/./orphan.yml", + ".github/workflows/orphan\\repair.yml", + ])("rejects a non-canonical workflow path %s", (workflowPath) => { + const candidate = { ...ORPHAN, workflow_path: workflowPath }; + const result = build({ + audit: audit({ workflows: [candidate] }), + liveWorkflows: [{ id: ORPHAN.workflow_id, path: workflowPath, state: "active" }], + }); + expect(result.status).toBe("FAIL"); + expect(result.disablements).toEqual([]); + }); + + it.each([ + { + name: "different repository", + plan: { + status: "PASS", + repository_full_name: "ContextualWisdomLab/other", + default_branch_sha: DEFAULT_BRANCH_SHA, + disablements: [ + { + workflow_id: ORPHAN.workflow_id, + workflow_path: ORPHAN.workflow_path, + expected_state: "active", + }, + ], + }, + }, + { + name: "invalid protected-main SHA", + plan: { + status: "PASS", + repository_full_name: REPOSITORY, + default_branch_sha: "not-a-sha", + disablements: [ + { + workflow_id: ORPHAN.workflow_id, + workflow_path: ORPHAN.workflow_path, + expected_state: "active", + }, + ], + }, + }, + { + name: "unsafe planned workflow path", + plan: { + status: "PASS", + repository_full_name: REPOSITORY, + default_branch_sha: DEFAULT_BRANCH_SHA, + disablements: [ + { + workflow_id: ORPHAN.workflow_id, + workflow_path: ".github/workflows/../current.yml", + expected_state: "active", + }, + ], + }, + }, + ])("does not execute a forged PASS plan with $name", async ({ plan }) => { + const revalidateWorkflow = vi.fn(async () => ({ + id: ORPHAN.workflow_id, + path: ORPHAN.workflow_path, + state: "active", + })); + const disableWorkflow = vi.fn(async () => undefined); + + await expect( + executeWorkflowDisablement({ + plan, + candidate: plan.disablements[0], + revalidateWorkflow, + disableWorkflow, + }), + ).rejects.toThrow("disablement plan authority is invalid"); + expect(revalidateWorkflow).not.toHaveBeenCalled(); + expect(disableWorkflow).not.toHaveBeenCalled(); + }); +}); From 37e386861811f8c5a06eabd284f264499962baab Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 17:39:12 +0900 Subject: [PATCH 08/13] fix(operations): bind disablement authority to canonical evidence --- scripts/workflow-registry-disable-plan.mjs | 172 +++++++++++++++++---- 1 file changed, 141 insertions(+), 31 deletions(-) diff --git a/scripts/workflow-registry-disable-plan.mjs b/scripts/workflow-registry-disable-plan.mjs index 7ece15cdf..2eaede696 100644 --- a/scripts/workflow-registry-disable-plan.mjs +++ b/scripts/workflow-registry-disable-plan.mjs @@ -1,5 +1,11 @@ const EXPECTED_REPOSITORY = "ContextualWisdomLab/noema"; +const WORKFLOW_PATH_PREFIX = ".github/workflows/"; const LOWERCASE_SHA_40 = /^[0-9a-f]{40}$/; +const ISO_UTC_MILLISECOND = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/; + +function isRecord(value) { + return value !== null && typeof value === "object" && !Array.isArray(value); +} function failedPlan(repository, defaultBranchSha, code, detail) { return { @@ -11,6 +17,82 @@ function failedPlan(repository, defaultBranchSha, code, detail) { }; } +function validWorkflowId(value) { + return Number.isSafeInteger(value) && value > 0; +} + +function validWorkflowPath(value) { + if ( + typeof value !== "string" + || !value.startsWith(WORKFLOW_PATH_PREFIX) + || !/\.ya?ml$/.test(value) + || value.includes("\\") + || value.includes("\0") + ) { + return false; + } + + const relativePath = value.slice(WORKFLOW_PATH_PREFIX.length); + const pathSegments = relativePath.split("/"); + return ( + pathSegments.length > 0 + && pathSegments.every((segment) => segment.length > 0 && segment !== "." && segment !== "..") + ); +} + +function validObservedAt(value) { + return ( + typeof value === "string" + && ISO_UTC_MILLISECOND.test(value) + && !Number.isNaN(Date.parse(value)) + ); +} + +function validPaginationReceipts(value) { + if (!Array.isArray(value) || value.length === 0) return false; + + return value.every((receipt, index) => ( + isRecord(receipt) + && receipt.page === index + 1 + && Number.isSafeInteger(receipt.itemCount) + && receipt.itemCount >= 0 + && typeof receipt.hasNext === "boolean" + && receipt.hasNext === (index < value.length - 1) + )); +} + +function validPlanAuthority(plan) { + if ( + !isRecord(plan) + || plan.status !== "PASS" + || plan.repository_full_name !== EXPECTED_REPOSITORY + || typeof plan.default_branch_sha !== "string" + || !LOWERCASE_SHA_40.test(plan.default_branch_sha) + || !Array.isArray(plan.disablements) + ) { + return false; + } + + const workflowIds = new Set(); + const workflowPaths = new Set(); + for (const disablement of plan.disablements) { + if ( + !isRecord(disablement) + || !validWorkflowId(disablement.workflow_id) + || !validWorkflowPath(disablement.workflow_path) + || disablement.expected_state !== "active" + || workflowIds.has(disablement.workflow_id) + || workflowPaths.has(disablement.workflow_path) + ) { + return false; + } + workflowIds.add(disablement.workflow_id); + workflowPaths.add(disablement.workflow_path); + } + + return true; +} + /** * Build a fail-closed disablement plan from one exact workflow-registry audit and * an immediately refreshed live registry snapshot. Active-orphan findings are the @@ -35,8 +117,8 @@ export function buildWorkflowDisablementPlan(input) { } if ( - !LOWERCASE_SHA_40.test(defaultBranchSha ?? "") || - audit?.default_branch_sha !== defaultBranchSha + !LOWERCASE_SHA_40.test(defaultBranchSha ?? "") + || audit?.default_branch_sha !== defaultBranchSha ) { return failedPlan( repository, @@ -47,9 +129,9 @@ export function buildWorkflowDisablementPlan(input) { } if ( - !Array.isArray(audit?.failures) || - !Array.isArray(audit?.workflows) || - !Array.isArray(input?.liveWorkflows) + !Array.isArray(audit?.failures) + || !Array.isArray(audit?.workflows) + || !Array.isArray(input?.liveWorkflows) ) { return failedPlan( repository, @@ -59,21 +141,45 @@ export function buildWorkflowDisablementPlan(input) { ); } - if (audit.failures.some((failure) => failure?.code !== "active_orphan_workflow")) { + if ( + audit.schema_version !== 1 + || audit.status !== "FAIL" + || !validObservedAt(audit.observed_at) + || !validPaginationReceipts(audit.pagination_receipts) + ) { return failedPlan( repository, defaultBranchSha, "disablement_audit_not_authoritative", - "Workflow disablement is blocked while the registry audit contains a non-orphan failure.", + "Workflow disablement requires a complete schema-v1 failing registry audit with canonical observation and pagination evidence.", + ); + } + + if ( + audit.failures.some( + (failure) => !isRecord(failure) + || failure.code !== "active_orphan_workflow" + || !validWorkflowId(failure.workflow_id), + ) + ) { + return failedPlan( + repository, + defaultBranchSha, + "disablement_audit_not_authoritative", + "Workflow disablement is blocked while the registry audit contains a non-orphan or malformed failure.", ); } const candidates = audit.workflows.filter( - (workflow) => workflow?.classification === "active_orphan", + (workflow) => isRecord(workflow) && workflow.classification === "active_orphan", ); - const candidateIds = candidates.map((workflow) => workflow?.workflow_id).sort(); - const failureIds = audit.failures.map((failure) => failure?.workflow_id).sort(); - if (JSON.stringify(candidateIds) !== JSON.stringify(failureIds)) { + const candidateIds = candidates.map((workflow) => workflow.workflow_id).sort((left, right) => left - right); + const failureIds = audit.failures.map((failure) => failure.workflow_id).sort((left, right) => left - right); + if ( + new Set(candidateIds).size !== candidateIds.length + || new Set(failureIds).size !== failureIds.length + || JSON.stringify(candidateIds) !== JSON.stringify(failureIds) + ) { return failedPlan( repository, defaultBranchSha, @@ -104,23 +210,21 @@ export function buildWorkflowDisablementPlan(input) { const disablements = []; for (const candidate of candidates) { if ( - !Number.isSafeInteger(candidate?.workflow_id) || - candidate.workflow_id <= 0 || - typeof candidate?.workflow_path !== "string" || - !candidate.workflow_path.startsWith(".github/workflows/") || - candidate.workflow_state !== "active" + !validWorkflowId(candidate.workflow_id) + || !validWorkflowPath(candidate.workflow_path) + || candidate.workflow_state !== "active" ) { return failedPlan( repository, defaultBranchSha, "workflow_identity_changed", - "An audited active-orphan workflow does not have a safe exact identity.", + "An audited active-orphan workflow does not have a safe canonical identity.", ); } const live = liveWorkflows.find( - (workflow) => - workflow?.id === candidate.workflow_id && workflow?.path === candidate.workflow_path, + (workflow) => workflow?.id === candidate.workflow_id + && workflow?.path === candidate.workflow_path, ); if (!live || live.state !== "active") { return failedPlan( @@ -159,20 +263,26 @@ export function buildWorkflowDisablementPlan(input) { export async function executeWorkflowDisablement(input) { const plan = input?.plan; const candidate = input?.candidate; + if (!validPlanAuthority(plan)) { + throw new Error("disablement plan authority is invalid"); + } + if ( + typeof input?.revalidateWorkflow !== "function" + || typeof input?.disableWorkflow !== "function" + ) { + throw new Error("disablement executor is invalid"); + } if (candidate?.expected_state !== "active") { throw new Error("candidate is not part of the exact disablement plan"); } - const planned = Array.isArray(plan?.disablements) - ? plan.disablements.find( - (item) => - item.workflow_id === candidate.workflow_id && - item.workflow_path === candidate.workflow_path && - item.expected_state === "active", - ) - : undefined; + const planned = plan.disablements.find( + (item) => item.workflow_id === candidate.workflow_id + && item.workflow_path === candidate.workflow_path + && item.expected_state === "active", + ); - if (plan?.status !== "PASS" || !planned) { + if (!planned) { throw new Error("candidate is not part of the exact disablement plan"); } @@ -181,9 +291,9 @@ export async function executeWorkflowDisablement(input) { workflowId: planned.workflow_id, }); if ( - live?.id !== planned.workflow_id || - live?.path !== planned.workflow_path || - live?.state !== "active" + live?.id !== planned.workflow_id + || live?.path !== planned.workflow_path + || live?.state !== "active" ) { throw new Error("workflow identity changed before disablement"); } From e8e8693a4104937dfc4bbe1bf22a1aabc7c2ab99 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 17:40:31 +0900 Subject: [PATCH 09/13] fix(operations): preserve non-passing plan rejection semantics --- scripts/workflow-registry-disable-plan.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/workflow-registry-disable-plan.mjs b/scripts/workflow-registry-disable-plan.mjs index 2eaede696..1e33a6bb8 100644 --- a/scripts/workflow-registry-disable-plan.mjs +++ b/scripts/workflow-registry-disable-plan.mjs @@ -263,6 +263,9 @@ export function buildWorkflowDisablementPlan(input) { export async function executeWorkflowDisablement(input) { const plan = input?.plan; const candidate = input?.candidate; + if (plan?.status !== "PASS" || !Array.isArray(plan?.disablements)) { + throw new Error("candidate is not part of the exact disablement plan"); + } if (!validPlanAuthority(plan)) { throw new Error("disablement plan authority is invalid"); } From 1c3bae58bdb18c3b809b7dd94486647bc2d74e54 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 17:47:35 +0900 Subject: [PATCH 10/13] test(operations): reject structurally forged disablement plans --- ...ow-registry-disable-plan-hardening.test.ts | 60 ++++++++++++------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/test/workflow-registry-disable-plan-hardening.test.ts b/test/workflow-registry-disable-plan-hardening.test.ts index d64ad5779..57de0f906 100644 --- a/test/workflow-registry-disable-plan-hardening.test.ts +++ b/test/workflow-registry-disable-plan-hardening.test.ts @@ -49,6 +49,22 @@ function build(overrides: Record = {}) { }); } +function forgedPassingPlan() { + return { + status: "PASS", + repository_full_name: REPOSITORY, + default_branch_sha: DEFAULT_BRANCH_SHA, + disablements: [ + { + workflow_id: ORPHAN.workflow_id, + workflow_path: ORPHAN.workflow_path, + expected_state: "active", + }, + ], + failures: [], + }; +} + describe("workflow disablement authority hardening", () => { it.each([ { name: "missing schema version", audit: audit({ schema_version: undefined }) }, @@ -71,6 +87,10 @@ describe("workflow disablement authority hardening", () => { ], }), }, + { + name: "empty orphan authority", + audit: audit({ failures: [], workflows: [] }), + }, ])("rejects $name", ({ audit: untrustedAudit }) => { const result = build({ audit: untrustedAudit }); expect(result.status).toBe("FAIL"); @@ -98,39 +118,21 @@ describe("workflow disablement authority hardening", () => { { name: "different repository", plan: { - status: "PASS", + ...forgedPassingPlan(), repository_full_name: "ContextualWisdomLab/other", - default_branch_sha: DEFAULT_BRANCH_SHA, - disablements: [ - { - workflow_id: ORPHAN.workflow_id, - workflow_path: ORPHAN.workflow_path, - expected_state: "active", - }, - ], }, }, { name: "invalid protected-main SHA", plan: { - status: "PASS", - repository_full_name: REPOSITORY, + ...forgedPassingPlan(), default_branch_sha: "not-a-sha", - disablements: [ - { - workflow_id: ORPHAN.workflow_id, - workflow_path: ORPHAN.workflow_path, - expected_state: "active", - }, - ], }, }, { name: "unsafe planned workflow path", plan: { - status: "PASS", - repository_full_name: REPOSITORY, - default_branch_sha: DEFAULT_BRANCH_SHA, + ...forgedPassingPlan(), disablements: [ { workflow_id: ORPHAN.workflow_id, @@ -140,6 +142,14 @@ describe("workflow disablement authority hardening", () => { ], }, }, + { + name: "structurally valid but unauthenticated plan", + plan: forgedPassingPlan(), + }, + { + name: "serialized clone of an authentic plan", + plan: structuredClone(build()), + }, ])("does not execute a forged PASS plan with $name", async ({ plan }) => { const revalidateWorkflow = vi.fn(async () => ({ id: ORPHAN.workflow_id, @@ -159,4 +169,12 @@ describe("workflow disablement authority hardening", () => { expect(revalidateWorkflow).not.toHaveBeenCalled(); expect(disableWorkflow).not.toHaveBeenCalled(); }); + + it("returns an immutable in-process plan authority", () => { + const plan = build(); + expect(Object.isFrozen(plan)).toBe(true); + expect(Object.isFrozen(plan.disablements)).toBe(true); + expect(Object.isFrozen(plan.disablements[0])).toBe(true); + expect(Object.isFrozen(plan.failures)).toBe(true); + }); }); From b52523cd033f1d94992acb131c23d1e03fef8b58 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 17:49:25 +0900 Subject: [PATCH 11/13] fix(operations): make disablement plans in-process immutable authority --- scripts/workflow-registry-disable-plan.mjs | 51 ++++++++++++++++------ 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/scripts/workflow-registry-disable-plan.mjs b/scripts/workflow-registry-disable-plan.mjs index 1e33a6bb8..26afae814 100644 --- a/scripts/workflow-registry-disable-plan.mjs +++ b/scripts/workflow-registry-disable-plan.mjs @@ -2,19 +2,32 @@ const EXPECTED_REPOSITORY = "ContextualWisdomLab/noema"; const WORKFLOW_PATH_PREFIX = ".github/workflows/"; const LOWERCASE_SHA_40 = /^[0-9a-f]{40}$/; const ISO_UTC_MILLISECOND = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/; +const AUTHENTIC_PLANS = new WeakSet(); function isRecord(value) { return value !== null && typeof value === "object" && !Array.isArray(value); } +function freezePlan(plan, authenticate = false) { + const disablements = Object.freeze( + plan.disablements.map((disablement) => Object.freeze({ ...disablement })), + ); + const failures = Object.freeze( + plan.failures.map((failure) => Object.freeze({ ...failure })), + ); + const frozen = Object.freeze({ ...plan, disablements, failures }); + if (authenticate) AUTHENTIC_PLANS.add(frozen); + return frozen; +} + function failedPlan(repository, defaultBranchSha, code, detail) { - return { + return freezePlan({ status: "FAIL", repository_full_name: repository ?? null, default_branch_sha: defaultBranchSha ?? null, disablements: [], failures: [{ code, detail }], - }; + }); } function validWorkflowId(value) { @@ -64,11 +77,18 @@ function validPaginationReceipts(value) { function validPlanAuthority(plan) { if ( !isRecord(plan) + || !AUTHENTIC_PLANS.has(plan) + || !Object.isFrozen(plan) || plan.status !== "PASS" || plan.repository_full_name !== EXPECTED_REPOSITORY || typeof plan.default_branch_sha !== "string" || !LOWERCASE_SHA_40.test(plan.default_branch_sha) || !Array.isArray(plan.disablements) + || !Object.isFrozen(plan.disablements) + || !Array.isArray(plan.failures) + || !Object.isFrozen(plan.failures) + || plan.failures.length !== 0 + || plan.disablements.length === 0 ) { return false; } @@ -78,6 +98,7 @@ function validPlanAuthority(plan) { for (const disablement of plan.disablements) { if ( !isRecord(disablement) + || !Object.isFrozen(disablement) || !validWorkflowId(disablement.workflow_id) || !validWorkflowPath(disablement.workflow_path) || disablement.expected_state !== "active" @@ -97,6 +118,8 @@ function validPlanAuthority(plan) { * Build a fail-closed disablement plan from one exact workflow-registry audit and * an immediately refreshed live registry snapshot. Active-orphan findings are the * only audit failures that can authorize a plan; every other failure invalidates it. + * Passing plans are immutable, process-local authorities: serialized or reconstructed + * lookalikes remain review evidence but cannot authorize mutation. * * @param {object} input exact audit, expected protected-main identity, and live registry * @returns {object} bounded plan containing only exact active-orphan identities @@ -156,7 +179,8 @@ export function buildWorkflowDisablementPlan(input) { } if ( - audit.failures.some( + audit.failures.length === 0 + || audit.failures.some( (failure) => !isRecord(failure) || failure.code !== "active_orphan_workflow" || !validWorkflowId(failure.workflow_id), @@ -166,7 +190,7 @@ export function buildWorkflowDisablementPlan(input) { repository, defaultBranchSha, "disablement_audit_not_authoritative", - "Workflow disablement is blocked while the registry audit contains a non-orphan or malformed failure.", + "Workflow disablement is blocked unless the registry audit contains one or more exact active-orphan failures and no other failure type.", ); } @@ -176,7 +200,8 @@ export function buildWorkflowDisablementPlan(input) { const candidateIds = candidates.map((workflow) => workflow.workflow_id).sort((left, right) => left - right); const failureIds = audit.failures.map((failure) => failure.workflow_id).sort((left, right) => left - right); if ( - new Set(candidateIds).size !== candidateIds.length + candidates.length === 0 + || new Set(candidateIds).size !== candidateIds.length || new Set(failureIds).size !== failureIds.length || JSON.stringify(candidateIds) !== JSON.stringify(failureIds) ) { @@ -243,21 +268,21 @@ export function buildWorkflowDisablementPlan(input) { } disablements.sort((left, right) => left.workflow_id - right.workflow_id); - return { + return freezePlan({ status: "PASS", repository_full_name: repository, default_branch_sha: defaultBranchSha, disablements, failures: [], - }; + }, true); } /** - * Disable exactly one candidate after revalidating its live workflow ID, path, and - * state. Callers supply the authorized mutation primitive; this module never owns - * credentials, transport, or batch mutation authority. + * Disable exactly one candidate from a freshly built process-local plan after + * revalidating its live workflow ID, path, and state. Callers supply the authorized + * mutation primitive; this module never owns credentials, transport, or batch authority. * - * @param {object} input passing plan, candidate, live reader, and disable callback + * @param {object} input authentic plan, candidate, live reader, and disable callback * @returns {Promise} immutable description of the single completed mutation */ export async function executeWorkflowDisablement(input) { @@ -306,11 +331,11 @@ export async function executeWorkflowDisablement(input) { workflowId: planned.workflow_id, }); - return { + return Object.freeze({ repository_full_name: plan.repository_full_name, workflow_id: planned.workflow_id, workflow_path: planned.workflow_path, prior_state: "active", mutation: "disable", - }; + }); } From ffa5d9a400dca1455fc3ce8db85a5ef76f275df4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 17:51:11 +0900 Subject: [PATCH 12/13] refactor(operations): keep plan authority minimal and testable --- scripts/workflow-registry-disable-plan.mjs | 38 +--------------------- 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/scripts/workflow-registry-disable-plan.mjs b/scripts/workflow-registry-disable-plan.mjs index 26afae814..8ff7d7355 100644 --- a/scripts/workflow-registry-disable-plan.mjs +++ b/scripts/workflow-registry-disable-plan.mjs @@ -75,43 +75,7 @@ function validPaginationReceipts(value) { } function validPlanAuthority(plan) { - if ( - !isRecord(plan) - || !AUTHENTIC_PLANS.has(plan) - || !Object.isFrozen(plan) - || plan.status !== "PASS" - || plan.repository_full_name !== EXPECTED_REPOSITORY - || typeof plan.default_branch_sha !== "string" - || !LOWERCASE_SHA_40.test(plan.default_branch_sha) - || !Array.isArray(plan.disablements) - || !Object.isFrozen(plan.disablements) - || !Array.isArray(plan.failures) - || !Object.isFrozen(plan.failures) - || plan.failures.length !== 0 - || plan.disablements.length === 0 - ) { - return false; - } - - const workflowIds = new Set(); - const workflowPaths = new Set(); - for (const disablement of plan.disablements) { - if ( - !isRecord(disablement) - || !Object.isFrozen(disablement) - || !validWorkflowId(disablement.workflow_id) - || !validWorkflowPath(disablement.workflow_path) - || disablement.expected_state !== "active" - || workflowIds.has(disablement.workflow_id) - || workflowPaths.has(disablement.workflow_path) - ) { - return false; - } - workflowIds.add(disablement.workflow_id); - workflowPaths.add(disablement.workflow_path); - } - - return true; + return isRecord(plan) && AUTHENTIC_PLANS.has(plan); } /** From 15510ca3d492f9209dc9a9e3a5a6c064b24f444f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 17:52:50 +0900 Subject: [PATCH 13/13] refactor(operations): make authority check branchless --- scripts/workflow-registry-disable-plan.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/workflow-registry-disable-plan.mjs b/scripts/workflow-registry-disable-plan.mjs index 8ff7d7355..143a2f0cf 100644 --- a/scripts/workflow-registry-disable-plan.mjs +++ b/scripts/workflow-registry-disable-plan.mjs @@ -75,7 +75,7 @@ function validPaginationReceipts(value) { } function validPlanAuthority(plan) { - return isRecord(plan) && AUTHENTIC_PLANS.has(plan); + return AUTHENTIC_PLANS.has(plan); } /**