Repository navigation
fix: address review findings on the v2.3.0 release - #292
Conversation
Where hard links aren't available the key file is created with a direct create-new write, so a second process could read it before all 32 bytes land and treat it as corrupt. A file shorter than a key is re-read for up to a second before it counts as invalid.
capa add https://user:token@host/repo.git wrote the token into capabilities.yaml, which is usually committed. parseGitUrlSource now strips http userinfo; private hosts use the git credential helper.
The enabled flag was checked before the download only, so a registry disabled while its new adapter was being fetched still executed it. Re-check after the download and before running the adapter, in both the server and CLI refresh (409 / error, status left as is).
matcherWithinPrefix compared raw alternatives, so ^mcp__github__.*$ or a wrapping (?:...) group fell back to the union with mcp__.* and fired for every MCP tool. Ignore anchors and one wrapping group when deciding.
PR Summary by QodoFix four v2.3.0 release review findings
AI Description
Diagram
High-Level Assessment
Files changed (10)
|
Code Review by Qodo
1.
|
stageRegistry deletes the installed adapter before downloading, so a registry disabled mid-refresh was left with the new file and its old stored hash and failed to load once re-enabled. Refreshes now move the current install aside and put it back if staging or the enabled check fails.
A short key file that has been there a while is corrupt, not mid-write; waiting on it stalled every key load, including each /health request.
Two operations on the same slug could delete or restore each other's adapter files (e.g. a refresh restoring its backup over a concurrent refresh's install). Server install/edit/refresh/delete and CLI refresh/remove now hold a per-slug lock directory (atomic mkdir, shared across processes); a second operation gets RegistryBusyError (409 from the server). A lock older than 10 minutes is treated as left by a crashed process.
A lock was treated as abandoned once its mtime was 10 minutes old, so a long refresh could have its lock taken over, and the original operation could then delete the new owner's lock. The lock now records its owner (pid and token) and is touched while the operation runs; it is taken over only when stale and its owner process is gone, by renaming it aside so only one taker wins; and it is removed only by its current owner.
Replaces the lock directory with a lock file published by writing a temp file and hard-linking it into place, so it is never empty or ownerless and a failed write leaves nothing behind. Liveness is the heartbeat mtime rather than a pid that can be reused. Taking over an abandoned lock requires a short-lived takeover guard, under which the lock is re-checked, so concurrent takers can't remove each other's fresh lock.
The cross-process file lock kept drawing races that need OS-level locking Node doesn't expose. The server, which runs every registry change the web UI makes, now holds a plain in-process lock per registry, and the CLI's refresh and remove refuse while the server is running and point to the web UI. Drops the file lock, its heartbeat and takeover logic.
refactor: in-process registry lock (release review round 6)
fix: release review round 5
fix: release review round 4
fix: release review round 3
fix: release review round 2
Summary
Fixes the four findings Qodo raised on the v2.3.0 release PR (#291). All four are in code merged to
developthis cycle.What changed
master.keyread (from fix: use the OS keychain in compiled binaries for new installs #283): where hard links aren't available the key file is created with a direct create-new write, so a concurrent process could read fewer than 32 bytes and fail as "corrupt". A file shorter than a key is now re-read for up to a second before it counts as invalid; longer or wrong files still fail immediately.capabilities.yaml(from Install skills and plugins from any git host #279):capa add https://user:token@host/repo.gitwrote the token into the project file.parseGitUrlSourcenow strips http(s) userinfo (sshgit@is kept); private hosts use the git credential helper.RegistryDisabledError, 409 from the server; status left untouched).^mcp__github__.*$or a wrapping(?:...)group failed the "already within the family" check and was unioned withmcp__.*, firing for every MCP tool. Anchors and one wrapping group are now ignored for that check; other groups still fall back to the union.Screenshots / logs
N/A.
Test plan
master-key-store.test.ts: a separate process finishes writing the key while capa reads it (16 pass).git-url.test.ts: credentials dropped for https,ssh://git@kept (16 pass).registries-routes.test.ts: a registry disabled during the fetch gets 409, its adapter never runs (marker file absent) and it stays disabled; withregistry.test.tsandsrc/shared/registries: 111 pass.hook-handlers.test.ts: anchored and grouped scoped matchers stay scoped; multi-group matchers still union (32 pass withsrc/cli/utils/hooks).bunx tsc --noEmit -p .and-p web-ui.Checklist
bunx tsc --noEmitpassesbun run smellsclean vs base (or CI Qlty Smells job green)🤖 Generated with Claude Code