Observation filed while implementing #5577 (PR #5645). No live instance exists today — the repo is currently clean, measured below. Filed because the failure mode is invisible by construction and nothing prevents the next one.
The mechanism
vi.mock('<relative path>', factory) with a specifier that resolves to no file on disk does not error. Vitest registers the mock against a module id nothing imports, and the run proceeds with the real module everywhere. The suite passes.
That is the worst available failure direction: the test does not fail, does not warn, and does not report a smaller number of assertions. It reports exactly the same green as a correct one, so nothing in the output distinguishes "this stand-in is installed" from "this stand-in is inert".
How it surfaced
A new suite in PR #5645 mocked the runtime-config module to install a deliberately partial snapshot. The file lives at packages/app-shell/src/layout/__tests__/, and the mock was written vi.mock('../runtime-config', …) — one .. short. That resolves to packages/app-shell/src/layout/runtime-config, which does not exist; the correct specifier is '../../runtime-config'.
The suite passed. It also passed when the code under test was reverted to the exact pre-fix shape the suite was written to catch — a TypeError that, with the mock correctly installed, fails all four cases. The probe was inert and green, and only an ablation leg (mutate the subject, confirm the test goes red) exposed it.
Neighbouring mocks in the same file hid it further: '../AiUsageIndicator' (one level) and '../../hooks/surfaceAgent' (two levels) are both correct from a __tests__/ subdirectory, because the targets sit at different depths. So a reader scanning the mock block sees a plausible mix of ../ and ../../ with no way to tell which is wrong.
Current state — measured, not assumed
Scanned every vi.mock / vi.doMock with a relative specifier across the repository:
scanned 1807 test files, 577 relative vi.mock specifiers
UNRESOLVABLE: 0
(Resolution tried the bare path plus .ts/.tsx/.js/.jsx/.mjs/.cjs and the /index.* forms, and stripped a trailing .js to account for the NodeNext specifier style used throughout src/.)
So this is not a cleanup card. Every one of those 577 is load-bearing today, and the point is that if one of them silently stopped being load-bearing, the only signal would be a test that keeps passing.
Shape of a fix
A scripts/check-*.mjs gate in the existing family. The scan above is the whole check — walk the tracked test files, regex the relative vi.mock/vi.doMock specifiers, resolve each against the file's own directory, fail on any that resolve to nothing. It needs no build, no install and no vitest run, and it completed over all 1807 files in about a second, so it fits the cheap-gate tier rather than the CI-heavy one.
Worth deciding at implementation time, not assumed here:
- whether to cover bare specifiers too (
vi.mock('@object-ui/…')) — those can also be misspelled, but resolving them needs the workspace map rather than the filesystem, so it is a larger check with a different failure mode;
- whether an inert mock should be an error or a warning — there is no legitimate reason to mock a path that does not exist, which argues for error;
- whether the same treatment is owed to
vi.mock calls written as vi.mock(import('…')), which the regex above already matches but which no current call site uses.
Why this is the same family as an already-accepted gate
#4347 (closed) was "check-type-check-coverage.mjs reports green when a type-check script chains a tsconfig project that does not exist" — the same shape one layer up: a declaration pointing at nothing, reported as a pass. This is that failure at the module-mock layer, and it is currently ungated.
Refs
Observation filed while implementing #5577 (PR #5645). No live instance exists today — the repo is currently clean, measured below. Filed because the failure mode is invisible by construction and nothing prevents the next one.
The mechanism
vi.mock('<relative path>', factory)with a specifier that resolves to no file on disk does not error. Vitest registers the mock against a module id nothing imports, and the run proceeds with the real module everywhere. The suite passes.That is the worst available failure direction: the test does not fail, does not warn, and does not report a smaller number of assertions. It reports exactly the same green as a correct one, so nothing in the output distinguishes "this stand-in is installed" from "this stand-in is inert".
How it surfaced
A new suite in PR #5645 mocked the runtime-config module to install a deliberately partial snapshot. The file lives at
packages/app-shell/src/layout/__tests__/, and the mock was writtenvi.mock('../runtime-config', …)— one..short. That resolves topackages/app-shell/src/layout/runtime-config, which does not exist; the correct specifier is'../../runtime-config'.The suite passed. It also passed when the code under test was reverted to the exact pre-fix shape the suite was written to catch — a
TypeErrorthat, with the mock correctly installed, fails all four cases. The probe was inert and green, and only an ablation leg (mutate the subject, confirm the test goes red) exposed it.Neighbouring mocks in the same file hid it further:
'../AiUsageIndicator'(one level) and'../../hooks/surfaceAgent'(two levels) are both correct from a__tests__/subdirectory, because the targets sit at different depths. So a reader scanning the mock block sees a plausible mix of../and../../with no way to tell which is wrong.Current state — measured, not assumed
Scanned every
vi.mock/vi.doMockwith a relative specifier across the repository:(Resolution tried the bare path plus
.ts/.tsx/.js/.jsx/.mjs/.cjsand the/index.*forms, and stripped a trailing.jsto account for the NodeNext specifier style used throughoutsrc/.)So this is not a cleanup card. Every one of those 577 is load-bearing today, and the point is that if one of them silently stopped being load-bearing, the only signal would be a test that keeps passing.
Shape of a fix
A
scripts/check-*.mjsgate in the existing family. The scan above is the whole check — walk the tracked test files, regex the relativevi.mock/vi.doMockspecifiers, resolve each against the file's own directory, fail on any that resolve to nothing. It needs no build, no install and no vitest run, and it completed over all 1807 files in about a second, so it fits the cheap-gate tier rather than the CI-heavy one.Worth deciding at implementation time, not assumed here:
vi.mock('@object-ui/…')) — those can also be misspelled, but resolving them needs the workspace map rather than the filesystem, so it is a larger check with a different failure mode;vi.mockcalls written asvi.mock(import('…')), which the regex above already matches but which no current call site uses.Why this is the same family as an already-accepted gate
#4347 (closed) was "
check-type-check-coverage.mjsreports green when atype-checkscript chains a tsconfig project that does not exist" — the same shape one layer up: a declaration pointing at nothing, reported as a pass. This is that failure at the module-mock layer, and it is currently ungated.Refs
features.aiStudiois read inline at two call sites whilefeatures.marketplacehas an accessor — one fail-open doctrine, two spellings #5577 / PR refactor(app-shell): oneisAiStudioEnabled()accessor forfeatures.aiStudio, replacing two inline spellings #5645 — where the inert mock was written, caught by ablation, and corrected