---
id: 12104
title: 'Stage 2: rewrite ai/config.template.mjs to meta-leaf tree'
state: CLOSED
labels:
  - enhancement
  - ai
  - refactoring
  - architecture
assignees:
  - neo-opus-ada
createdAt: '2026-05-27T20:14:00Z'
updatedAt: '2026-05-29T09:35:40Z'
githubUrl: 'https://github.com/neomjs/neo/issues/12104'
author: neo-opus-ada
commentsCount: 0
parentIssue: 12101
subIssues: []
subIssuesCompleted: 0
subIssuesTotal: 0
blockedBy: []
blocking: []
closedAt: '2026-05-29T09:35:40Z'
---
# Stage 2: rewrite ai/config.template.mjs to meta-leaf tree

Authored by Claude Opus 4.7 (Claude Code 1M). Origin Session ID: `ba55de70-c601-448e-9eb4-9c214be1d9c6`. Sub of #12101 (parent Epic).

## Context

Sequentially depends on Stage 1 (BaseConfig substrate landing). Rewrites top-level `ai/config.template.mjs` from current parallel `defaultConfig` + `envBindings` structure (~700 LOC post-PR-#12098) to single meta-leaf tree (~400 LOC) using `{env, default, parse}` descriptors. Subclass shape via `static config.data` deep-merge inheritance per Provider's `data_` descriptor.

## The Problem

Current `ai/config.template.mjs` has two parallel trees that must stay in sync manually:
- `defaultConfig: { orchestrator: { intervals: { pollMs: 60_000 } } }`
- `envBindings: { orchestrator: { intervals: { pollMs: 'NEO_ORCHESTRATOR_POLL_INTERVAL_MS' } } }`

Adding a config requires TWO coordinated edits across separate file regions. Drift waiting to happen.

## The Architectural Reality

Meta-leaf shape per Discussion #12100 §3.1:

```js
class Config extends BaseConfig {
    static config = {
        className: 'Neo.ai.Config',
        data: {
            mcpHttpPort: leaf(3000, 'MCP_HTTP_PORT', Env.parsePort),
            ollama: {
                keep_alive: leaf(-1, 'NEO_OLLAMA_KEEP_ALIVE', Env.parseKeepAlive)
            },
            openAiCompatible: {
                contextLimitTokens: leaf(262144, 'NEO_OPENAI_COMPATIBLE_CONTEXT_LIMIT_TOKENS', Env.parseNumber),
                safeProcessingLimitTokens: leaf(200000, 'NEO_OPENAI_COMPATIBLE_SAFE_PROCESSING_LIMIT_TOKENS', Env.parseNumber),
                keep_alive: leaf(-1, 'NEO_OPENAI_COMPATIBLE_KEEP_ALIVE', Env.parseKeepAlive)
            },
            orchestrator: {
                providerReadiness: {
                    attempts: leaf(120, 'NEO_PROVIDER_READINESS_ATTEMPTS', Env.parseNumber),
                    delayMs: leaf(1_000, 'NEO_PROVIDER_READINESS_DELAY_MS', Env.parseNumber),
                    timeoutMs: leaf(2_000, 'NEO_PROVIDER_READINESS_TIMEOUT_MS', Env.parseNumber)
                }
            },
            graphProvider: leaf('openAiCompatible', 'NEO_GRAPH_PROVIDER', Env.parseString),
            backupPath: path.resolve(neoRootDir, '.neo-ai-data/backups')  // plain value, no env
        }
    }
}
```

Each leaf carries default + env-binding + parser in one entry. Adding a config = 1 line.

## The Fix

Full rewrite of `ai/config.template.mjs` to meta-leaf tree. Existing consumer code reads `AiConfig.X.Y` unchanged (access shape preserved). Stage 1 prototype validation (OQ9 by @neo-gemini-pro) demonstrates the shape end-to-end.

## Acceptance Criteria

- [ ] `ai/config.template.mjs` rewritten to single meta-leaf tree using Stage 1's `leaf(default, env, parse)` factory (or `{env, default, parse}` bare-object form — Stage 1 picks).
- [ ] All current `envBindings` entries migrated to leaf form with parser-equivalence preserved (per Stage 1 AC-Stage1-1 typed-parser registry; `Env.parsePort` / `parseUrl` / `parseKeepAlive` for non-trivial cases).
- [ ] All current `defaultConfig` entries either leaf-wrapped (env-overridable) or plain values (no env override).
- [ ] LOC delta: ~700 → ~400 (target -300 LOC).
- [ ] Test suite passes against new shape (consumers reading `AiConfig.X.Y` unchanged).
- [ ] Per-MCP-server config rewrites NOT in Stage 2 scope (Stage 4 owns).

## Out of Scope

- Per-MCP-server config rewrites (Stage 4 owns)
- Consumer-side codemod (Stage 3 + 4 own)
- Stage 0 operator-overlay drift extension (separate sub)

## Related

- **Parent Epic**: #12101
- **Blocked by**: Stage 1 (BaseConfig substrate must land first)
- **Discussion**: #12100 §3.1 + Stage 2 v14

Handoff Retrieval Hints: `query_raw_memories("Stage 2 config.template meta-leaf rewrite")` + diff `ai/config.template.mjs` against Stage 1 BaseConfig substrate

## Timeline

- 2026-05-27T20:14:02Z @neo-opus-ada added the `enhancement` label
- 2026-05-27T20:14:02Z @neo-opus-ada added the `ai` label
- 2026-05-27T20:14:02Z @neo-opus-ada added the `refactoring` label
- 2026-05-27T20:14:02Z @neo-opus-ada added the `architecture` label
- 2026-05-27T20:15:34Z @neo-opus-ada added parent issue #12101
- 2026-05-27T21:56:22Z @neo-opus-ada cross-referenced by #12114
- 2026-05-27T22:15:10Z @neo-opus-ada cross-referenced by PR #12115
- 2026-05-28T05:06:11Z @neo-gpt cross-referenced by #12103
- 2026-05-29T02:02:13Z @neo-opus-ada assigned to @neo-opus-ada
- 2026-05-29T03:23:35Z @neo-opus-ada cross-referenced by PR #12164
- 2026-05-29T09:35:40Z @tobiu closed this issue

