LearnNewsExamplesServices
Frontmatter
id14238
titleMemoryService config consts — module-load capture → use-site reads (reactive AiConfig for self-heal)
stateClosed
labels
enhancementairefactoringarchitecture
assigneesneo-opus-ada
createdAtJun 27, 2026, 3:31 PM
updatedAtJun 27, 2026, 3:59 PM
githubUrlhttps://github.com/neomjs/neo/issues/14238
authorneo-opus-ada
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJun 27, 2026, 3:59 PM

MemoryService config consts — module-load capture → use-site reads (reactive AiConfig for self-heal)

Closed v13.1.0/archive-v13-1-0-chunk-7 enhancementairefactoringarchitecture
neo-opus-ada
neo-opus-ada commented on Jun 27, 2026, 3:31 PM

Context

@tobiu (prio-0, on #14234's merge): "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." #14234 (the #14231 AiConfig pass) moved MemoryService's config constants into a new aiConfig.memoryService section but captured them as MODULE-LEVEL consts — a stale snapshot a runtime healing-mutation never reaches.

The Problem

The v13.1 self-heal pipeline does setData on AiConfig at runtime to recover (e.g., a recovery actuator adjusting a timeout / retry budget under contention). A module-level const MINI_SUMMARY_TIMEOUT_MS = aiConfig.memoryService.miniSummaryTimeoutMs; is read ONCE at module-load → frozen → the healing-mutation is invisible to it → blocks self-healing. ADR-0019 §2: "READ resolved leaves AT THE USE SITE." B2's "alias only if used 3+ times in one scope" is a FUNCTION-LOCAL alias (re-read per call), NOT a module-level capture (the misread that produced #14234's shape).

The Architectural Reality

8 module-level captures in ai/services/memory-core/MemoryService.mjs:21-78: miniSummaryTimeoutMs, miniSummaryBackfillMaxRunMs, miniSummaryBackfillFreshReserve, chromaFetchTimeoutMs, graphProjectionMaxAttempts, graphProjectionRetryBaseMs, graphProjectionRetryMaxMs, graphProjectionDrainIntervalMs. Use sites: listMemories default-param (:900), backfillMiniSummaries (:1627/:1637/:1662/:1663/:1706), _scheduleMemoryGraphProjection (:590/:600/:601/:606), _startGraphProjectionDrainLoop (:681).

The Fix

Delete the 8 module-level const-defs; read aiConfig.memoryService.* at the use site — inline for 1-2 uses; a function-local alias (re-read per call) for 3+ in one method (graphProjectionMaxAttempts ×3 in _scheduleMemoryGraphProjection); the listMemories default-param chromaTimeoutMs = aiConfig.memoryService.chromaFetchTimeoutMs is use-site (re-evaluated per call). Behavior-preserving (same resolved values).

Decision Record impact

aligned-with ADR-0019 — the use-site read-gate; corrects a B2 misread (module-load capture of a reactive leaf).

Acceptance Criteria

  • 0 module-level const X = aiConfig.memoryService.* in MemoryService.mjs.
  • Every consumer reads aiConfig.memoryService.* at the use site (inline, or a function-local alias re-read per call) — a runtime setData is reflected.
  • Behavior-preserving; MemoryService / SessionService specs pass.

Out of Scope

  • Other repo-wide module-load captures (QueryService/DatabaseService neoRootDir, the github-workflow syncers' issueSync/pullRequest, analyzeNlTelemetry paths) — a separate systemic sweep; those are boot-static paths / sync-config with lower self-heal relevance. File separately if warranted.

Related

#14234 (the PR that introduced the captures), #14231 (the AiConfig pass), #14193 (de-dup epic, same file), ADR-0019 (the use-site read-gate).

Origin Session ID: f4bc5569-9c5f-477b-a810-7fb084867d6a

Authored by Ada (@neo-opus-ada · Claude Opus 4.8, Claude Code).

tobiu referenced in commit 4a0715b - "refactor(ai): read MemoryService config leaves at the use site, not module-load consts (ADR-0019) (#14238) (#14241) on Jun 27, 2026, 3:59 PM
tobiu closed this issue on Jun 27, 2026, 3:59 PM