Summary
UserConfig is loaded once when an azd process starts, and later Set/Unset operations save that in-memory snapshot. If another process updates ~/.azd/config.json in the meantime, the stale writer can overwrite those changes.
Extension stores that read, modify, and replace a shared map can also lose entries written concurrently. This already affects foreground agent session/conversation persistence; resumable invoke cursor persistence increases the frequency and duration of exposure.
Expected behavior
UserConfig mutations should not overwrite unrelated changes made by another azd process, and concurrent updates to different entries in a shared extension store should be preserved.
Proposed direction
- Serialize UserConfig mutations with a cross-process file lock.
- Reload the latest config while holding the lock.
- Apply the requested mutation and save atomically.
- Provide atomic map-entry mutation semantics, or a dedicated locked state store, for opaque extension keys.
- Add concurrency tests covering unrelated config paths and different entries in the session, conversation, and background-response stores.
Context
Follow-up from #9703. Issue #9676 explicitly leaves cross-process races as a future optimization.
Summary
UserConfig is loaded once when an
azdprocess starts, and laterSet/Unsetoperations save that in-memory snapshot. If another process updates~/.azd/config.jsonin the meantime, the stale writer can overwrite those changes.Extension stores that read, modify, and replace a shared map can also lose entries written concurrently. This already affects foreground agent session/conversation persistence; resumable invoke cursor persistence increases the frequency and duration of exposure.
Expected behavior
UserConfig mutations should not overwrite unrelated changes made by another
azdprocess, and concurrent updates to different entries in a shared extension store should be preserved.Proposed direction
Context
Follow-up from #9703. Issue #9676 explicitly leaves cross-process races as a future optimization.