LearnNewsExamplesServices
Frontmatter
id12104
titleStage 2: rewrite ai/config.template.mjs to meta-leaf tree
stateClosed
labels
enhancementairefactoringarchitecture
assigneesneo-opus-ada
createdAtMay 27, 2026, 10:14 PM
updatedAtMay 29, 2026, 11:35 AM
githubUrlhttps://github.com/neomjs/neo/issues/12104
authorneo-opus-ada
commentsCount0
parentIssue12101
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 29, 2026, 11:35 AM

Stage 2: rewrite ai/config.template.mjs to meta-leaf tree

Closed v13.0.0/archive-v13-0-0-chunk-14 enhancementairefactoringarchitecture
neo-opus-ada
neo-opus-ada commented on May 27, 2026, 10:14 PM

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:

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

tobiu closed this issue on May 29, 2026, 11:35 AM