From ef6408de33529d5264240e004ea826547ef003bb Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Tue, 18 Aug 2026 20:53:30 +0900 Subject: [PATCH 1/2] refactor(config): extract provider-name leaf; break config<->profile cycle --- src/config.ts | 25 ++----------------------- src/config/provider-name.ts | 24 ++++++++++++++++++++++++ src/router.ts | 3 ++- src/routing/profile.ts | 2 +- 4 files changed, 29 insertions(+), 25 deletions(-) create mode 100644 src/config/provider-name.ts diff --git a/src/config.ts b/src/config.ts index a0fa0a3b25..9f590e7323 100644 --- a/src/config.ts +++ b/src/config.ts @@ -5,6 +5,7 @@ import { homedir } from "node:os"; import { dirname, join, resolve } from "node:path"; import { Database } from "bun:sqlite"; import * as z from "zod/v4"; +import { isValidProviderName, hasOwnProvider } from "./config/provider-name"; import { bumpConfigGenerationAtPath, bumpCurrentConfigGeneration, @@ -736,19 +737,6 @@ const providerConfigSchema = z.object({ responsesSnapshotRepair: z.boolean().optional(), }).passthrough(); -const RESERVED_PROVIDER_NAMES = new Set([ - // JavaScript prototype-pollution guards. - "__proto__", - "prototype", - "constructor", - // System-reserved routing namespace (resolved before provider/account - // namespaces in routeModelInternal). "combo" is intentionally NOT reserved: - // a physical provider named `combo` is a supported pattern (combo aliases - // hosted on the combo provider), and the combo selector only wins when an - // actual combo id matches. - "policy", -]); -const PROVIDER_NAME_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,62}[A-Za-z0-9])?$/; const HEADER_NAME_PATTERN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/; const SENSITIVE_PROVIDER_HEADERS = new Set([ "authorization", @@ -760,16 +748,7 @@ const SENSITIVE_PROVIDER_HEADERS = new Set([ "x-amz-security-token", ]); -export function isValidProviderName(name: string): boolean { - const trimmed = name.trim(); - return trimmed === name - && PROVIDER_NAME_PATTERN.test(name) - && !RESERVED_PROVIDER_NAMES.has(name.toLowerCase()); -} - -export function hasOwnProvider(providers: Record, name: string): boolean { - return Object.prototype.hasOwnProperty.call(providers, name); -} +export { isValidProviderName, hasOwnProvider } from "./config/provider-name"; export function providerBaseUrlConfigError(baseUrl: string): string | null { try { diff --git a/src/config/provider-name.ts b/src/config/provider-name.ts new file mode 100644 index 0000000000..244bd3cc3e --- /dev/null +++ b/src/config/provider-name.ts @@ -0,0 +1,24 @@ +const RESERVED_PROVIDER_NAMES = new Set([ + // JavaScript prototype-pollution guards. + "__proto__", + "prototype", + "constructor", + // System-reserved routing namespace (resolved before provider/account + // namespaces in routeModelInternal). "combo" is intentionally NOT reserved: + // a physical provider named `combo` is a supported pattern (combo aliases + // hosted on the combo provider), and the combo selector only wins when an + // actual combo id matches. + "policy", +]); +const PROVIDER_NAME_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,62}[A-Za-z0-9])?$/; + +export function isValidProviderName(name: string): boolean { + const trimmed = name.trim(); + return trimmed === name + && PROVIDER_NAME_PATTERN.test(name) + && !RESERVED_PROVIDER_NAMES.has(name.toLowerCase()); +} + +export function hasOwnProvider(providers: Record, name: string): boolean { + return Object.prototype.hasOwnProperty.call(providers, name); +} diff --git a/src/router.ts b/src/router.ts index be06d6c478..297795180e 100644 --- a/src/router.ts +++ b/src/router.ts @@ -8,7 +8,8 @@ import { type ComboPick, } from "./combos"; import type { NormalizedComboConfig } from "./combos/types"; -import { hasOwnProvider, resolveEnvValue } from "./config"; +import { hasOwnProvider } from "./config/provider-name"; +import { resolveEnvValue } from "./config"; import { assertProviderDestinationAllowed } from "./lib/destination-policy"; import { redactSecretString, redactUrlForLog } from "./lib/redact"; import { diff --git a/src/routing/profile.ts b/src/routing/profile.ts index 17e07d6b04..6c978de94a 100644 --- a/src/routing/profile.ts +++ b/src/routing/profile.ts @@ -13,7 +13,7 @@ import type { } from "../types"; import { codexAccountNamespaceEntries } from "../codex/account-namespaces"; import { listComboIds, resolveComboId } from "../combos"; -import { hasOwnProvider } from "../config"; +import { hasOwnProvider } from "../config/provider-name"; import { MAX_COMPATIBILITY_REQUIRED_SUITES } from "./compatibility/types"; import { POLICY_NAMESPACE } from "./profile-namespace"; From 8853184614a9c0dca30adad3994ceba53152cb2f Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Tue, 18 Aug 2026 20:53:30 +0900 Subject: [PATCH 2/2] docs(devlog): WP2a-1 audited plan --- .../030_wp2a_provider_name_leaf.md | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 devlog/_plan/260818_megafile_split_program/030_wp2a_provider_name_leaf.md diff --git a/devlog/_plan/260818_megafile_split_program/030_wp2a_provider_name_leaf.md b/devlog/_plan/260818_megafile_split_program/030_wp2a_provider_name_leaf.md new file mode 100644 index 0000000000..08c7a6934c --- /dev/null +++ b/devlog/_plan/260818_megafile_split_program/030_wp2a_provider_name_leaf.md @@ -0,0 +1,62 @@ +# WP2a-1 — config provider-name leaf (cycle breaker; parallel PR off dev) + +Branch codex/split-wp2a-config-names on dev@aaf04690e. NOT stacked on the +types stack (disjoint files, DEV-STACK-01 'independent parts -> parallel PRs'). +Class C2 pure move + 2 consumer retargets. Risk basis 000_risk_assessment.md +WP2a; highest-leverage low-risk config extraction: breaks the existing +config <-> routing/profile import cycle. + +## Loop spec + +- Goal: isValidProviderName/hasOwnProvider live in a leaf with no heavy deps; + routing/profile.ts and router.ts stop importing them through the 3900-line + config barrel (which loads Zod + bun:sqlite + registry transitively). +- Non-goals: no other config extraction this PR; management write-path + callers keep importing from ./config (barrel re-export). +- Verifier: typecheck + lidge full suite + core-lab-boundary. + +## File change map + +- ADD src/config/provider-name.ts: RESERVED_PROVIDER_NAMES, + PROVIDER_NAME_PATTERN (both module-private consts, config.ts 738-750), + isValidProviderName (762), hasOwnProvider (769). Zero imports. +- EDIT src/config.ts: delete moved bodies; add + `export { isValidProviderName, hasOwnProvider } from "./config/provider-name"`; + internal call sites (1150, 1390, 1597 + others) need a local + `import { ... } from "./config/provider-name"` since re-export binds nothing + (WP1 lesson). +- EDIT src/routing/profile.ts:16: import hasOwnProvider from + ../config/provider-name (cycle edge profile->config removed). +- EDIT src/router.ts:11: split import — hasOwnProvider from + ./config/provider-name, resolveEnvValue stays from ./config. + +## Accept criteria + +1. typecheck exit 0. 2. lidge full suite 0 fail (baseline 13201 pass). +3. core-lab-boundary green (router edge now reaches a leaf with no imports — + protected graph shrinks). +4. rg 'from "../config"' src/routing/profile.ts -> no hasOwnProvider import + through the barrel (cycle gone; remaining profile imports from config: none + expected — verify, else keep others intact). +5. Source diff: exactly 4 files under src/. + +## Risks + +- config.ts superRefine calls isValidProviderName internally — the local + import must land before schema evaluation (top of file, hoisted; ESM fine). +- routing/profile.ts may import more than hasOwnProvider from ../config — + verify and leave other names on the barrel. + + +## Audit amendments (grok PASS / sol NEAR-PASS) + +- Internal call sites are EXACTLY 3 (1150, 1390 isValidProviderName; 1597 + hasOwnProvider), all inside superRefine callbacks — no TDZ risk. +- AC3 claim corrected: the protected graph does NOT shrink (router keeps the + barrel edge for resolveEnvValue; the leaf adds one dead-end module). The + real win is the config<->profile cycle break. core-lab-boundary stays + green either way. +- profile.ts imports nothing else from ../config — cycle fully gone. +- Tests importing isValidProviderName via barrel: config.test.ts:13, + policy-execution.test.ts:6 — barrel re-export preserves both. +