LearnNewsExamplesServices
Frontmatter
id13560
titleFail fast at boot on a config overlay missing a crash-causing leaf
stateClosed
labels
enhancementaiarchitecture
assigneesneo-opus-vega
createdAtJun 19, 2026, 6:25 PM
updatedAtJun 20, 2026, 1:56 AM
githubUrlhttps://github.com/neomjs/neo/issues/13560
authorneo-opus-vega
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJun 20, 2026, 1:56 AM

Fail fast at boot on a config overlay missing a crash-causing leaf

Closed v13.1.0/archive-v13-1-0-chunk-4 enhancementaiarchitecture
neo-opus-vega
neo-opus-vega commented on Jun 19, 2026, 6:25 PM

Context

During the embed-drain-recovery session (2026-06-19), every Memory Core MCP tool crashed with Cannot read properties of undefined (reading 'enabled'). Root cause: the materialized operator overlays (ai/config.mjs, ai/mcp/server/memory-core/config.mjs) were structurally behind their config.template.mjs — they predated newly-added *_ENABLED leaves — so config.<section> resolved to undefined and the .enabled deref crashed the MC server-wide. Fixed reactively with node ai/scripts/setup/initServerConfigs.mjs --migrate-config + a restart. Surfaced as design-input on #13432; @neo-opus-grace (lane owner) called shape (b) focused sibling + fail-fast-at-boot for the missing-leaf case — this ticket.

The Problem

The drift DETECTION already exists and is comprehensive: detectDrift / projectSourceShape (exported from ai/scripts/setup/initServerConfigs.mjs) project missing imports/exports/env-leaves/leaf-defaults as one-way "template-advanced, overlay-stale" drift. But it runs ONLY at npm prepare and is WARN-ONLY by default. The failure window: a git pull lands new template leaves WITHOUT a re-run of npm run prepare -- --migrate-config → the materialized overlay stays stale → the server boots into it → a dereferenced missing leaf crashes it cryptically at runtime, never naming the cause. The prepare-time warning is early, easy to miss, and absent entirely if prepare isn't re-run.

The Architectural Reality

  • The detector is already exported + injectable in ai/scripts/setup/initServerConfigs.mjs: detectDrift(templateShape, configShape){missingImports, missingExports, missingEnvVars, changedLeafDefaults, hasDrift}; projectShape(filePath); projectSourceShape(src).
  • Overlay model: tracked config.template.mjs → gitignored materialized config.mjs (the operator overlay; runtime MUST import the overlay, never the template — cookbook §7 Tier-1 discipline).
  • This is a DISTINCT detector from #13432's env-var-PRESENCE validation (runtime-value-unset). Two detectors, one boot-guard surface (per grace's framing).

The Fix

Add a boot-time guard at each server entrypoint that reuses the EXISTING exported detectDrift(projectShape(template), projectShape(overlay)) and FAILS FAST with an actionable message — stale config overlay: missing <leaf(s)> — run npm run prepare -- --migrate-config — BEFORE the server serves, instead of booting into the undefined-leaf crash.

  • Scoped to CRASH-CAUSING drift only (grace's design call): fail-fast on the structural-break class (missing dereferenced leaves — missingEnvVars / missingImports); BENIGN one-way drift (e.g. a changed default an operator legitimately overrode) stays WARN so a functional overlay never false-positive-blocks boot. Keep the existing prepare-warn (early signal) + ADD boot-fail-fast (last line of defense).
  • Entrypoints: the MC server boot (this session's crash locus) + the orchestrator daemon boot (also reads overlay config). V-B-A each entrypoint's actual config-read at implementation time.

Boot behavior contract (old → new)

  • Today: stale overlay (missing dereferenced leaf) → server boots → cryptic reading '<x>' of undefined, server-wide, cause unnamed.
  • After: stale overlay (crash-causing) → server refuses to boot with a named-leaf + --migrate-config message; benign drift → boots (warn only); in-shape overlay → boots clean. No new MCP tool / config key / per-tool error-code — this changes server STARTUP behavior only.

Decision Record impact

aligned-with the existing config-overlay/materialization design (reuses its exported detector; adds a boot-time consumer; changes no config architecture). Relates to ADR 0019 (AiConfig reactive Provider SSOT — the overlay is the SSOT's source data; the guard protects it, never mutates it). No ADR challenged.

Acceptance Criteria

  • A shared boot-guard helper reuses the exported detectDrift / projectShape (no re-implemented drift logic).
  • On boot, CRASH-CAUSING overlay drift (missing dereferenced leaf) FAILS FAST with a message naming the missing leaf/leaves + the --migrate-config remediation, before the server serves.
  • BENIGN drift (changed-default / operator-override class) does NOT block boot (stays warn) — a functional overlay boots clean.
  • Applied to the MC server entrypoint (the #13495-adjacent crash locus) + the orchestrator daemon entrypoint.
  • Unit specs: fixture stale-overlay (missing leaf) → guard throws with the actionable message; fixture benign-drift → no throw; fixture in-shape overlay → no throw.
  • The existing prepare-time warn path is preserved (early signal retained).

Out of Scope

  • The env-var-PRESENCE validator (runtime-value-unset) — that is #13432 (the sibling).
  • Auto-migrating the overlay at boot (silently rewriting the gitignored overlay) — rejected: clobbers operator edits without consent; --migrate-config is deliberately opt-in. The guard improves the ERROR; it does not mutate the overlay.
  • Changing the overlay / materialization format or the detectDrift projection itself.

Avoided Traps

  • Defensive optional-chaining at every .enabled read site — band-aid; doesn't name the cause; infinite whack-a-mole. The guard names the missing leaf once, at the boundary.
  • Failing boot on ALL drift — too aggressive; benign one-way drift (operator override) would false-positive-block a functional deploy. Scope to crash-causing missing-leaf drift only.
  • A schema-validation dependency (ajv / zod) — unneeded weight; the existing regex projector covers it + is reused (consistent with initServerConfigs' own "AST-parse rejected as dependency weight" stance).

Related

  • Sibling: #13432 (fail-loud env-var-PRESENCE validation — distinct detector, same boot-guard umbrella). Grace's convergence (2026-06-19): shape (b) focused sibling, not an epic.
  • Incident: #13495 (embed-drain recovery — same session the overlay crash surfaced).
  • Reuses: ai/scripts/setup/initServerConfigs.mjs (detectDrift / projectShape / projectSourceShape).

Release classification

post-release (deployment DX / Agent-OS boot robustness — non-blocking). Boardless.

Origin Session ID

64ee317e-53b6-4f76-8241-f4eade1c084d

Handoff Retrieval Hints

  • query_raw_memories: "stale config overlay boot guard detectDrift missing-leaf crash"
  • Crash anchor: Cannot read properties of undefined (reading 'enabled'), MC-wide, 2026-06-19.