From 9701725c800e24fd47d118711bc4f0f7bb7ffd6b Mon Sep 17 00:00:00 2001 From: Vivswan Shah <58091053+Vivswan@users.noreply.github.com> Date: Mon, 5 Oct 2026 04:00:27 -0400 Subject: [PATCH] refactor(sections): move the plan lockstep types beside KeyErasedPlan and name FamilyPlan KeyedPlan FamilyPlan, WidePlan, WidePlanAt, Invariant, and PlanMisfits lived in src/sections/shared/keyed-values.ts, the secrets and variables family scaffolding, and the setup factory in setup-section.ts had to import a "Family" vocabulary from a family module to prove its one plan the same way. The five types now sit after KeyErasedPlan in src/sections/contract/plan.ts, the module that already owns PlanContext, PlannedOp, SectionPlan, and the key-erasure the lockstep compares through. FamilyPlan is renamed KeyedPlan: one section's plan() under its own key brand, the counterpart KeyErasedPlan strips. The other four keep their names and bodies. keyed-values.ts keeps no re-export and drops the imports only the moved types used. repo-secrets.ts, repo-variables.ts, and setup-section.ts import the three public types from the contract. KeyErasedPlan loses its export keyword since nothing outside plan.ts reads it now. --- src/sections/contract/plan.ts | 51 ++++++++++++++++++++++- src/sections/shared/keyed-values.ts | 58 +++------------------------ src/sections/shared/repo-secrets.ts | 13 +++--- src/sections/shared/repo-variables.ts | 13 +++--- src/sections/shared/setup-section.ts | 6 ++- 5 files changed, 74 insertions(+), 67 deletions(-) diff --git a/src/sections/contract/plan.ts b/src/sections/contract/plan.ts index 6207cb19..ce12f52f 100644 --- a/src/sections/contract/plan.ts +++ b/src/sections/contract/plan.ts @@ -24,7 +24,13 @@ import type { GraphqlVariablesOf, } from "./graphql.js"; import { parseLive } from "./live.js"; -import type { EndpointDict, GraphqlDict, SectionContext, SectionMeta } from "./module.js"; +import type { + EndpointDict, + GraphqlDict, + SectionContext, + SectionMeta, + ValidatedInput, +} from "./module.js"; import { call, callGraphql, @@ -358,13 +364,54 @@ export interface PlanContext< * A plan handler with its context's key brand erased: a shared handler serving several sections is * compared to each branded per-key signature through this (repo-secrets, repo-variables, setup-section). */ -export type KeyErasedPlan

= P extends ( +type KeyErasedPlan

= P extends ( ctx: PlanContext, declared: infer D, ) => infer R ? (ctx: PlanContext, declared: D) => R : never; +/** + * One section's plan() under its own key brand, over exactly its own dictionary and declared value (the + * registry's exactness lockstep). + */ +export type KeyedPlan = ( + ctx: PlanContext, + declared: ValidatedInput, +) => Promise>, SectionFailure>>; + +/** + * The plan a factory file writes ONCE over its wide dictionary (every route the union over the sections it + * serves): inside the generic factory the section is unresolved, so the contract's role derivations only + * resolve over this view. The brand on `declared` names the section it is called as. + */ +export type WidePlan = ( + ctx: PlanContext, + declared: ValidatedInput, +) => Promise>, SectionFailure>>; + +type WidePlanAt = ( + ctx: PlanContext, + declared: ValidatedInput, +) => Promise>, SectionFailure>>; + +type Invariant = [A] extends [B] ? ([B] extends [A] ? true : false) : false; + +/** + * The sections whose own plan signature the wide plan is NOT, key brand aside: a factory file pins it to + * never, so a role the wide view carries and a section's dictionary does not (or the reverse) fails to compile + * there instead of losing role checking silently. + */ +export type PlanMisfits< + Keys extends SectionKey, + Wide extends EndpointDict, + Table extends { readonly [F in Keys]: Wide }, +> = { + [K in Keys]: Invariant, KeyErasedPlan>> extends true + ? never + : K; +}[Keys]; + /** The run's on-missing-permission input: how a read the token is denied classifies. */ export type OnMissingPermission = "fail" | "warn"; diff --git a/src/sections/shared/keyed-values.ts b/src/sections/shared/keyed-values.ts index 5b1595bf..a3a1397f 100644 --- a/src/sections/shared/keyed-values.ts +++ b/src/sections/shared/keyed-values.ts @@ -1,10 +1,9 @@ /** * The scaffolding the repo-scoped secret families (./repo-secrets.ts) and variable families (./repo-variables.ts) - * share: what a section supplies, the undeclared-policy unwrap, the knobbed snapshot wrap, the wide plan every - * family of a kind is served by, and the lockstep proving it is each family's own plan. What a value IS (sealed - * and write-only, or readable and compared) stays in the family file: it decides the routes, the plan scope, and - * the read-back, so those are written once per kind, not once per section. The plan types and the lockstep are - * not keyed-values specific: the setup factory (./setup-section.ts) proves its one plan the same way. + * share: what a section supplies, the undeclared-policy unwrap, and the knobbed snapshot wrap. What a value IS + * (sealed and write-only, or readable and compared) stays in the family file: it decides the routes, the plan + * scope, and the read-back, so those are written once per kind, not once per section. The wide plan a family file + * writes and the lockstep proving it is each family's own plan are the contract's (../contract/plan.ts). */ import type { Result } from "neverthrow"; @@ -14,20 +13,11 @@ import type { SectionFailure } from "../contract/errors.js"; import { defaultUndeclaredPolicy, type EndpointDict, - type GraphqlDict, type SectionMeta, undeclaredPolicy, - type ValidatedInput, } from "../contract/module.js"; import type { PatResource } from "../contract/permissions.js"; -import type { - KeyErasedPlan, - PlanContext, - PlannedOp, - Read, - SectionPlan, - SnapshotContext, -} from "../contract/plan.js"; +import type { Read, SnapshotContext } from "../contract/plan.js"; import { knobbedSnapshot } from "./snapshot-helpers.js"; export interface KeyedValuesFamily { @@ -40,44 +30,6 @@ export interface KeyedValuesFamily { export type Declared = Entry[] | UndeclaredPolicyList; -/** One family's plan() over exactly its own dictionary and declared value (the registry's exactness lockstep). */ -export type FamilyPlan = ( - ctx: PlanContext, - declared: ValidatedInput, -) => Promise>, SectionFailure>>; - -/** - * The plan a family file writes ONCE over its wide dictionary (every route the union over the kind's - * segments): inside the generic factory the segment is unresolved, so the contract's role derivations only - * resolve over this view. The brand on `declared` names the family it is called as. - */ -export type WidePlan = ( - ctx: PlanContext, - declared: ValidatedInput, -) => Promise>, SectionFailure>>; - -type WidePlanAt = ( - ctx: PlanContext, - declared: ValidatedInput, -) => Promise>, SectionFailure>>; - -type Invariant = [A] extends [B] ? ([B] extends [A] ? true : false) : false; - -/** - * The sections whose own plan signature the wide plan is NOT, key brand aside: a factory file pins it to - * never, so a role the wide view carries and a section's dictionary does not (or the reverse) fails to compile - * there instead of losing role checking silently. - */ -export type PlanMisfits< - Keys extends SectionKey, - Wide extends EndpointDict, - Table extends { readonly [F in Keys]: Wide }, -> = { - [K in Keys]: Invariant, KeyErasedPlan>> extends true - ? never - : K; -}[Keys]; - export function knobbedEntries( meta: SectionMeta, declared: Declared, diff --git a/src/sections/shared/repo-secrets.ts b/src/sections/shared/repo-secrets.ts index 3f14ed2c..0a398aaa 100644 --- a/src/sections/shared/repo-secrets.ts +++ b/src/sections/shared/repo-secrets.ts @@ -24,16 +24,19 @@ import { type SectionSnapshot, } from "../contract/module.js"; import type { PatResource } from "../contract/permissions.js"; -import type { PlannedOp, SnapshotContext } from "../contract/plan.js"; +import type { + KeyedPlan, + PlanMisfits, + PlannedOp, + SnapshotContext, + WidePlan, +} from "../contract/plan.js"; import { DependabotSecretConfig } from "../dependabot_secrets/schema.js"; import { type Declared, - type FamilyPlan, type KeyedValuesFamily, knobbedEntries, - type PlanMisfits, snapshotOf, - type WidePlan, } from "./keyed-values.js"; import { knobbed, routed, type sealedSecretConfig } from "./schema-helpers.js"; import { @@ -117,7 +120,7 @@ type WideEndpoints = RepoSecretsEndpoints; /** One family's plan(), indexed by K so the generic factory can assign its one WidePlan to it. */ type RepoSecretsPlan = { - [F in RepoSecretsKey]: FamilyPlan; + [F in RepoSecretsKey]: KeyedPlan; }[K]; type _WidePlanIsEveryFamilyPlan = MustBeNever< diff --git a/src/sections/shared/repo-variables.ts b/src/sections/shared/repo-variables.ts index c225ab21..2a8726cd 100644 --- a/src/sections/shared/repo-variables.ts +++ b/src/sections/shared/repo-variables.ts @@ -20,15 +20,18 @@ import { type SectionSnapshot, } from "../contract/module.js"; import type { PatResource } from "../contract/permissions.js"; -import type { PlannedOp, SnapshotContext } from "../contract/plan.js"; +import type { + KeyedPlan, + PlanMisfits, + PlannedOp, + SnapshotContext, + WidePlan, +} from "../contract/plan.js"; import { type Declared, - type FamilyPlan, type KeyedValuesFamily, knobbedEntries, - type PlanMisfits, snapshotOf, - type WidePlan, } from "./keyed-values.js"; import { knobbed, routed } from "./schema-helpers.js"; import { projectOntoSchema } from "./snapshot-helpers.js"; @@ -105,7 +108,7 @@ type WideEndpoints = RepoVariablesEndpoints; /** One family's plan(), indexed by K so the generic factory can assign its one WidePlan to it. */ type RepoVariablesPlan = { - [F in RepoVariablesKey]: FamilyPlan; + [F in RepoVariablesKey]: KeyedPlan; }[K]; type _WidePlanIsEveryFamilyPlan = MustBeNever< diff --git a/src/sections/shared/setup-section.ts b/src/sections/shared/setup-section.ts index 1adf8195..b2fe805d 100644 --- a/src/sections/shared/setup-section.ts +++ b/src/sections/shared/setup-section.ts @@ -21,11 +21,13 @@ import { type GraphqlDict, requirePlainMapping, type SectionSnapshot } from "../ import type { SectionPermission } from "../contract/permissions.js"; import { hasDrift, + type KeyedPlan, + type PlanMisfits, type PlannedOp, type SectionPlan, type SnapshotContext, + type WidePlan, } from "../contract/plan.js"; -import type { FamilyPlan, PlanMisfits, WidePlan } from "./keyed-values.js"; import { type SetupLanguages, undeclarableLanguages } from "./setup-schema.js"; import { leftOutOfSnapshot, projectOntoSchema } from "./snapshot-helpers.js"; @@ -76,7 +78,7 @@ type SetupTable = { readonly [F in SetupKey]: SetupEndpoints }; /** One setup's plan(), indexed by K so the factory's one WidePlan can be assigned to it. */ type SetupPlan = { - [F in SetupKey]: FamilyPlan; + [F in SetupKey]: KeyedPlan; }[K]; type WideEndpoints = SetupEndpoints;