---
id: 10842
title: 'Substrate Config Refactor: applyEnv Priority Inversion Fix'
state: CLOSED
labels:
  - ai
  - refactoring
assignees:
  - neo-gemini-pro
createdAt: '2026-05-06T18:36:34Z'
updatedAt: '2026-05-07T00:45:39Z'
githubUrl: 'https://github.com/neomjs/neo/issues/10842'
author: neo-gemini-pro
commentsCount: 0
parentIssue: 10822
subIssues: []
subIssuesCompleted: 0
subIssuesTotal: 0
blockedBy: []
blocking: []
closedAt: '2026-05-07T00:45:39Z'
---
# Substrate Config Refactor: applyEnv Priority Inversion Fix

**Context**
Epic #10822 aims to establish clear environment variable vs configuration priority. We identified a systemic priority inversion where `Config.load()` (`Neo.merge`) inadvertently overwrote environment variable defaults with JSON config values across multiple MCP servers.

**The Problem**
Fragmented, one-off "normalizer" helpers (e.g., in `DeploymentConfig.mjs` and `EmbeddingProviderConfig.mjs`) failed to address the core priority inversion. These helpers were ad-hoc and did not natively support nested keys, strict validation safety, or empty-string preservation.

**The Architectural Reality**
The triad swarm reached consensus in Discussion #10840: we must shift to a single source-of-truth `applyEnv()` reflex. This pattern uses a flat, dot-path ledger (`envBindings`) mapping environment variables to configuration keys, combined with explicit parsing functions (e.g., `parsePort`, `parseBool`) to guarantee runtime safety.

**The Fix**
Implement the new `EnvConfig.mjs` utility and strict parsers, migrate all MCP server `config.template.mjs` files to use the `envBindings` map, and completely deprecate the legacy helper files.

**Acceptance Criteria**
- [ ] New `EnvConfig.mjs` helper (`applyEnvBindings` + `setDeep` + parsers) created in `ai/mcp/server/shared/helpers/`.
- [ ] Tier 1 `ai/config.template.mjs` migrates to `envBindings` (no `process.env` in defaults).
- [ ] MC + KB `config.template.mjs` migrate to `envBindings`.
- [ ] Delete `DeploymentConfig.mjs` 5 helpers + `EmbeddingProviderConfig.mjs` entire file.
- [ ] Unit suite: all rejection paths + nested-write + empty-string preservation.
- [ ] Integration suite: MC + KB load-config-then-env-override.
- [ ] Final migration audit: zero `process.env.X` reads in config templates for env-bound keys (or explicit justification).

**Origin Session ID**
`88a6ed3a-b1b9-461a-aaf3-7c9984bd12e7`

## Timeline

- 2026-05-06T18:36:36Z @neo-gemini-pro added the `ai` label
- 2026-05-06T18:36:37Z @neo-gemini-pro added the `refactoring` label
- 2026-05-06T18:36:43Z @neo-gemini-pro added parent issue #10822
- 2026-05-06T18:37:07Z @neo-gemini-pro assigned to @neo-gemini-pro
- 2026-05-06T23:49:42Z @neo-opus-ada cross-referenced by #10862
- 2026-05-07T00:22:58Z @neo-gemini-pro cross-referenced by PR #10863
- 2026-05-07T00:26:29Z @neo-gemini-pro referenced in commit `7227e09` - "fix(mcp): block prototype pollution in setDeep (#10842)"
- 2026-05-07T00:45:39Z @tobiu referenced in commit `e2f7377` - "refactor(mcp): EnvConfig priority inversion fix (#10842) (#10863)

* refactor(mcp): EnvConfig utility and config template migration (#10842)

* fix(mcp): block prototype pollution in setDeep (#10842)

* fix(mcp): resolve review nits for parseNumber and parseString

---------

Co-authored-by: neo-gemini-pro <neo-gemini-pro@users.noreply.github.com>"
- 2026-05-07T00:45:39Z @tobiu closed this issue
- 2026-05-11T19:53:36Z @neo-opus-ada cross-referenced by #10822

