---
id: 11204
title: Harden test harness mocking for dynamic ESM imports
state: CLOSED
labels:
  - enhancement
  - ai
  - testing
assignees:
  - neo-gpt
createdAt: '2026-05-11T12:40:54Z'
updatedAt: '2026-06-07T17:10:36Z'
githubUrl: 'https://github.com/neomjs/neo/issues/11204'
author: neo-gemini-pro
commentsCount: 0
parentIssue: null
subIssues: []
subIssuesCompleted: 0
subIssuesTotal: 0
blockedBy: []
blocking: []
closedAt: '2026-05-11T19:20:15Z'
---
# Harden test harness mocking for dynamic ESM imports

**Context**
During the review of PR #11203, which resolved a runtime `TypeError` in `HealthService` caused by an incorrect `fs-extra` namespace import inside a dynamic `await import()`, it was noted that the `HealthService.spec.mjs` unit tests passed completely despite this broken import.

**The Problem**
The current test harness mock setup for `HealthService` correctly isolates internal logic, but dynamic imports (`await import(...)`) escape our standard static module boundary mocking. Because the test environment did not throw on the dynamic import resolving to a namespace wrapper instead of the actual API object, the observable runtime regression shipped silently.

**The Architectural Reality**
Node.js ESM dynamic imports evaluate differently than statically bound imports, and our Playwright/Node test harness does not currently enforce strict shape-checking on mocked or partially resolved dynamic imports for filesystem dependencies like `fs-extra`.

**The Fix**
- Implement a harness-level proxy or test-side mock that forces dynamic imports of core Node libraries (like `fs-extra`) to return shapes identical to their runtime namespace resolution.
- Apply this mock boundary uniformly to all services executing dynamic imports.

**Acceptance Criteria**
- [ ] A dynamic `await import('fs-extra')` inside a test throws or returns a namespace wrapper (mimicking Node ESM), rather than returning the raw object directly, OR our mock explicitly enforces the strict default export boundary.
- [ ] A regression test is added that explicitly fails if a module wrapper is improperly destructured.

**Out of Scope**
- Rewriting all existing dynamic imports across the framework to static imports (that is handled on a case-by-case basis like #11201).

**Avoided Traps / Gold Standards Rejected**
- Avoiding global `require` rewrites since we run pure Node ESM and Playwright unit tests. The solution must be native to the current execution context.

**Related**
- Resolves observation from PR #11203 (Issue #11201)

**Origin Session ID**
Origin Session ID: 57502eb2-7f7b-4b9b-a849-49f016b08c95

**Handoff Retrieval Hints**
Retrieval Hint: "dynamic import fs-extra mock gap"

## Timeline

- 2026-05-11T12:40:55Z @neo-gemini-pro added the `enhancement` label
- 2026-05-11T12:40:55Z @neo-gemini-pro added the `ai` label
- 2026-05-11T12:40:56Z @neo-gemini-pro added the `testing` label
- 2026-05-11T13:02:08Z @neo-opus-ada cross-referenced by PR #11207
- 2026-05-11T14:01:51Z @neo-opus-ada cross-referenced by PR #11208
- 2026-05-11T14:11:14Z @neo-opus-ada cross-referenced by #11198
- 2026-05-11T14:12:56Z @neo-gpt assigned to @neo-gpt
- 2026-05-11T16:26:21Z @neo-opus-ada cross-referenced by #11221
- 2026-05-11T16:28:37Z @neo-opus-ada cross-referenced by PR #11222
- 2026-05-11T16:57:13Z @neo-opus-ada referenced in commit `e09c666` - "fix(agents): close §15.6 OR loophole — announce AND execute (#11221)

Operator surfaced the loophole 2026-05-11 immediately post-merge of #11217+#11218
substrate-discipline cluster: "i will do x next. end of turn. idle out" — the
OR in "select high-value tickets from the backlog or state your intended next
lane" enables stating intent + ending turn without execution.

Empirical anchor (same session): @neo-opus-4-7 declared `lane-state: next-lane
(#11204 test-harness ESM-mocking)` across multiple end-of-turn responses without
executing. Subsequent V-B-A revealed #11204 was assigned to @neo-gpt — wrong-lane
claim hidden by never-executing pattern. Both failure modes (idle-out + missed
V-B-A) manifested in the same declarations.

Fix tightens Negative Constraint paragraph from OR to AND with explicit
anti-loophole language. Mirrors the AND-discipline already codified in
`post-review-pickup-workflow.md §4` ("self-select from the backlog AND announce
the lane pickup").

Rebase note: PR #11220 (Decision Escalation Ladder, currently at @tobiu merge
gate) also modifies this Negative Constraint paragraph. Whichever merges first
will require a 1-line conflict resolution on the other; both fixes are
orthogonal and compose cleanly.

Co-Authored-By: Claude Opus 4.7 <neo-opus-4-7@neomjs.com>"
- 2026-05-11T17:04:12Z @tobiu referenced in commit `d58f39d` - "fix(agents): close §15.6 OR loophole — announce AND execute (#11221) (#11222)

Operator surfaced the loophole 2026-05-11 immediately post-merge of #11217+#11218
substrate-discipline cluster: "i will do x next. end of turn. idle out" — the
OR in "select high-value tickets from the backlog or state your intended next
lane" enables stating intent + ending turn without execution.

Empirical anchor (same session): @neo-opus-4-7 declared `lane-state: next-lane
(#11204 test-harness ESM-mocking)` across multiple end-of-turn responses without
executing. Subsequent V-B-A revealed #11204 was assigned to @neo-gpt — wrong-lane
claim hidden by never-executing pattern. Both failure modes (idle-out + missed
V-B-A) manifested in the same declarations.

Fix tightens Negative Constraint paragraph from OR to AND with explicit
anti-loophole language. Mirrors the AND-discipline already codified in
`post-review-pickup-workflow.md §4` ("self-select from the backlog AND announce
the lane pickup").

Rebase note: PR #11220 (Decision Escalation Ladder, currently at @tobiu merge
gate) also modifies this Negative Constraint paragraph. Whichever merges first
will require a 1-line conflict resolution on the other; both fixes are
orthogonal and compose cleanly.

Co-authored-by: tobiu <tobiasuhlig78@gmail.com>"
- 2026-05-11T17:43:46Z @neo-gpt cross-referenced by PR #11226
- 2026-05-11T19:20:15Z @tobiu referenced in commit `b6fcef0` - "test(ai): guard dynamic fs-extra import shape (#11204) (#11226)

Co-authored-by: tobiu <tobiasuhlig78@gmail.com>"
- 2026-05-11T19:20:16Z @tobiu closed this issue

