LearnNewsExamplesServices
Frontmatter
id14239
titleADR-0019: module-level AiConfig const-captures go stale → can block self-heal (convert self-heal-path consumers to use-site reads)
stateClosed
labels
aiarchitecture
assigneesneo-gpt
createdAtJun 27, 2026, 3:37 PM
updatedAtJun 28, 2026, 12:01 AM
githubUrlhttps://github.com/neomjs/neo/issues/14239
authorneo-opus-vega
commentsCount3
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJun 28, 2026, 12:01 AM

ADR-0019: module-level AiConfig const-captures go stale → can block self-heal (convert self-heal-path consumers to use-site reads)

neo-opus-vega
neo-opus-vega commented on Jun 27, 2026, 3:37 PM

Problem

Module-level const X = aiConfig.leaf captures freeze a REACTIVE AiConfig leaf at import-time. The self-heal actuator (and operator / --migrate-config) setDatas configs at RUNTIME — so a frozen capture is stale and silently defeats the change. Operator (2026-06-27):

"state providers are reactive. healing scripts can MUTATE configs at run-time. if you still STORE state provider leaves inside const values, they are stale. this can block self healing. the goal of ADR 19 is to USE AiConfig directly at consumer level."

A guardian blast-radius sweep found ~20 module-level AiConfig const-captures across ai/ — systemic, not isolated. Precedent: #14238 (Ada — the self-heal pipeline's producer/lease captures; the operator-elevated prio-0 instance).

Scope (prioritized)

  • P0 — self-heal-path / heal-mutated consumers (the actual self-heal blockers): convert to use-site reads (inline, or a function-local alias re-read per call). e.g. ai/services/memory-core/MemoryService.mjs (~8: retry / timeout / drain-interval captures) + audit the orchestrator + memory-core self-heal consumers. #14238 is the pattern.
  • P1 — lower-risk ADR-0019 cleanup (heal-immutable): stable-path captures (neoRootDir, storagePaths.graph, datasets.*), github-workflow syncer config (issueSync / pullRequest), queryScoreWeights. The heal does not mutate these — fix opportunistically, do not churn as P0.

Acceptance Criteria

  • P0 self-heal-path consumers read AiConfig at the use-site (no module-load captures of heal-mutated leaves).
  • The self-heal soak (#14165) asserts a mutate-config-at-runtime → heal cycle, so a re-introduced stale capture FAILS the gate (regression guard — this is the structural fix, not just the one-time sweep).
  • P1 captures audited + either converted or explicitly noted as stable (heal-immutable).

Avoided traps

  • NOT every const = aiConfig.x is wrong: a FUNCTION-scope alias re-read per call is fine (#14238's maxAttempts); only MODULE-LOAD captures freeze. detect-only producer descriptions are also correct (detect ≠ actuator).
  • Stable-path captures are heal-immutable → P1, don't churn as P0.

Related: #14238 (precedent), #14132 (cutover), #14165 (soak regression-guard), Epic #14039, ADR-0019.

Authored by Vega (@neo-opus-vega · Claude Opus 4.8); guardian blast-radius V-B-A. Filed via gh relay (gh-author neo-opus-ada = shared-token artifact).

tobiu closed this issue on Jun 28, 2026, 12:01 AM
tobiu referenced in commit 88e8d86 - "feat(ai): guard module-scope AiConfig captures (#14239) (#14266) on Jun 28, 2026, 12:01 AM