PR #16771 merged at exact head e9e54d9f14060f5e2df52a567b2330c47939c88f and resolved #16776. A post-merge exact-head falsifier found two contract gaps in the delivered heap-observation channel. The resolved ticket stays closed; this is its linked successor.
The first gap is a measured false negative in the same direction the merged repair intended to eliminate. On Node v25.9.0, both of these declarations are accepted and apply a 256 MiB old-space ceiling:
The second gap is a boot-boundary totality interval found by source execution order. HeapObservationReporterService.start() promises “returns, never throws” and says its guard wraps config reads, but its production default is evaluated before the try body:
JavaScript evaluates default parameters before entering the function body, so a throwing production config read escapes the advertised boundary. No live AiConfig.heapObservation getter failure has been observed; this is a contract-level hole at a boot-critical boundary, not a claimed incident.
Live latest-open sweep: checked the 20 newest open issues at 2026-08-09T17:00Z; #16810 covers the missing shared mount, not declaration parsing or reporter totality. A live all-state title/content search found no equivalent. Recent A2A claim sweep over 30 messages found no claim on this scope. Knowledge Base retrieval surfaced #16763/#16776 as the delivered contract but no quoted-option successor.
The Problem
1. Valid quoted NODE_OPTIONS becomes “undeclared”
readDeclaredCeiling() currently whitespace-splits raw NODE_OPTIONS and applies an unquoted-token regex. Its JSDoc claims Node accepts no quoting for this flag; the runtime controls above falsify that claim.
“Undeclared” is affirmative evidence that no declaration was found. Returning it for a ceiling demonstrably in force makes deployment drift and heap-budget reasoning wrong in the dangerous direction: a bounded process reads as unbounded.
2. The reporter's total guard begins after the production config read
The current test injects a config object whose enabled getter throws. That proves the in-body read is caught, but it cannot reach the default-parameter evaluation used by production. The PR body disclosed this seam bound, yet the implementation and JSDoc still claim the production config read is inside the guard.
A heap-observation lane is advisory. It must not take down its MCP host while trying to describe memory visibility.
The Architectural Reality
ai/services/shared/processHeapObservation.mjs owns process-local heap evidence and declaration-channel classification. The exact-head structure map places it with the shared process-observation primitives.
ai/mcp/server/shared/services/HeapObservationReporterService.mjs owns cadence publication and the total boot failure envelope. The structure map places it beside shared MCP-server services.
ai/mcp/server/BaseServer.mjs starts the reporter after boot(); knowledge-base and memory-core servers opt in by canonical service key.
ai/daemons/orchestrator/services/DeploymentStateBridgeService.mjs consumes the record but must not compensate for a producer that misclassifies its own declaration.
No new file or service boundary is required. The existing collector and reporter are the correct owners.
The mandatory structure-map command failed in the live worktree because it recursively entered ai/deploy/.neo-ai-data and attempted to read multi-gigabyte local backup JSONL files. The same command against an isolated tracked-source archive of the merged head completed and confirmed the placements above. No local data was changed.
The Fix
Replace whitespace-only NODE_OPTIONS splitting with a bounded parser for the quoting forms Node actually accepts for this flag. Do not execute or shell-evaluate the string.
Recognize at minimum a double-quoted whole option and a double-quoted numeric value. A malformed declaration that names max-old-space-size must fail closed as ambiguous/unreadable, never “undeclared.”
Correct the JSDoc claim about Node quoting and keep ceilingSources: ['node-options'] truthful.
Move the production AiConfig.heapObservation read inside start()'s guarded body. Add a dependency/read seam that can throw at that exact stage without mutating the shared AiConfig singleton.
Preserve the existing collector/reporter/bridge ownership and every no-ratio/no-threshold boundary from #16776.
Valid quoted NODE_OPTIONS ceilings are declared with exact bytes and node-options source
Malformed flag-bearing input is non-affirmative (ambiguous/reasoned unreadable), never undeclared
Correct function JSDoc and measured-form table
Child-process controls plus direct parser assertions; whitespace-split mutation reds
HeapObservationReporterService.start()
#16776 total boot-envelope contract + ADR-0019 use-site config
Production default config resolution occurs inside the total guard
Returns false, optionally WARNs safely, never escapes into host boot
Correct start() JSDoc
Exact default-read throw control without shared-singleton mutation
Decision Record impact
aligned-with ADR 0019; no amendment. Config remains a use-site read from the owning Tier-1 singleton, but the read moves inside the consumer's existing failure envelope. No change to ADR-0025 or ADR-0026 diagnosis/actuation semantics.
Acceptance Criteria
NODE_OPTIONS='"--max-old-space-size=256"' is proven active in a child Node process and readDeclaredCeiling() returns declared, 256 * 1024 * 1024, source node-options.
NODE_OPTIONS='--max-old-space-size="256"' has the same declaration result.
Restoring whitespace-only splitting reds the quoted-form controls.
Malformed input that visibly names the ceiling flag cannot return undeclared.
Retrieval Hint: query_raw_memories("PR 16771 quoted NODE_OPTIONS heap observation total config read"); exact source head e9e54d9f14060f5e2df52a567b2330c47939c88f.
tobiu referenced in commit b2e0ef2 - "fix(heap-observation): a quoted NODE_OPTIONS ceiling is in force, not undeclared (#16819) (#16945) on Aug 11, 2026, 9:08 AM
Context
PR #16771 merged at exact head
e9e54d9f14060f5e2df52a567b2330c47939c88fand resolved #16776. A post-merge exact-head falsifier found two contract gaps in the delivered heap-observation channel. The resolved ticket stays closed; this is its linked successor.The first gap is a measured false negative in the same direction the merged repair intended to eliminate. On Node
v25.9.0, both of these declarations are accepted and apply a 256 MiB old-space ceiling:NODE_OPTIONS='"--max-old-space-size=256"' node -p 'JSON.stringify({execArgv:process.execArgv,limit:require("node:v8").getHeapStatistics().heap_size_limit})' NODE_OPTIONS='--max-old-space-size="256"' node -p 'JSON.stringify({execArgv:process.execArgv,limit:require("node:v8").getHeapStatistics().heap_size_limit})'Both produce
heap_size_limit = 469762048bytes whileprocess.execArgvcontains no ceiling declaration. At the merged head, however:readDeclaredCeiling([], '"--max-old-space-size=256"') // {state: 'undeclared', bytes: null, sources: []}The second gap is a boot-boundary totality interval found by source execution order.
HeapObservationReporterService.start()promises “returns, never throws” and says its guard wraps config reads, but its production default is evaluated before thetrybody:start({serviceKey, dir, writeLog, config = AiConfig.heapObservation}) { try {JavaScript evaluates default parameters before entering the function body, so a throwing production config read escapes the advertised boundary. No live
AiConfig.heapObservationgetter failure has been observed; this is a contract-level hole at a boot-critical boundary, not a claimed incident.Live latest-open sweep: checked the 20 newest open issues at
2026-08-09T17:00Z; #16810 covers the missing shared mount, not declaration parsing or reporter totality. A live all-state title/content search found no equivalent. Recent A2A claim sweep over 30 messages found no claim on this scope. Knowledge Base retrieval surfaced #16763/#16776 as the delivered contract but no quoted-option successor.The Problem
1. Valid quoted
NODE_OPTIONSbecomes “undeclared”readDeclaredCeiling()currently whitespace-splits rawNODE_OPTIONSand applies an unquoted-token regex. Its JSDoc claims Node accepts no quoting for this flag; the runtime controls above falsify that claim.“Undeclared” is affirmative evidence that no declaration was found. Returning it for a ceiling demonstrably in force makes deployment drift and heap-budget reasoning wrong in the dangerous direction: a bounded process reads as unbounded.
2. The reporter's total guard begins after the production config read
The current test injects a
configobject whoseenabledgetter throws. That proves the in-body read is caught, but it cannot reach the default-parameter evaluation used by production. The PR body disclosed this seam bound, yet the implementation and JSDoc still claim the production config read is inside the guard.A heap-observation lane is advisory. It must not take down its MCP host while trying to describe memory visibility.
The Architectural Reality
ai/services/shared/processHeapObservation.mjsowns process-local heap evidence and declaration-channel classification. The exact-head structure map places it with the shared process-observation primitives.ai/mcp/server/shared/services/HeapObservationReporterService.mjsowns cadence publication and the total boot failure envelope. The structure map places it beside shared MCP-server services.ai/mcp/server/BaseServer.mjsstarts the reporter afterboot(); knowledge-base and memory-core servers opt in by canonical service key.ai/daemons/orchestrator/services/DeploymentStateBridgeService.mjsconsumes the record but must not compensate for a producer that misclassifies its own declaration.The mandatory structure-map command failed in the live worktree because it recursively entered
ai/deploy/.neo-ai-dataand attempted to read multi-gigabyte local backup JSONL files. The same command against an isolated tracked-source archive of the merged head completed and confirmed the placements above. No local data was changed.The Fix
NODE_OPTIONSsplitting with a bounded parser for the quoting forms Node actually accepts for this flag. Do not execute or shell-evaluate the string.max-old-space-sizemust fail closed as ambiguous/unreadable, never “undeclared.”ceilingSources: ['node-options']truthful.AiConfig.heapObservationread insidestart()'s guarded body. Add a dependency/read seam that can throw at that exact stage without mutating the sharedAiConfigsingleton.Contract Ledger Matrix
readDeclaredCeiling(execArgv, nodeOptions)NODE_OPTIONSceilings aredeclaredwith exact bytes andnode-optionssourceambiguous/reasoned unreadable), neverundeclaredHeapObservationReporterService.start()false, optionally WARNs safely, never escapes into host bootstart()JSDocDecision Record impact
aligned-with ADR 0019; no amendment. Config remains a use-site read from the owning Tier-1 singleton, but the read moves inside the consumer's existing failure envelope. No change to ADR-0025 or ADR-0026 diagnosis/actuation semantics.Acceptance Criteria
NODE_OPTIONS='"--max-old-space-size=256"'is proven active in a child Node process andreadDeclaredCeiling()returnsdeclared,256 * 1024 * 1024, sourcenode-options.NODE_OPTIONS='--max-old-space-size="256"'has the same declaration result.undeclared.start(), returnsfalse, and does not escape host boot.config.enabledgetter, and it does not mutate sharedAiConfig.Out of Scope
Avoided Traps
NODE_OPTIONSis data. Executing it to obtain tokens creates a command-injection surface.NODE_OPTIONSas a declaration. Only the target flag affects this field.AiConfigin a test. ADR-0019's singleton-bleed mechanism remains prohibited.Related
Origin Session ID: 98ad9827-765c-40f3-b368-2bd0224c9949
Retrieval Hint:
query_raw_memories("PR 16771 quoted NODE_OPTIONS heap observation total config read"); exact source heade9e54d9f14060f5e2df52a567b2330c47939c88f.