Context
QueryRecentTurns.spec.mjs (added by #12671) patches TextEmbeddingService.embedText in its beforeAll to return an offline 4096-length vector, but its afterAll restores only StorageRouter.getMemoryCollection — not embedText. The mock therefore leaks into any sibling spec that shares the same Playwright worker. Surfaced 2026-06-07 when it reddened the unit CI lane on the docs-only PR #12681 (#9994).
Diagnosed by @neo-gpt (read-only V-B-A: ordered repro + root-cause + precedent).
The Problem
Playwright runs specs in shared workers (fullyParallel). When QueryRecentTurns.spec.mjs runs before TextEmbeddingService.retry.spec.mjs in the same worker, the unrestored embedText override makes TextEmbeddingService.retry.spec.mjs:216 receive the 4096-length 0.1 vector instead of its expected [0.1, 0.2, 0.3] → red. Because worker assignment/ordering is non-deterministic, this is a latent flake that reddens unrelated PRs (here, the docs-only #12681).
Ordered repro (fails like CI on the unfixed spec):
npm run test-unit -- test/playwright/unit/ai/services/memory-core/QueryRecentTurns.spec.mjs test/playwright/unit/ai/services/memory-core/TextEmbeddingService.retry.spec.mjs --workers=1
The Architectural Reality
- Leak site:
test/playwright/unit/ai/services/memory-core/QueryRecentTurns.spec.mjs — beforeAll sets TextEmbeddingService.embedText = async () => new Array(4096).fill(0.1); afterAll restores getMemoryCollection but not embedText.
- Victim:
test/playwright/unit/ai/services/memory-core/TextEmbeddingService.retry.spec.mjs:216.
- Precedent (correct pattern):
test/playwright/unit/ai/services/memory-core/HealthService.spec.mjs saves originalEmbedText before patching + restores it in teardown.
- Class: method-mock-on-singleton not restored — sibling of #12435's aiConfig-singleton-mutation, but a distinct object (
TextEmbeddingService.embedText, not aiConfig), so it is its own narrow fix rather than part of #12435.
The Fix
Mirror the file's existing originalGetMemoryCollection save/restore for embedText:
- declare
originalEmbedText in the describe-scope let,
originalEmbedText = TextEmbeddingService.embedText before the override,
if (originalEmbedText) TextEmbeddingService.embedText = originalEmbedText in afterAll.
Acceptance Criteria
Out of Scope
- The aiConfig-singleton-mutation isolation (#12435) — distinct object/scope.
- A broader audit of every spec's mock-restore hygiene — this ticket fixes the one confirmed leak blocking #12681; a systemic sweep is a separate concern if warranted.
Related
- #12671 — origin of
QueryRecentTurns.spec.mjs (CLOSED).
- #12681 / #9994 — the docs PR reddened by this leak (unblocked once this merges).
- #12435 — sibling test-isolation class (aiConfig singleton; distinct object).
Decision Record impact: none (test-isolation hygiene; aligned with the restore/by-construction isolation discipline, no ADR amendment).
Origin Session ID: 2d558ccb-e067-4777-bb80-e52f86d5ca43
Retrieval Hint: "embedText mock leak QueryRecentTurns sibling spec CI red"
Live latest-open sweep: checked latest 20 open issues 2026-06-07; no equivalent found.
Context
QueryRecentTurns.spec.mjs(added by #12671) patchesTextEmbeddingService.embedTextin itsbeforeAllto return an offline 4096-length vector, but itsafterAllrestores onlyStorageRouter.getMemoryCollection— notembedText. The mock therefore leaks into any sibling spec that shares the same Playwright worker. Surfaced 2026-06-07 when it reddened theunitCI lane on the docs-only PR #12681 (#9994).Diagnosed by @neo-gpt (read-only V-B-A: ordered repro + root-cause + precedent).
The Problem
Playwright runs specs in shared workers (
fullyParallel). WhenQueryRecentTurns.spec.mjsruns beforeTextEmbeddingService.retry.spec.mjsin the same worker, the unrestoredembedTextoverride makesTextEmbeddingService.retry.spec.mjs:216receive the 4096-length0.1vector instead of its expected[0.1, 0.2, 0.3]→ red. Because worker assignment/ordering is non-deterministic, this is a latent flake that reddens unrelated PRs (here, the docs-only #12681).Ordered repro (fails like CI on the unfixed spec):
The Architectural Reality
test/playwright/unit/ai/services/memory-core/QueryRecentTurns.spec.mjs—beforeAllsetsTextEmbeddingService.embedText = async () => new Array(4096).fill(0.1);afterAllrestoresgetMemoryCollectionbut notembedText.test/playwright/unit/ai/services/memory-core/TextEmbeddingService.retry.spec.mjs:216.test/playwright/unit/ai/services/memory-core/HealthService.spec.mjssavesoriginalEmbedTextbefore patching + restores it in teardown.TextEmbeddingService.embedText, notaiConfig), so it is its own narrow fix rather than part of #12435.The Fix
Mirror the file's existing
originalGetMemoryCollectionsave/restore forembedText:originalEmbedTextin the describe-scopelet,originalEmbedText = TextEmbeddingService.embedTextbefore the override,if (originalEmbedText) TextEmbeddingService.embedText = originalEmbedTextinafterAll.Acceptance Criteria
QueryRecentTurns.spec.mjsrestoresTextEmbeddingService.embedTextinafterAll.QueryRecentTurns.spec.mjs+TextEmbeddingService.retry.spec.mjsremain green standalone.Out of Scope
Related
QueryRecentTurns.spec.mjs(CLOSED).Decision Record impact: none (test-isolation hygiene; aligned with the restore/by-construction isolation discipline, no ADR amendment).
Origin Session ID: 2d558ccb-e067-4777-bb80-e52f86d5ca43 Retrieval Hint: "embedText mock leak QueryRecentTurns sibling spec CI red" Live latest-open sweep: checked latest 20 open issues 2026-06-07; no equivalent found.