fix(marketplace): serialize registry writes to prevent lost updates - #473
Merged
christso merged 2 commits intoSep 14, 2026
Merged
Conversation
validateAllPlugins validates every configured plugin in parallel. Each plugin backed by a remote marketplace independently calls updateMarketplace(), which loads the shared marketplaces.json, mutates its own in-memory copy, and saves — with no coordination between concurrent callers. Two calls racing on the same file let the one that saves last silently overwrite the other's already-persisted change (a lost update), and on Windows the concurrent temp-file renames onto the same destination can also throw EPERM, failing `allagents update` outright. Add an in-process, per-registry-path async lock and route every load-mutate-save sequence through it (updateMarketplace's final save, refreshMarketplace, removeInvalidMarketplaceRegistration). Each writer now reloads the latest saved state before merging its own change in, instead of overwriting with a stale snapshot loaded before the queue.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Contributor
|
I pushed What changedThe original PR queued selected registry saves. This update makes the queue the shared registry transaction boundary:
CoverageThe original 30 ms race now uses explicit promise barriers. Regressions cover:
Verification
The contract remains in-process only. This does not add an OS/file lock or cross-process coordination. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #472
Summary
validateAllPluginsvalidates every configured plugin in parallel (Promise.all). Each plugin backed by a remote marketplace independently callsupdateMarketplace(), which loads the sharedmarketplaces.json, mutates its own in-memory copy, and saves — with no coordination between concurrent callers.EPERM, failingallagents updateoutright (see fix(sync): concurrent plugin validation races on marketplaces.json, losing updates and throwing EPERM on Windows #472 for the traced repro).withRegistryLock) and routes every load-mutate-save sequence through it:updateMarketplace's final save,refreshMarketplace, andremoveInvalidMarketplaceRegistration. Each writer now reloads the latest saved state immediately before merging its own change in, instead of overwriting with a stale snapshot loaded before the queue.Test plan
tests/unit/core/marketplace-update-concurrency.test.ts: two concurrentupdateMarketplace()calls updating two different marketplaces on the same shared registry file — confirmed it fails on the pre-fix code (silently drops one update) and passes with the fix.bun test— full suite: no new failures (7 pre-existing failures intests/unit/core/native/types.test.ts, unrelated to this change, present onorigin/mainbefore this branch too).bun run typecheck— clean.bun run lint— clean.allagents updatethree times against the real workspace that originally reproduced theEPERMcrash (4 marketplaces across user + project scope) — all three runs now exit 0.