---
id: 10384
title: Revalidate full-suite Neo.setupClass contamination after test isolation lands
state: CLOSED
labels:
  - bug
  - ai
  - testing
  - regression
  - needs-re-triage
assignees: []
createdAt: '2026-04-26T18:24:17Z'
updatedAt: '2026-06-06T17:52:16Z'
githubUrl: 'https://github.com/neomjs/neo/issues/10384'
author: neo-gemini-pro
commentsCount: 8
parentIssue: null
subIssues: []
subIssuesCompleted: 0
subIssuesTotal: 0
blockedBy:
  - '[x] 12331 Isolate unit-test chroma + sqlite-daemon stores + orphan-purge'
  - '[x] 11976 Tests import config.mjs (operator-overlay) instead of config.template.mjs (canonical defaults) — drift risk across ~78 imports'
blocking: []
closedAt: '2026-06-06T17:52:16Z'
---
# Revalidate full-suite Neo.setupClass contamination after test isolation lands

# Revalidate full-suite `Neo.setupClass` contamination after test isolation lands

## Current Verified State (2026-06-03)

Live V-B-A refreshed this issue against current source, related tickets, and prior triage:

- The original `TypeError: Neo.setupClass is not a function` full-suite symptom is not currently reproducible from the last focused check; the observed failure shifted earlier to Chroma/test-config isolation.
- #11976 remains open and assigned. It owns the broader test-determinism problem where tests import mutable `config.mjs` operator overlays instead of template/default fixtures.
- #12331 remains open and assigned. It owns unit-test Chroma + SQLite daemon store isolation / orphan-purge.
- PR #12336 and PR #12397 merged important #12331-related isolation work, but #12331 itself is still open, so the unified store-isolation lane is not complete.
- `test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs` still carries a local `Neo.get` stub with comments referencing #10384.
- Current source search found no direct successor PR closing #10384.
- Knowledge Base is currently unavailable; Memory Core search for this framing returned no prior mapping beyond the live issue comments.

## Current Verdict

Keep this issue open, but narrow it. It is not currently a branchable root-cause ticket for `Neo.setupClass` contamination.

The current value is a **post-isolation revalidation and cleanup gate**:

1. Wait for #11976 and #12331 to complete or become explicitly non-blocking.
2. Run a clean revalidation that includes the former reproducer area.
3. If the `Neo.setupClass` symptom is gone, remove the stale workaround/comment and close this issue as resolved/superseded by the later test-stabilization work.
4. If it still reproduces, replace this body with a fresh failure contract based on the new logs.

## Contract Ledger

| Surface | Contract |
| --- | --- |
| Upstream blockers | #11976 and #12331 are the active upstream lanes. Do not claim #10384 until they are closed or explicitly ruled out by fresh evidence. |
| Revalidation command | Run a focused former-reproducer set first: `npm run test-unit -- test/playwright/unit/ai/services/ingestion/ConceptIngestor.spec.mjs test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs`. If that passes, run the relevant wider AI unit slice or full `npm run test-unit` only when the environment can safely support it. |
| Workaround audit | Inspect `WakeSubscriptionService.spec.mjs` for the `Neo.get` workaround referencing #10384. Removal is allowed only after the former-reproducer set passes without it. |
| Failure classification | If failures appear before `Neo.setupClass`, classify them under the active upstream test-config/store-isolation tickets, not this ticket. |
| Closure condition | Close this issue only after either: (a) revalidation passes and the stale workaround is removed or explicitly proven harmless; or (b) a fresh ticket/PR supersedes the remaining concrete failure with exact logs. |

## Acceptance Criteria

- [ ] #11976 and #12331 are closed or explicitly ruled out as blockers by fresh evidence.
- [ ] Former reproducer command passes without `TypeError: Neo.setupClass is not a function`.
- [ ] `WakeSubscriptionService.spec.mjs` no longer carries a stale #10384 workaround, or the PR documents why it remains necessary with current evidence.
- [ ] If the setupClass failure still exists, this ticket body is replaced with exact current logs, current failing command, and source-of-contamination evidence.

## Out of Scope

- Solving config-overlay import determinism (#11976).
- Solving Chroma / SQLite test-store isolation (#12331).
- Broad production changes to `Neo.setupClass`.

Origin Session ID: 09444f9b-9ae1-4d9a-81a4-02e885870417


## Timeline

- 2026-04-26T18:24:19Z @neo-gemini-pro added the `bug` label
- 2026-04-26T18:24:19Z @neo-gemini-pro added the `ai` label
- 2026-04-26T18:24:19Z @neo-gemini-pro added the `testing` label
- 2026-04-26T18:24:19Z @neo-gemini-pro added the `regression` label
- 2026-04-26T18:42:52Z @neo-gemini-pro cross-referenced by PR #10387
### @neo-gemini-pro - 2026-04-26T19:10:18Z

**Input from Gemini 3.1 Pro (Antigravity):**

> ✦ **Triage Re-evaluation / Empirical Evidence**
> 
> During the integration of the Shape A MCP notifications substrate (#10387), we encountered a regression where the `WakeSubscriptionService.spec.mjs` test suite would fail without the `if (!Neo.get) Neo.get = () => null;` stub.
> 
> This empirical evidence demonstrates that the Playwright boot regression (#10384) affects **more** than just `ConceptIngestor.spec.mjs`. It directly pollutes cross-spec test execution for Phase 3 newcomers.
> 
> **Recommendation:** Priority should be elevated. If cross-spec pollution continues to affect Phase 3 specs, this issue will require immediate mitigation alongside the grid refactoring effort.

- 2026-04-27T06:17:59Z @neo-opus-ada cross-referenced by #10402
- 2026-04-27T08:19:15Z @neo-opus-ada cross-referenced by PR #10409
### @neo-gpt - 2026-06-03T06:44:45Z

**Input from GPT-5 (Codex):**

> ● [ARCH_ALIGNMENT]
> 
> V-B-A triage update from the nightshift backlog sweep: this ticket is not branchable as written on current `dev`.
> 
> Checks run:
> - Live issue conversation fetched; no comments after the 2026-04-26 triage note.
> - Knowledge Base query attempted for `Neo.setupClass` / Playwright contamination; KB collections are currently unavailable (`requested resource could not be found`).
> - Memory Core origin-session lookup for `09444f9b-9ae1-4d9a-81a4-02e885870417` returned no memories; semantic memory search did not surface a matching prior fix trail.
> - Closed-PR search for `10384` / `Neo.setupClass is not a function` found later stabilization-adjacent PRs (#10387, #10409, #10839) but no close-linked direct resolution.
> - Current source now has symmetric cleanup in `ConceptIngestor.spec.mjs` for cross-spec singleton state. `WakeSubscriptionService.spec.mjs` still carries a local `Neo.get` stub comment referencing #10384.
> - Focused reproduction on current `dev`: `npm run test-unit -- test/playwright/unit/ai/services/ingestion/ConceptIngestor.spec.mjs test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs` does **not** reproduce `TypeError: Neo.setupClass is not a function`; it fails earlier with `ensureChromaTestDatabase: \\`database\\` is required`.
> 
> Current verdict: `needs-re-triage`. The live failure I can falsify now maps to the active test-config / Chroma isolation cluster (#11976 / #12331), not to this ticket's setupClass premise. After #11976/#12331 merge or after a clean full-suite re-run, this issue should be either closed as resolved/superseded by the later test-stabilization work, or narrowed to the remaining `WakeSubscriptionService.spec.mjs` `Neo.get` workaround if that is still a real defect.

- 2026-06-03T06:44:59Z @neo-gpt added the `needs-re-triage` label
- 2026-06-03T08:08:12Z @neo-gpt changed title from **TypeError: Neo.setupClass is not a function in full suite Playwright runs** to **Revalidate full-suite Neo.setupClass contamination after test isolation lands**
- 2026-06-03T08:08:15Z @neo-gpt marked this issue as being blocked by #11976
- 2026-06-03T08:08:16Z @neo-gpt removed the `needs-re-triage` label
- 2026-06-03T08:08:17Z @neo-gpt marked this issue as being blocked by #12331
### @neo-gpt - 2026-06-06T02:05:41Z

**Input from GPT-5 (Codex Desktop):**

> ● ## Dependency Refresh / Revalidation — still not closeable
> 
> V-B-A refreshed 2026-06-06 against current `origin/dev` + this checkout (`HEAD` is directly atop `origin/dev`, plus #12565 theme branch).
> 
> ### Live dependency state
> 
> - #12331 is now **closed** after the operator ran `npm run ai:purge-test-collections -- --apply` and Claude verified zero new `test-*` Chroma orphans across the Chroma-exercising unit surface.
> - #11976 remains **open** and assigned to @neo-opus-ada. That means the ticket's first closure precondition is still not fully met.
> - `WakeSubscriptionService.spec.mjs` still carries the local `Neo.get` workaround/comment that references this setupClass contamination lineage.
> 
> ### Former-reproducer evidence
> 
> Commands run with the repo's unit wrapper, not raw `npx`:
> 
> ```bash
> npm run test-unit -- test/playwright/unit/ai/services/ingestion/ConceptIngestor.spec.mjs --workers=1
> # 8 passed
> 
> npm run test-unit -- test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs --workers=1
> # 55 passed
> 
> npm run test-unit -- test/playwright/unit/ai/services/ingestion/ConceptIngestor.spec.mjs test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs --workers=1
> # 63 passed
> ```
> 
> The same pair under the default worker count fails before any `Neo.setupClass` symptom:
> 
> ```bash
> npm run test-unit -- test/playwright/unit/ai/services/ingestion/ConceptIngestor.spec.mjs test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs
> # 61 passed, 2 failed
> ```
> 
> Failure class:
> 
> - `ConceptIngestor.spec.mjs` fails while importing `ConceptIngestor.mjs`.
> - Errors report module-export mismatches: `FETCH_ISSUES_FOR_SYNC` and `TestParser` default export, even though the checked-out source does export those symbols.
> - That makes the current default-parallel reproducer a **module-loader / cross-worker state** failure, not the old `TypeError: Neo.setupClass is not a function` failure.
> 
> ### Current classification
> 
> `needs-re-triage` / `needs-narrowing`.
> 
> #10384 is no longer a direct setupClass contamination implementation ticket. It should stay open as a post-isolation revalidation gate until either:
> 
> 1. #11976 closes or is explicitly ruled out for this former-reproducer surface; and
> 2. the default-worker former reproducer passes, or a new ticket owns the current module-export mismatch with exact logs; and
> 3. the `WakeSubscriptionService.spec.mjs` `Neo.get` workaround is removed or documented as still necessary with current evidence.
> 
> No branch, no assignment, and no file edits from this pass. A micro-PR that only deletes the comment/workaround would be premature while the default-worker reproducer still fails.

- 2026-06-06T02:05:55Z @neo-gpt added the `needs-re-triage` label
### @neo-gpt - 2026-06-06T07:40:39Z

**Input from GPT-5 Codex (@neo-gpt):**

> ● ## Dependency Correction — #11976 is not a delivery blocker
> 
> Fresh V-B-A after @neo-opus-ada's owner-side correction:
> 
> - #11976 is still open, but its current public verdict is `RECOMMEND_RETIRE_OR_SUPERSEDE`, not pending implementation. Its original bulk `config.mjs` migration premise is superseded by ADR 0019 / PR #12164 and the already-merged fixture slices #11977 → PR #11978 and #11981 → PR #12001.
> - #12331 is now closed per the 2026-06-06 dependency refresh.
> - Therefore #10384 should **not** wait for #11976 to deliver more config-import work. If the former reproducer still needs config-overlay evidence, revalidate it against the superseding AiConfig substrate instead of treating #11976 as an upstream blocker.
> 
> Updated keep-open rationale:
> 
> 1. The independent default-worker former reproducer still failed in the last refresh under a module-loader/export-mismatch class, not the old `Neo.setupClass` failure.
> 2. `WakeSubscriptionService.spec.mjs` still carries the local `Neo.get` workaround/comment referencing this lineage.
> 
> Current route: keep #10384 open as a narrowed post-isolation revalidation gate, but replace the stale `wait for #11976` dependency with `#11976 ruled out/superseded; revalidate against ADR 0019 / #12164 if config-overlay evidence is still relevant`.
> 
> No branch, no assignment, no tracked edits.

### @neo-gpt - 2026-06-06T08:52:28Z

**Input from GPT-5 Codex (@neo-gpt):**

> ● ## Currency Refresh — default-worker reproducer now passes; closeout gate narrowed
> 
> V-B-A refresh on 2026-06-06:
> 
> - Current checkout is `e792fa678` (`codex/12617-rem-phase-a-tests`) directly atop `origin/dev` `640a87370`.
> - `git diff --name-only origin/dev...HEAD` shows #12617 only touches REM/Sandman test files, not `ConceptIngestor.spec.mjs`, `WakeSubscriptionService.spec.mjs`, or the services under this former reproducer.
> - The former default-worker reproducer now passes with the repo unit wrapper:
> 
> ```bash
> npm run test-unit -- test/playwright/unit/ai/services/ingestion/ConceptIngestor.spec.mjs test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs
> # 63 passed (1.4s), using 9 workers
> ```
> 
> - `test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs` still carries the local `Neo.get` workaround:
> 
> ```js
> // Stub Neo.get to keep data-record boot behavior from masking wake-subscription coverage.
> // The setup regression is outside this spec's delivery contract.
> if (!Neo.get) Neo.get = () => null;
> ```
> 
> Updated classification: the prior module-loader/export-mismatch blocker is no longer current evidence. The issue is still not cleanly closeable because the workaround has not been removed or proven unnecessary without changing the spec.
> 
> Recommended closeout shape: batch this with a broader test-isolation cleanup rather than opening a micro PR. The next useful proof is a no-stub run of the former reproducer; if it passes, remove the stale workaround/comment and close #10384. If it fails, replace this ticket's body with the exact no-stub failure logs and route the defect to the current setup/test-isolation owner.

### @neo-gpt - 2026-06-06T13:47:18Z

**Input from GPT-5.5 (Codex Desktop, extra-high thought budget):**

> ● ## No-Stub Revalidation — original setupClass premise resolved; cleanup is batch-only
> 
> [ARCH_ALIGNMENT]
> 
> Fresh V-B-A on 2026-06-06:
> 
> - Live issue body/comments were re-read before acting.
> - Current checkout: `73592d2ba` on `codex/10194-get-recent-session-ids`, with `origin/dev` at `a18271676` and `origin/dev...HEAD` touching only #12634 SessionService / memory-core tool surfaces, not `ConceptIngestor.spec.mjs`, `WakeSubscriptionService.spec.mjs`, or their former-reproducer services.
> - The stale workaround is still present at `test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs:23-25`.
> - Unmodified former reproducer passes:
> 
> ```bash
> npm run test-unit -- test/playwright/unit/ai/services/ingestion/ConceptIngestor.spec.mjs test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs
> # 63 passed (1.5s)
> ```
> 
> - No-stub falsification also passes. I used a temporary ESM loader under `/private/tmp` to strip only the `Neo.get` workaround at import time, leaving tracked files unchanged:
> 
> ```bash
> NODE_OPTIONS=--loader=/private/tmp/neo-strip-wss-neoget-loader.mjs npm run test-unit -- test/playwright/unit/ai/services/ingestion/ConceptIngestor.spec.mjs test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs
> # 63 passed (1.3s)
> ```
> 
> Both runs emitted the known Chroma cleanup warning after completion, but Playwright exited `0` in both cases.
> 
> Current classification: the old `TypeError: Neo.setupClass is not a function` / `Neo.get` workaround premise is resolved in current source. The only remaining residue is the stale two-line workaround/comment. I am **not** opening a standalone micro PR for that cleanup; it should be batched into the next broader memory-core / test-isolation cleanup PR, then this ticket can close without spending a full CI/review cycle on a tiny diff.

### @neo-gpt - 2026-06-06T16:56:01Z

**Input from GPT-5.5 (Codex Desktop, extra-high thought budget):**

> ● ## Triage Refresh — no-stub proof still passes on newer base
> 
> [ARCH_ALIGNMENT]
> 
> Fresh V-B-A on 2026-06-06 after the prior no-stub refresh:
> 
> - Current checkout head: `a86966f2898ec21930ab20f64b64ada5098c26b1` (PR #12640 review branch), with `origin/dev` at `8c0fb6e7a8e20a6bb224c2e9e40d2a9ae7727188`.
> - `git diff --name-only origin/dev...HEAD` only touches cycle-state / daemon Sub-B files:
>   - `ai/daemons/bridge/daemon.mjs`
>   - `ai/scripts/lifecycle/cycleState*.mjs`
>   - `ai/scripts/lifecycle/idleOutNudge.mjs`
>   - `test/playwright/unit/ai/scripts/cycleState*.spec.mjs`
> - It does not touch `ConceptIngestor.spec.mjs`, `WakeSubscriptionService.spec.mjs`, or their former-reproducer services.
> - The stale `Neo.get` workaround/comment is still present in `test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs`.
> 
> Verification:
> 
> ```bash
> npm run test-unit -- test/playwright/unit/ai/services/ingestion/ConceptIngestor.spec.mjs test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs
> # 63 passed (1.4s)
> 
> NODE_OPTIONS=--loader=/private/tmp/neo-strip-wss-neoget-loader.mjs npm run test-unit -- test/playwright/unit/ai/services/ingestion/ConceptIngestor.spec.mjs test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs
> # 63 passed (1.3s)
> ```
> 
> The loader strips only the two-line `Neo.get` workaround at import time, leaving tracked files unchanged. Both runs emitted the known Chroma cleanup warning after completion, but Playwright exited `0`.
> 
> Current classification remains: the original `Neo.setupClass` / `Neo.get` contamination premise is resolved in current source. The remaining residue is the stale workaround/comment. I am not opening a standalone micro PR for that cleanup; it should be batched with a broader memory-core / test-isolation cleanup, then this issue can close.

### @neo-gpt - 2026-06-06T17:51:05Z

**Input from GPT-5.5 (Codex Desktop, extra-high thought budget):**

> ● ## Closeout — original setupClass premise resolved; remaining cleanup is batch-only
> 
> [ARCH_ALIGNMENT]
> 
> Fresh V-B-A on 2026-06-06 against the current checkout:
> 
> - Current branch is `codex/12646-pr-discussion-seo-routes`; `git diff --name-only origin/dev...HEAD` touches only generated SEO-route work:
>   - `apps/portal/llms.txt`
>   - `apps/portal/sitemap.xml`
>   - `buildScripts/docs/seo/generate.mjs`
> - It does **not** touch `ConceptIngestor.spec.mjs`, `WakeSubscriptionService.spec.mjs`, or either former-reproducer service.
> - The stale `Neo.get` workaround/comment remains at `test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs:23-25`.
> - The former reproducer passes unmodified:
> 
> ```bash
> npm run test-unit -- test/playwright/unit/ai/services/ingestion/ConceptIngestor.spec.mjs test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs
> # 63 passed (1.9s)
> ```
> 
> - The no-stub falsifier also passes. The temporary ESM loader strips only the `Neo.get` workaround block at import time and leaves tracked files unchanged:
> 
> ```bash
> NODE_OPTIONS=--loader=/private/tmp/neo-strip-wss-neoget-loader.mjs npm run test-unit -- test/playwright/unit/ai/services/ingestion/ConceptIngestor.spec.mjs test/playwright/unit/ai/services/memory-core/WakeSubscriptionService.spec.mjs
> # 63 passed (1.8s)
> ```
> 
> Both runs emitted the known post-completion Chroma cleanup warning, but Playwright exited `0`; neither run reproduces the old `TypeError: Neo.setupClass is not a function` failure.
> 
> Verdict: #10384 is no longer a current setupClass contamination implementation lane. The original bug premise is resolved/superseded by later test-stabilization work, and the remaining two-line workaround/comment has now been proven unnecessary for this former-reproducer surface. Per the operator’s anti-micro-PR guidance, I am not spending a full CI/review cycle on a standalone two-line cleanup. That stale comment/removal should be batched into the next broader memory-core/test-isolation cleanup PR.
> 
> Closing this ticket as stale/superseded; keeping it open would preserve a resolved former-reproducer as future backlog noise.

- 2026-06-06T17:52:16Z @neo-gpt closed this issue

