Context
Stage 3 of Epic #12101 (Greenfield aiConfig). Sub-class C from the Discussion #12100 consumer ledger: pure delegation getters of the form get X() { return AiConfig.X.Y }, introduced in PR #12098. Discussion #12100 universal-bans them — "getter is pure indirection; consumer should read aiConfig.X.Y at point of use" — as part of retiring ~900–1200 LOC of getter/validator/duplication boilerplate.
A fresh inventory (2026-05-30) finds 19 such getters in ai/daemons/orchestrator/Orchestrator.mjs — up from the 16 the title cites; the tenantRepoSync.* + lms/mlx getters landed after #12100's count. The original title figure is therefore stale; the ledger below is authoritative.
Migration shape: delete the getter, inline its AiConfig.<path> read at each call site. Behaviour-preserving.
Contract Ledger
- Before (uniform):
this.<getter> read inside Orchestrator.mjs.
- After: the inline
AiConfig.<path> expression at point of use (see table).
- Source of authority:
AiConfig (the Tier-1 realm root Neo.ai.Config).
- Consumers: internal only — ~25
this.<getter> reads in Orchestrator.mjs (task-definition options ~478–486, scheduling configs, chroma reaper 637/655/695, poll loop 918, start-log 556, swarm-heartbeat wiring 545–546). No external consumers — the ai/daemons/kb-*/Kb*Service.mjs this.pollIntervalMs fields are independent (set from their own config), and the TaskDefinitions.mjs / scheduling/*.mjs matches are @param docs for the passed options, not the getters. Verified by grep 2026-05-30.
- Tests:
test/playwright/unit/ai/daemons/orchestrator/Orchestrator.invariants.spec.mjs + Orchestrator.spec.mjs — confirm none assert via a deleted getter; update any that do.
| # |
Getter surface (deleted) |
After — inline read |
| 1 |
pollIntervalMs |
AiConfig.orchestrator.intervals.pollMs |
| 2 |
summarySweepIntervalMs |
AiConfig.orchestrator.intervals.summarySweepMs |
| 3 |
kbSyncIntervalMs |
AiConfig.orchestrator.intervals.kbSyncMs |
| 4 |
backupIntervalMs |
AiConfig.orchestrator.intervals.backupMs |
| 5 |
primaryDevSyncIntervalMs |
AiConfig.orchestrator.intervals.primaryDevSyncMs |
| 6 |
tenantRepoSyncIntervalMs |
AiConfig.orchestrator.intervals.tenantRepoSyncMs |
| 7 |
tenantRepoSyncSweepCadenceMs |
AiConfig.orchestrator.tenantRepoSync.sweepCadenceMs |
| 8 |
tenantRepoSyncJitterRatio |
AiConfig.orchestrator.tenantRepoSync.jitterRatio |
| 9 |
dreamIntervalMs |
AiConfig.orchestrator.intervals.dreamMs |
| 10 |
goldenPathIntervalMs |
AiConfig.orchestrator.intervals.goldenPathMs |
| 11 |
swarmHeartbeatIntervalMs |
AiConfig.orchestrator.intervals.swarmHeartbeatMs |
| 12 |
swarmHeartbeatTargetSource |
AiConfig.orchestrator.swarmHeartbeat?.targetSource ?? null |
| 13 |
chromaPort |
AiConfig.engines.chroma?.port |
| 14 |
chromaMaxRuntimeMs |
AiConfig.orchestrator.chroma?.maxRuntimeMs |
| 15 |
mlxModel |
AiConfig.orchestrator.mlx?.model |
| 16 |
mlxPort |
AiConfig.orchestrator.mlx?.port |
| 17 |
lmsModel |
AiConfig.orchestrator.lms?.model |
| 18 |
lmsPort |
AiConfig.orchestrator.lms?.port |
| 19 |
lmsHost |
AiConfig.openAiCompatible?.host |
Acceptance Criteria
Out of Scope
- Logic-bearing getters —
mlxEnabled / lmsEnabled (!! coercion), the *Enabled getters (delegate to resolveDeploymentEnabled / resolveCloudOnlyEnabled), swarmHeartbeatIdentity (Env.parseString), and the multi-line swarmHeartbeatExplicitTargets / lmsModels blocks. These are NOT pure delegation; any seam decision is separate.
- Sub-class E (hidden-default-fallback) — #12100 folds it into the Stage-3+ sweep, but it is a distinct ban; track separately if hits surface here.
- Stage 4 services + MCP-server codemod (#12106).
- The thin-child overlay (the reframed #12102).
Related
- Parent Epic: #12101 (Stage 3). Source: Discussion #12100 (sub-class C; §"Stage 3 codemod-sweep prep" — "16 sites … mechanical delete-getter + replace-with-inline-read"). Sibling: #12106 (Stage 4).
- Anti-pattern origin: PR #12098.
Context
Stage 3 of Epic #12101 (Greenfield aiConfig). Sub-class C from the Discussion #12100 consumer ledger: pure delegation getters of the form
get X() { return AiConfig.X.Y }, introduced in PR #12098. Discussion #12100 universal-bans them — "getter is pure indirection; consumer should readaiConfig.X.Yat point of use" — as part of retiring ~900–1200 LOC of getter/validator/duplication boilerplate.A fresh inventory (2026-05-30) finds 19 such getters in
ai/daemons/orchestrator/Orchestrator.mjs— up from the 16 the title cites; thetenantRepoSync.*+lms/mlxgetters landed after #12100's count. The original title figure is therefore stale; the ledger below is authoritative.Migration shape: delete the getter, inline its
AiConfig.<path>read at each call site. Behaviour-preserving.Contract Ledger
this.<getter>read insideOrchestrator.mjs.AiConfig.<path>expression at point of use (see table).AiConfig(the Tier-1 realm rootNeo.ai.Config).this.<getter>reads inOrchestrator.mjs(task-definition options ~478–486, scheduling configs, chroma reaper 637/655/695, poll loop 918, start-log 556, swarm-heartbeat wiring 545–546). No external consumers — theai/daemons/kb-*/Kb*Service.mjsthis.pollIntervalMsfields are independent (set from their own config), and theTaskDefinitions.mjs/scheduling/*.mjsmatches are@paramdocs for the passed options, not the getters. Verified by grep 2026-05-30.test/playwright/unit/ai/daemons/orchestrator/Orchestrator.invariants.spec.mjs+Orchestrator.spec.mjs— confirm none assert via a deleted getter; update any that do.pollIntervalMsAiConfig.orchestrator.intervals.pollMssummarySweepIntervalMsAiConfig.orchestrator.intervals.summarySweepMskbSyncIntervalMsAiConfig.orchestrator.intervals.kbSyncMsbackupIntervalMsAiConfig.orchestrator.intervals.backupMsprimaryDevSyncIntervalMsAiConfig.orchestrator.intervals.primaryDevSyncMstenantRepoSyncIntervalMsAiConfig.orchestrator.intervals.tenantRepoSyncMstenantRepoSyncSweepCadenceMsAiConfig.orchestrator.tenantRepoSync.sweepCadenceMstenantRepoSyncJitterRatioAiConfig.orchestrator.tenantRepoSync.jitterRatiodreamIntervalMsAiConfig.orchestrator.intervals.dreamMsgoldenPathIntervalMsAiConfig.orchestrator.intervals.goldenPathMsswarmHeartbeatIntervalMsAiConfig.orchestrator.intervals.swarmHeartbeatMsswarmHeartbeatTargetSourceAiConfig.orchestrator.swarmHeartbeat?.targetSource ?? nullchromaPortAiConfig.engines.chroma?.portchromaMaxRuntimeMsAiConfig.orchestrator.chroma?.maxRuntimeMsmlxModelAiConfig.orchestrator.mlx?.modelmlxPortAiConfig.orchestrator.mlx?.portlmsModelAiConfig.orchestrator.lms?.modellmsPortAiConfig.orchestrator.lms?.portlmsHostAiConfig.openAiCompatible?.hostAcceptance Criteria
return AiConfig…) fromOrchestrator.mjs.this.<getter>read with the inlineAiConfig.<path>read from the ledger. Behaviour unchanged (same values, read at point of use).Kb*Service/TaskDefinitionsmatches are unrelated and stay as-is.Out of Scope
mlxEnabled/lmsEnabled(!!coercion), the*Enabledgetters (delegate toresolveDeploymentEnabled/resolveCloudOnlyEnabled),swarmHeartbeatIdentity(Env.parseString), and the multi-lineswarmHeartbeatExplicitTargets/lmsModelsblocks. These are NOT pure delegation; any seam decision is separate.Related