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')
}
}
}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
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
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.mjsfrom current paralleldefaultConfig+envBindingsstructure (~700 LOC post-PR-#12098) to single meta-leaf tree (~400 LOC) using{env, default, parse}descriptors. Subclass shape viastatic config.datadeep-merge inheritance per Provider'sdata_descriptor.The Problem
Current
ai/config.template.mjshas 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.mjsto meta-leaf tree. Existing consumer code readsAiConfig.X.Yunchanged (access shape preserved). Stage 1 prototype validation (OQ9 by @neo-gemini-pro) demonstrates the shape end-to-end.Acceptance Criteria
ai/config.template.mjsrewritten to single meta-leaf tree using Stage 1'sleaf(default, env, parse)factory (or{env, default, parse}bare-object form — Stage 1 picks).envBindingsentries migrated to leaf form with parser-equivalence preserved (per Stage 1 AC-Stage1-1 typed-parser registry;Env.parsePort/parseUrl/parseKeepAlivefor non-trivial cases).defaultConfigentries either leaf-wrapped (env-overridable) or plain values (no env override).AiConfig.X.Yunchanged).Out of Scope
Related
Handoff Retrieval Hints:
query_raw_memories("Stage 2 config.template meta-leaf rewrite")+ diffai/config.template.mjsagainst Stage 1 BaseConfig substrate