Skip to content

Bounded, cancellation-aware terminal contract for all-manager fan-outs #27

Description

@StellaHuang95

Need: The Python API's all-manager fan-outs — PythonEnvironmentsApi.getEnvironments('all' | 'global'), refreshEnvironments(undefined), and the equivalent Promise.all refreshes in src/features/envCommands.ts — aggregate results across every registered EnvironmentManager. A manager whose refresh/getEnvironments promise never settles keeps the aggregate pending indefinitely. This cannot be bounded safely today because the manager contract is uncancellable and state-mutating, so a timeout would falsely report failure while the operation keeps running and later mutates state.

Why this cannot be fixed inside PR #21

  • EnvironmentManager.refresh(scope): Promise<void> and EnvironmentManager.getEnvironments(scope): Promise<PythonEnvironment[]> (src/api.ts) expose no CancellationToken. This is the public interface implemented by third-party extensions.
  • Both operations mutate manager state: built-in managers populate their environment collection and fire onDidChangeEnvironments. InternalEnvironmentManager.refresh (src/internal.api.ts) additionally calls manager.refresh() then manager.getEnvironments('all') — two uncancellable, state-mutating calls per refresh.
  • Racing a timer (Promise.race) detaches the still-running operation: the caller is told the op failed/completed while the manager later commits collection changes and events. PR fix: isolate environment consumers from single-manager failures #21 deliberately removed exactly this unsafe pattern.
  • An optional token added to the interface does not help: third-party managers that ignore it would still run to completion and commit late state/events, so cancellation could not be guaranteed for every manager involved.

Acceptance criteria

A safe design must satisfy all of the following before the aggregate can be bounded:

  • Cancellation/termination capability: a first-class way to cancel or terminate an in-flight refresh/getEnvironments that guarantees no late state write or onDidChangeEnvironments event is committed after cancellation, for every participating manager.
  • Manager compliance: built-in managers (venv, conda, poetry, pipenv, pyenv, pixi, uv, sys) honor the contract, and a defined, testable compliance expectation exists for third-party managers, including a documented safe fallback when a manager does not comply.
  • Aggregation deadlines: a defensible deadline policy for the fan-out that only reports a manager failed/omitted when its work is genuinely terminated (not merely detached).
  • Tracked in-flight ownership: the API tracks the in-flight operation per manager/scope so duplicate concurrent calls coalesce instead of overlapping, with explicit ownership and cleanup.
  • Late events: defined semantics for results/events that arrive after a deadline or cancellation — suppressed or reconciled, never silently mutating stale state.
  • Truthful promise contract + compatibility: the public getEnvironments/refreshEnvironments promise contract stays truthful, and the change is assessed for backward compatibility across built-in and existing third-party managers (a breaking EnvironmentManager change requires a version/compat plan).

Related

Prerequisite for bounding the never-settling case discussed in PR #21 (threads on src/features/pythonApi.ts), and for extracting a shared ordered-settlement primitive across the API consumers and the envCommands.ts creation-flow refreshes. Until this lands, PR #21 keeps failure logging prompt (per-manager, as each settles) and awaits operations to completion rather than detaching them.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions