fix: preserve conda environments on transient discovery failure - #12
fix: preserve conda environments on transient discovery failure#12StellaHuang95 wants to merge 1 commit into
Conversation
|
🔒 Automated review in progress — @StellaHuang95 is auto-reviewing this PR. |
| ...new Set( | ||
| events.filter((e) => e.kind === EnvironmentChangeKind.add).map((e) => e.environment.name), | ||
| ), | ||
| ]; |
There was a problem hiding this comment.
Warning · Non-blocking recommendation
📍 src/test/managers/conda/condaEnvManager.resultPreservation.unit.test.ts:255
The Set deduplicates addition names, so this test passes even if the background path emits duplicate add events. Assert the raw addition count and ordered names instead.
[verified]
| traceVerbose( | ||
| 'Conda discovery failed during initialization; preserving collection and restoring persisted selections.', | ||
| ); | ||
| await this.preserveCollectionOnFailedDiscovery(); |
There was a problem hiding this comment.
Warning · Non-blocking recommendation
📍 src/managers/conda/condaEnvManager.ts:131
The same failure-result interpretation is repeated at all three discovery entry points, leaving the preservation invariant vulnerable to drift. Centralize applying a discovery result in a private helper, or otherwise keep the branches mechanically linked.
[verified]
| // concurrent background discovery failure (whose own re-resolution may also fail) | ||
| // cannot lose it. Registration is silent: the discovery paths announce the collection, | ||
| // and failure recovery reuses this entry via normalized-path identity. | ||
| this.registerFastPathEnv(scope, fastResult.env); |
There was a problem hiding this comment.
Issue · Please address or respond
📍 src/managers/conda/condaEnvManager.ts:394
The Skeptic reproduced a race where successful background discovery finishes before the fast path, after which this silently inserts an environment into the already-announced authoritative collection. Reconcile this completion order by emitting an add when necessary or routing the late result through a common post-discovery application path, and add a gated regression test.
[verified]
| // and never emit removals, but still restore persisted global/workspace | ||
| // selections (they resolve independently of discovery). A successful (possibly | ||
| // empty) array is applied normally. | ||
| if (refreshed === undefined) { |
There was a problem hiding this comment.
Warning · Non-blocking recommendation
📍 src/managers/conda/condaEnvManager.ts:131
The prior maintainability concern is only partially addressed: recovery is shared, but three callers still independently interpret undefined. Centralize discovery-result interpretation so future call sites cannot accidentally collapse failure into an authoritative empty result while retaining their distinct success-event policies.
[verified]
1f91115 to
03b74b0
Compare
03b74b0 to
1484ce6
Compare
refreshCondaEnvs now returns undefined when the native finder throws/rejects, distinct from an authoritative successful empty array. CondaEnvManager guards its initialize, refresh, and background-get discovery paths so a transient failure preserves the known-good collection and emits no removals, while a successful empty result still removes stale environments normally. Persisted global/workspace selections are still restored via loadEnvMap on the failure path, announcing only newly appended environments. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1484ce6 to
8d4f02e
Compare
| this.collection = refreshed; | ||
| await this.loadEnvMap(); | ||
| this._onDidChangeEnvironments.fire( | ||
| this.collection.map((e) => ({ |
There was a problem hiding this comment.
Issue · Please address or respond
📍 src/managers/conda/condaEnvManager.ts:362
[verified] The prior successful-background/late-fast-path race remains: this assignment can publish the authoritative background collection before a later fast-path result is reconciled, leaving that result without an add event. Route the late result through a common reconciliation path and add a gated successful-background regression test.
[verified]
| this.collection = | ||
| (await refreshCondaEnvs(false, this.nativeFinder, this.api, this.log, this)) ?? []; | ||
| const refreshed = await refreshCondaEnvs(false, this.nativeFinder, this.api, this.log, this); | ||
| if (refreshed === undefined) { |
There was a problem hiding this comment.
Warning · Non-blocking recommendation
📍 src/managers/conda/condaEnvManager.ts:127
The two prior centralization warnings are only partially addressed: initialization, refresh, and background initialization still interpret undefined independently. Centralize discovery-result interpretation while parameterizing each caller's distinct successful-result event policy.
[verified]
| await this.loadEnvMapPreservingCollection(); | ||
| return; | ||
| } | ||
| this.collection = refreshed; |
There was a problem hiding this comment.
Warning · Non-blocking recommendation
The initialization, refresh, and background initialization paths still independently classify undefined. Centralize discovery-result classification while preserving each caller's distinct success-event policy. [verified]
| await this.loadEnvMap(); | ||
| const added = this.collection.filter((env) => !known.has(env)); | ||
| if (added.length > 0) { | ||
| this._onDidChangeEnvironments.fire( |
There was a problem hiding this comment.
Warning · Non-blocking recommendation
The pre-await known snapshot can become stale if another resolution appends and announces an environment while loadEnvMap() is awaiting, causing this helper to emit a duplicate add. Have loadEnvMap() report appended environments, or recheck announcement state before firing. [verified]
Context
Conda environment discovery runs the native finder (PET). When that refresh throws/rejects — a
transient failure —
refreshCondaEnvspreviously returned[], the same value it returns for agenuine "no conda environments" result.
CondaEnvManagercould not tell the two apart, so atransient failure replaced the known-good collection with
[]and emitted spuriousremoveevents, making previously-discovered environments disappear from the UI until the next successful
refresh.
Root cause
refreshCondaEnvscollapsed two distinct outcomes into[]:Every manager discovery path (
initialize,refresh, backgroundget) then assignedthis.collection = resultand emitted add/remove events, so a failure wiped the collection.Fix
refreshCondaEnvsnow returnsPythonEnvironment[] | undefined:undefined= discovery failed (native finder threw/rejected).[]) = authoritative success;[]means genuinely no environments.undefined:loadEnvMap()so persisted global/workspace selections are restored — announcing only environments newly
appended by that resolution (
loadEnvMapPreservingCollection).[]): replace the collection and emit the normal add/remove events.Legitimate deletion is unchanged: a successful
[]is authoritative and removes stale environmentsexactly as before. Only a thrown-failure
undefinedis treated as "preserve".Tests
condaUtils.refreshCondaEnvs.unit.test.ts: the utility returnsundefinedon a rejected refreshand
[]on a successful empty discovery.condaEnvManager.resultPreservation.unit.test.ts:refresh/initializepreserves the known-good collection and emits no changes whennothing persisted resolves;
[]empties the collection and emits removals; a successful non-empty resultreplaces the collection and emits removals + adds;
addition and never a removal, and retains it across
getcalls.