---
number: 12189
title: >-
  refactor(ai): Stage-5 — make Neo.util.Env BaseConfig-internal; retract
  consumer doctrine + applyEnvBindings (Resolves #12107)
author: neo-opus-ada
state: MERGED
createdAt: '2026-05-30T01:12:01Z'
updatedAt: '2026-05-30T04:55:48Z'
closedAt: '2026-05-30T04:55:45Z'
mergedAt: '2026-05-30T04:55:45Z'
head: agent/12107-env-retraction
base: dev
url: 'https://github.com/neomjs/neo/pull/12189'
---
## Summary

Stage 5 of Epic #12101 — `Neo.util.Env` becomes genuinely BaseConfig-internal. The `Env.parseX('NEO_X') ?? AiConfig.X` consumer doctrine + the bulk `applyEnvBindings` are retracted; the lone external consumer (the orchestrator's `swarmHeartbeatIdentity`) is migrated off the parsers. After this, **`BaseConfig` is the sole prod importer** of `Env.mjs`.

## Deltas

- **Env.mjs** — top JSDoc reframed from "canonical env-parsing home for all consumers" to "internal typed-parser registry for BaseConfig; not consumer-facing" (links `learn/agentos/AiConfigModel.md`). Deleted `applyEnvBindings` (0 prod callers). Fixed the `parseNumber` note that referenced the `?? AiConfig.X` consumer pattern. The 6 parser fns stay — BaseConfig's `typeParsers` map wires them into the meta-leaf registry.
- **Orchestrator.mjs** — `swarmHeartbeatIdentity` now reads `process.env.NEO_AGENT_IDENTITY?.trim()` directly (behaviour-equivalent to `Env.parseString`), matching the 6 other `NEO_AGENT_IDENTITY` consumers; `NEO_AGENT_IDENTITY` is a runtime identity, not a config-tunable. Removed the now-dead `import Env`; refreshed the stale `(D)` operator-policy class-doc (Stage-3 inlined the intervals it described).
- **Env.spec.mjs** — dropped the `applyEnvBindings` describe (73 lines); the parser tests are unchanged.

Net −140 lines.

## Intake correction

The ticket's premise — "ZERO `Env.parseX` consumers remain" — was false: BaseConfig's `typeParsers` map + the orchestrator's `swarmHeartbeatIdentity` keeper both used the parsers. So AC1 ("retract consumer-facing exports") cannot mean *unexport* (BaseConfig imports them) — the retraction is of the consumer **doctrine/framing**, reframing the fns as internal, which makes **AC4 literally true** (BaseConfig is the sole prod importer). The parser-registry restructure (`Env.parsers` vs BaseConfig-internal) is **Stage-1 OQ5**, which this ticket explicitly defers — so it is left untouched here.

## Test Evidence

95 specs pass (`test-unit`): Env.spec (parsers), BaseConfig.spec (leaf-parser wiring), Orchestrator.spec, Orchestrator.invariants.spec.

Evidence: `node --check` clean on all three files; grep confirms 0 residual `Env.parse*`/`applyEnvBindings` in prod outside Env.mjs/BaseConfig; BaseConfig still references all 6 parsers (8 hits = map + 2 fallbacks); Env.mjs still defines all 6.

## Post-Merge Validation

Behaviour-equivalent: `swarmHeartbeatIdentity` returns the trimmed `NEO_AGENT_IDENTITY` or undefined, identical to the retired `Env.parseString` path. No config-template or env-contract change.

FAIR-band: n/a — operator-directed epic lane (#12101 Stage 5).

Resolves #12107.

Authored by Claude Opus 4.8 (Claude Code, 1M context).
