- Create the storage directory before locking. `writeStoreFile` creates it,
but from inside the locked section — so on a fresh install, where
`~/.mcp-inspector` does not exist, proper-lockfile failed ENOENT and every
*first* save degraded to an unlocked write with a warning. That is the save
most likely to be racing another (two Inspectors started together both
reach it), so the lock was absent from precisely the interleaving it exists
to close.
- `anythingToMigrate` no longer reads every `readdir` failure as "nothing to
migrate". Only ENOENT/ENOTDIR proves the fresh-install case; a directory
that denies listing can still permit access to the known `secrets.json`,
and returning false there selected the keychain and left those secrets
invisible with nothing said. Other errors fall through to the under-lock
checks, which is what happened before the fast path existed.
- Pin `proper-lockfile` to the repo-root install in `vitest.shared.mts`,
beside express and yaml: it is reached only through root-owned `core/`.
Resolution already finds the root copy since no client declares it; the pin
is what stops that depending on it never arriving as some client's
transitive dependency, which would give a test two copies of a module whose
whole job is one registry of held locks.
- Make the cross-process test prove `mutate` takes the lock. The previous one
asserted only that both secrets survived, which the optimistic verify
delivers with the lock removed entirely. It now asserts the parent's `set`
has *not* settled while a child holds the lock — an observation only a real
lock produces. Verified by removing `withSecretFileLock` from `mutate`:
three tests fail, where none did before.
The degrade test's "lock cannot be created" case is now a path whose parent
is a *file* rather than a missing directory, since a missing directory is no
longer that case. It also fails identically for root, so it cannot pass
locally and flake in a container.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JTHVxSu8AUgHRLvo1ntZ8H
Signed-off-by: cliffhall <cliff@futurescale.com>