LearnNewsExamplesServices
Frontmatter
id16485
titleThe heap-ceiling seam fixture mutates the shared AiConfig singleton
stateClosed
labels
bugaitesting
assigneesneo-opus-ada
createdAtAug 4, 2026, 9:32 AM
updatedAtAug 11, 2026, 12:56 PM
githubUrlhttps://github.com/neomjs/neo/issues/16485
authorneo-opus-grace
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 11, 2026, 12:56 PM

The heap-ceiling seam fixture mutates the shared AiConfig singleton

Closed Backlog/active-chunk-12 bugaitesting
neo-opus-grace
neo-opus-grace commented on Aug 4, 2026, 9:32 AM

Context

Successor to #16480, carrying the residual @neo-gpt-emmy recorded when she approved PR #16479 as Approve+Follow-Up under the temporary incident exception (review).

Filed at @tobiu's direction while the GPT family is near its rate limit. Low priority — the coverage delivered by #16480 is green and the seam it pins is real.

Live latest-open sweep: checked the latest 20 open issues at 2026-08-04T07:29Z; no equivalent found. A2A in-flight claim sweep (all read-states): no competing [lane-claim].

The Problem

The heap-ceiling injection tests added by #16480 set up their fixture by mutating the shared AiConfig singleton:

saved = AiConfig.orchestrator.supervisedTaskHeapMb;
AiConfig.setData('orchestrator.supervisedTaskHeapMb', INJECTED);

ADR-0019 assigns leaf resolution to the config layer and the test-isolation discipline is explicit that a spec must not mutate the singleton. This does not merely violate a style rule — it is unsound for what the test claims:

  • The leaf's value is resolved through the env layer at construction. Writing it afterwards with setData exercises a different path than a deployment does, so the test proves the member can be set, not that the env override resolves.
  • The mutation is only invisible because nothing else in that file reads the leaf during the window. Any future spec added to the same file that resolves config during the mutated window inherits a value it never asked for, and the failure would surface as an unrelated flake.

The restore in afterEach bounds the blast radius; it does not make the setup correct.

The Architectural Reality

  • test/playwright/unit/ai/daemons/orchestrator/Orchestrator.spec.mjs — the #16463 describe block with beforeEach/afterEach AiConfig.setData writes.
  • ai/configBase.mjsparseSupervisedTaskHeapMb, the metadata.parse hook that reads the env var by NAME, which is what makes a fresh-process witness the faithful path.
  • ai/daemons/orchestrator/Orchestrator.mjs:417 — the injection seam under test.
  • ai/daemons/orchestrator/services/ProcessSupervisorService.mjsFALLBACK_SUPERVISED_TASK_HEAP_MB = 384, which equals the leaf default and is why the fixture must use a value no fallback can produce.

The Fix

Replace both setData writes with a fresh Node process in which NEO_SUPERVISED_TASK_HEAP_MB exists before canonical imports, so the leaf resolves through the same env layer a deployment uses. Assert the seam from that process's output.

Retarget the two durable AC anchors (AC-F1, AC-F2) from parent #16463 to the delivering leaf #16480, so the anchors point at the ticket that actually closed them.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback / Error Semantics Docs Evidence
heap-ceiling seam fixture this ticket Env present before canonical imports in a fresh process A process that cannot be spawned FAILS the test; never silently degrades to setData spec JSDoc the injected ceiling reaches the spawned child env
AC anchors #16480 AC-F1/AC-F2 recorded on the delivering leaf ticket bodies anchors resolve to #16480, not #16463

Decision Record impact

aligned-with ADR 0019 — restores the resolution path the ADR assigns to the leaf, in the test as well as in production.

Acceptance Criteria

  • Neither heap-ceiling test calls AiConfig.setData; no spec in the file mutates the shared singleton for this fixture.
  • The env var is present before canonical imports in the spawned process, so the value resolves through the env layer rather than being written after construction.
  • The injected value remains one no fallback can produce (FALLBACK_SUPERVISED_TASK_HEAP_MB and the leaf default are both 384, so 384 cannot be the fixture value).
  • Both mutation receipts are preserved: Orchestrator.mjs:417 → 0 fails the leaf→service witness; runTask's member read → FALLBACK_SUPERVISED_TASK_HEAP_MB fails the service→child witness, and only that one.
  • A process-spawn failure fails the test rather than falling back to an in-process shortcut.
  • AC-F1 and AC-F2 anchors reference #16480.

Out of Scope

  • The L4 half of #16463 (live survival, retained-set, steady-state concurrency). Unchanged and still open.
  • Any production change. #16480 delivered no runtime behaviour and neither does this.

Avoided Traps

  • Keeping setData and "just adding" a fresh-process test. The unsound fixture would remain in the file as a template for the next spec.
  • Using 384 as the injected value. The service fallback and the leaf default are both 384, so the assertion would pass with the injection deleted — the exact vacuity #16480 was shaped to avoid.

Related

Retrieval Hint: supervised task heap ceiling fresh process env witness AiConfig setData isolation ADR-0019

Origin Session ID: 8132018f-e94a-4ef5-bce4-ec788ef56b7e

tobiu referenced in commit 54c38cf - "test(orchestrator): witness the heap ceiling through the env layer, not a singleton mutation (#16485) (#16946) on Aug 11, 2026, 12:56 PM
tobiu closed this issue on Aug 11, 2026, 12:56 PM