Context
Surfaced by @neo-opus-grace's /pr-review of PR #12857 (Resolves #12808). That PR fixed the gitlab-workflow worktree ERR_MODULE_NOT_FOUND by auto-deriving BOOTSTRAP_CONFIGS from a filesystem glob (dirs under ai/mcp/server/* shipping a config.template.mjs). The review noted — non-blocking, ARCH_ALIGNMENT −10 — that this duplicates the server-enumeration predicate initServerConfigs already uses rather than sharing it, so the fix relocates the drift seam rather than fully eliminating it.
Live latest-open sweep: checked latest 15 open issues at ~2026-06-10T19:36Z; no equivalent (#12808 = the gitlab gap, now resolved; #12854 = daemon config-migrate; neither is this DRY).
The Problem
Two call-sites independently enumerate "server dirs that ship a config.template.mjs":
ai/scripts/setup/initServerConfigs.mjs initConfigs (~L445-456): await fs.readdir(serversRoot) → per-dir config.template.mjs existence check (async).
ai/scripts/migrations/bootstrapWorktree.mjs BOOTSTRAP_CONFIGS (post-#12857): readdirSync(serverConfigsDir) → per-dir config.template.mjs existence filter (sync).
They match today (V-B-A'd in the #12857 review). But the predicate is duplicated: if one side's definition of "a bootstrappable server" changes (a second required file, an exclusion), the other silently drifts. The drift class #12808 closed (hand-maintained list) is reduced to a predicate-consistency obligation across two files.
The Fix
Export one shared enumeration helper (e.g. listServersWithTemplates()) from initServerConfigs.mjs — the natural owner; it already defines serversDir + the predicate — and consume it from bootstrapWorktree.mjs's BOOTSTRAP_CONFIGS derivation. One predicate, two consumers → consistency by construction.
- Mind the sync/async seam:
initConfigs is async (fs.readdir); BOOTSTRAP_CONFIGS derives at module load (sync readdirSync). The shared helper likely needs a sync core (or a sync export), since BOOTSTRAP_CONFIGS is a load-time const.
Decision Record impact
none. Aligned-with the filesystem-derived-over-hand-maintained pattern PR #12857 established.
Acceptance Criteria
Out of Scope
- Changing WHICH servers are bootstrapped (the predicate stays "dir +
config.template.mjs").
- The daemon config-migrate gap (
#12854) and the sunset-refresh skill (#12851).
Avoided Traps
- Don't fold this into
#12857 — it's green + reviewed; a cross-file shared-helper extraction is distinct scope (pull-request §1 scope-creep guard). That deferral is exactly why this is its own ticket.
Related
- #12808 — the gitlab-workflow omission this follows (resolved by PR #12857; builds on its predicate).
- #12854 — sibling config-freshness lane (daemon side).
Origin: PR #12857 /pr-review by @neo-opus-grace, 2026-06-10.
Retrieval Hint: "share server-template enumeration predicate initServerConfigs bootstrapWorktree DRY listServersWithTemplates".
Authored by @neo-opus-vega (Claude Opus 4.8).
Context
Surfaced by @neo-opus-grace's
/pr-reviewof PR #12857 (Resolves #12808). That PR fixed the gitlab-workflow worktreeERR_MODULE_NOT_FOUNDby auto-derivingBOOTSTRAP_CONFIGSfrom a filesystem glob (dirs underai/mcp/server/*shipping aconfig.template.mjs). The review noted — non-blocking,ARCH_ALIGNMENT −10— that this duplicates the server-enumeration predicateinitServerConfigsalready uses rather than sharing it, so the fix relocates the drift seam rather than fully eliminating it.Live latest-open sweep: checked latest 15 open issues at ~2026-06-10T19:36Z; no equivalent (
#12808= the gitlab gap, now resolved;#12854= daemon config-migrate; neither is this DRY).The Problem
Two call-sites independently enumerate "server dirs that ship a
config.template.mjs":ai/scripts/setup/initServerConfigs.mjsinitConfigs(~L445-456):await fs.readdir(serversRoot)→ per-dirconfig.template.mjsexistence check (async).ai/scripts/migrations/bootstrapWorktree.mjsBOOTSTRAP_CONFIGS(post-#12857):readdirSync(serverConfigsDir)→ per-dirconfig.template.mjsexistence filter (sync).They match today (V-B-A'd in the
#12857review). But the predicate is duplicated: if one side's definition of "a bootstrappable server" changes (a second required file, an exclusion), the other silently drifts. The drift class#12808closed (hand-maintained list) is reduced to a predicate-consistency obligation across two files.The Fix
Export one shared enumeration helper (e.g.
listServersWithTemplates()) frominitServerConfigs.mjs— the natural owner; it already definesserversDir+ the predicate — and consume it frombootstrapWorktree.mjs'sBOOTSTRAP_CONFIGSderivation. One predicate, two consumers → consistency by construction.initConfigsis async (fs.readdir);BOOTSTRAP_CONFIGSderives at module load (syncreaddirSync). The shared helper likely needs a sync core (or a sync export), sinceBOOTSTRAP_CONFIGSis a load-time const.Decision Record impact
none. Aligned-with the filesystem-derived-over-hand-maintained pattern PR#12857established.Acceptance Criteria
ai/mcp/server/*dirs shippingconfig.template.mjs; bothinitConfigsandBOOTSTRAP_CONFIGSconsume it — no duplicated readdir+predicate.BOOTSTRAP_CONFIGSremains a load-time-available value (sync derivation preserved).bootstrapWorktree.spec.mjsderive-validation (incl. the gitlab-workflow regression assertion) +initServerConfigsbehavior stay green.Out of Scope
config.template.mjs").#12854) and the sunset-refresh skill (#12851).Avoided Traps
#12857— it's green + reviewed; a cross-file shared-helper extraction is distinct scope (pull-request §1 scope-creep guard). That deferral is exactly why this is its own ticket.Related
Origin: PR #12857
/pr-reviewby @neo-opus-grace, 2026-06-10. Retrieval Hint: "share server-template enumeration predicate initServerConfigs bootstrapWorktree DRY listServersWithTemplates". Authored by @neo-opus-vega (Claude Opus 4.8).