Skip to content

fix(mcp-server): inject the adapter factory in tests, so CI cannot bypass the seam - #35

Merged
christophersesugh merged 1 commit into
mainfrom
fix/mcp-server-embedder-wiring-tests
Oct 18, 2026
Merged

christophersesugh merged 1 commit into
mainfrom
fix/mcp-server-embedder-wiring-tests

Conversation

@christophersesugh

Copy link
Copy Markdown
Contributor

Why main has been red since 2026-09-25

Two tests in packages/mcp-server/tests/factory.test.ts failed on every CI run:

FAIL factory.test.ts > ... > lazy wiring > initializes on the first vector operation and recalls semantically
  AssertionError: expected "vi.fn()" to be called 1 times, but got 0 times
FAIL factory.test.ts > ... > local mode with zero API keys > recall falls back to the lexical path when the adapter is absent
  AssertionError: expected "vi.fn()" to be called at least once

Root cause — not timing

The lexical assertions inside those same tests passed. Recall worked; only the mock call count was wrong. That ruled out a race.

createMemoFSMcpRuntimeFromConfig delegates the lazy adapter load into @memofs/core. Core is consumed as a built dist (exports → ./dist/index.mjs) and therefore sits outside vitest's module graph — so vi.mock("@memofs/adapter-transformers") never intercepts the import(adapterModule) inside it. CI builds core before testing (test:run dependsOn ^build), so the real adapter was constructed while the mock recorded zero calls. Local runs only passed because resolution happened to differ, which is why this was not reproducible off-CI.

Three further tests asserted the mock was not called — in CI those passed trivially and asserted nothing at all.

Fix

local-embedder.ts already documented a seam for exactly this ("Injected factory for tests so the dynamic import is not exercised") but it was never plumbed through.

  • Threaded the optional adapterFactory through RuntimeFactoryOptions. Production callers omit it, so the adapter stays an optional peer dependency and behaviour is unchanged.
  • Converted all five embedder-wiring tests to inject it, so assertions hold regardless of externalisation, bundling, or install state.
  • Removed the dead module mock, vi.hoisted, and the beforeEach reset.

Verified

Typecheck clean, Biome clean, 48/48 in the mcp-server suite, changeset added per CONTRIBUTING.

Not reproducible locally, so this CI run is the real confirmation.

Note

The ruleset required a status check named Quality Checks, which no workflow produces — only Validate exists. That has been corrected to Validate, and admins can now bypass on PR merge, so this is mergeable.

This is also unblocking #32, #33, and #34.

…pass the seam

Two embedder-wiring tests failed on every CI run since 2026-09-25: the mock
recorded zero calls where one was expected. The lexical assertions in the same
tests passed, so recall worked and only the mock count was wrong.

The cause is not timing. createMemoFSMcpRuntimeFromConfig delegates the lazy
adapter load to @memofs/core, which is consumed as a built dist and so sits
outside vitest's module graph — vi.mock never intercepts the dynamic import
there. CI builds core before testing (test:run depends on ^build), so the real
adapter was constructed. Local runs only passed because resolution happened to
differ.

Three tests asserted the mock was *not* called, which passed trivially in CI
and asserted nothing. All five now inject adapterFactory, the seam
local-embedder already documented for exactly this purpose, so the assertions
hold regardless of externalisation or install state.

Threaded adapterFactory through RuntimeFactoryOptions. It is optional and
production callers omit it, so the adapter remains an optional peer dependency
and behaviour is unchanged.

Verified: typecheck clean, biome clean, 48/48 in the mcp-server suite. Not
reproducible locally, so the CI run is the real confirmation.
@christophersesugh
christophersesugh merged commit 8e2dafc into main Oct 18, 2026
1 check failed
@christophersesugh
christophersesugh deleted the fix/mcp-server-embedder-wiring-tests branch October 18, 2026 22:25
christophersesugh added a commit that referenced this pull request Oct 18, 2026
…nd warrants (#36)

## Two deterministic failures on main

Both predate the mcp-server fix in #35 — they were masked by it
short-circuiting the job.

### 1. `doctor` / `validate` failed on a freshly-initialised workspace

```
missing_file  .memofs/trials/assignments.jsonl
missing_file  .memofs/trials/outcomes.jsonl
missing_file  .memofs/warrants/history.jsonl
```

`REQUIRED_FILES` was `CANONICAL_MEMOFS_FILES` — the *full* canonical
set. The trials-ledger work added `BOOTSTRAP_FILE_PATHS` precisely
because those paths are created lazily on first append so `init` output
stays unchanged (its doc comment says so explicitly), but nothing
repointed the CLI at it. `REQUIRED_DIRS` was already correct, so the two
were inconsistent.

- `REQUIRED_FILES` → `BOOTSTRAP_FILE_PATHS` (files that must exist)
- new `SNAPSHOT_FILES` → `CANONICAL_MEMOFS_FILES` (capture what exists)

`snapshot` moves to `SNAPSHOT_FILES`, preserving today's behaviour of
including trial and warrant state in a snapshot once written. Silently
narrowing the shared constant would have dropped that from snapshots.

### 2. Hosted schema URL disagreed with the published `$id`

The CLI stamped `$schema:
https://docs.memofs.dev/schema/connectors.json` into
`.memofs/connectors.json`, but both hosted schemas and both packaged
schemas declare `memofs.dev`. Confirmed against what is actually served
— both hosts resolve and return byte-identical documents, and the `$id`
inside is `memofs.dev`. The CLI constants were the outlier.

Corrected both schema constants and the six stale test expectations,
including a fixture in `@memofs/connectors`. The remaining
`docs.memofs.dev` references are prose links that still resolve, so they
are left for a separate sweep rather than bundled into a CI fix.

### 3. One flaky test

`packs context for agent use` runs three in-process CLI commands against
a real filesystem and measured **5025ms against vitest's 5000ms
default** — a coin flip under parallel load. Given an explicit 30s
timeout.

## Verified

- `@memofs/cli` — **235/235**, twice
- `doctor-validate` — 8/8 (was 5 failing)
- `@memofs/connectors` — 98/98
- `connectors-schema` — 5/5 (was failing)
- typecheck and biome clean

## Known remaining

`turbo run test:run --force` still fails `@memofs/adapter-r2`, which
passes **5/5 in isolation**. This suite is broadly load-sensitive —
several tests sit near the 5s default and only fail when ~26 packages
run in parallel. That is a separate systemic issue and not addressed
here.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant