[9/?] feat(python-setup): extension wiring & config-view dispatch (DRAFT — blocked) - #2054
Draft
rugpanov wants to merge 20 commits into
Draft
[9/?] feat(python-setup): extension wiring & config-view dispatch (DRAFT — blocked)#2054rugpanov wants to merge 20 commits into
rugpanov wants to merge 20 commits into
Conversation
*Why:*
The extension drives `databricks environments setup-local` and parses its single
JSON result. This adds the argv/path helpers and the process gateway that all
later consumers (the setup orchestrator) build on. Stacked on the error-message
change.
*What:*
- `utils/setupLocalArgs.ts` (pure): `buildSetupLocalArgs(invocation)` assembles
the argv (compute flag → mode → profile → hidden --constraint-source-url →
--output json) from a `SetupLocalInvocation` whose `compute` field is the
cluster|serverless target, and `resolveCliPath({override, bundled})` prefers a non-empty
custom CLI path over the bundled binary (tolerates an undefined override for the
unset setting). Serverless version is passed as a bare number (e.g. "5") per the
shipped contract.
- `utils/venvInterpreterPath.ts` (pure): OS-aware interpreter path
(Scripts\python.exe on Windows, bin/python elsewhere), platform injectable.
- `models/PythonSetupResult.ts`: TODO noting the result's `target` field should
be renamed to `compute` to match the invocation side — it mirrors the CLI wire
key, so that rename must land CLI-first (with a schemaVersion bump). No behavior
change.
- `gateways/PythonSetupCliClient.ts`: spawns the CLI (injectable spawn seam),
captures stdout → `parsePythonSetupResult`, forwards stderr to an onLog
channel, resolves with the parsed result on success AND failure exits (caller
branches on result.ok), rejects on spawn error or unparseable stdout (with
captured stderr appended).
- Cancellation terminates the whole process tree via `terminateProcessTree`
(injectable OS primitives, so it is unit-tested), so the `uv sync` grandchild
can't keep mutating the project after cancel: on Windows `taskkill /T /F` (a
bare SIGTERM doesn't reach the CLI; the helper spawn swallows its own `error`
so a missing taskkill can't crash the host), and on POSIX the child is spawned
`detached` (its own process-group leader) and cancel signals the negated pid
to tear down the group, falling back to a direct kill if the group is gone.
Rejects with a distinct `PythonSetupCancelledError`; a token already
cancelled at entry rejects before spawning at all, so the mutating CLI never
starts.
- stdout is accumulated as raw bytes and decoded once (and stderr via a
StringDecoder) so a multi-byte UTF-8 char split across chunk boundaries can't
corrupt the JSON payload; child stdout/stderr stream `error`s are routed
through the settle guard so an EPIPE can't become an uncaught exception.
*Design note:* there is no live per-phase progress under `--output json` — the
CLI emits only the final JSON object on stdout (per-phase text is text-mode
only, and `uv sync` output is buffered). Consumers show an indeterminate
spinner and read `result.phases` afterwards; no stderr phase-narrator is built
(the plan's `=== Phase N ===` assumption was from the old POC and does not hold).
*Verification:*
- Focused specs (ts-mocha --type-check on the 3 python-setup test files) → 33
passing, 0 failing (argv, detached-spawn, path, venv-path, gateway spawn/parse/
cancel/pre-cancel-no-spawn/UTF-8-split/trailing-stderr-flush/stream-error, and
terminateProcessTree group-kill/taskkill/fallback cases).
- `tsc --noEmit` (whole package) → clean; `eslint` + `prettier -c` on changed
files → clean. Full `yarn test:unit` (extension-host build) not re-run.
Co-authored-by: Isaac
*Why*
The CLI client (PythonSetupCliClient) shipped with no call site, which makes its
API hard to review in isolation. This adds the orchestrator that actually
drives it end-to-end, so the client's usage lands alongside the client.
*What*
- Add PythonSetupEnvironmentSetup (python-setup/controllers): decide whether to
run, resolve the compute target, invoke cli.run() under a progress indicator,
then on success adopt the provisioned venv interpreter and persist
{envKey, pythonVersion} for drift detection; on failure surface the mapped
getPythonSetupErrorMessage copy; treat PythonSetupCancelledError as a silent
user action.
- Consumes the real client surface: run(invocation{compute}, {cwd, onLog, token}),
isPythonSetupSuccess, getPythonSetupErrorMessage. The client is depended on
structurally (CliRunner) so the flow is unit-testable without spawn/vscode.
- The two not-yet-built pieces are injected seams with explicit TODOs, not hard
dependencies:
* isVisible() -> TODO(DECO-27781/#2044): shouldShowPythonSetup(...). Wiring
stub returns false, so the feature is inert end-to-end for now.
* resolveCompute() -> TODO(DECO-27782): serverless-version picker. Wiring
stub returns undefined, so a serverless setup is a no-op until then.
Review fixes (Claude + Codex, two rounds):
- Cancellation is no longer dead code: ProgressTask now exposes a
CancellationLike token alongside the log sink, and setup() threads it into
cli.run({cwd, onLog, token}). The production withProgress passes through the
progress notification's own token, so a user "Cancel" tears down the
project-mutating CLI instead of being a silent no-op.
- Re-entrancy guard: overlapping setup() calls coalesce onto a single in-flight
run (cleared in finally, incl. on rejection) so two setup-local processes
can't race the same cwd's writes/backup.
- Strict real-run success: an ok:true result must carry venvPath AND
target/resolved. Missing any (dry-run shape / CLI-schema drift) is treated as
a failure instead of flipping `ready` with no interpreter adopted or with no
drift baseline persisted.
*Verification*
yarn --cwd packages/databricks-vscode test:unit (351 passing; 13 orchestrator
cases, incl. no-open-project, raw-error-reject branch, success-without-venvPath,
success-missing-target/resolved, token threaded to cli.run, re-entrancy
coalescing, and in-flight guard cleared on rejection). test:lint and build
(tsc --build) clean.
Co-authored-by: Isaac
*Why*
When a Databricks Connect setup targets serverless, the extension must suggest
which serverless environment version to provision. The signal comes from
several places of differing trust (a bundle YAML declaration, a notebook's
recorded environment, the workspace default), so we score candidates by
weighted provenance rather than guessing.
*What*
- Add scoreServerlessVersions(observations): pure, deterministic ranking by
total per-source weight (bundleYaml > notebook > workspaceDefault > fallback),
ties broken by higher numeric version. A built-in fallback candidate is always
present and merges into a matching observed version instead of duplicating it;
a repeated (version, source) pair is not double-counted.
- Versions are the CLI's bare-integer form ("4", "5") -- the value
--serverless-version accepts -- not the vN display form the CLI echoes in
results. This keeps the scored value forwardable straight into the
setup-local invocation.
*Verification*
yarn --cwd packages/databricks-vscode test:unit (332 passing; 7 new scoring
cases: weight ranking, source agreement, dedup, tie-break, always-fallback,
fallback merge, disagreement surfacing).
Co-authored-by: Isaac
*Why* The scored serverless-version candidates must be shown to the user for explicit confirmation (no silent auto-selection), with the recommended one pre-selected and its provenance visible so the choice is informed. *What* - buildVersionPickItems(ranked): pure QuickPick-row builder -- pre-picks and stars the top candidate, carries each row's bare version for the caller to forward to the CLI, and summarises provenance in the description using human-readable source labels (e.g. "bundle config", not the internal "bundleYaml" key). - pickServerlessVersion(ranked): thin window.showQuickPick wrapper over the pure builder; returns the confirmed bare version or undefined on dismissal. The top candidate is recommended but the user must confirm. *Verification* yarn --cwd packages/databricks-vscode test:unit (336 passing; 4 new builder cases: top-pick + provenance, star labelling, fallback-only, empty ranking). Co-authored-by: Isaac
*Why* Scoring and the picker need a single entry point that (1) is guarded by the python-setup opt-in feature flag so the feature stays hidden until a user opts in, and (2) ties collection -> scoring -> confirmation together. This is what the setup orchestrator's compute-resolution seam will call for a serverless target. *What* - Add resolveServerlessVersion(deps): gate on the feature flag FIRST -- with the feature off it returns undefined and does no collection or UI (fully inert) -- then collect observations, score them, and let the user confirm; returns the confirmed bare version or undefined on dismissal. Collection, the gate, and the picker are injected seams so the flow is unit-testable and the extension wiring can supply real implementations later. - Add isPythonSetupEnabled(): the concrete flag reader (experiments.optInto includes PYTHON_SETUP_FEATURE_ID). Placed in python-setup/ rather than on workspaceConfigs to avoid the WorkspaceConfigs<->FeatureManager circular import. Backward compatibility: the resolver is deliberately independent of the existing databricks.connect.serverlessDbconnectVersion setting (a DBR/dbconnect version like "17.3" consumed by the legacy pip flow). The new serverless-environment version is a distinct bare-integer namespace and never reads or writes that setting, so existing users are unaffected. *Verification* yarn --cwd packages/databricks-vscode test:unit / test:lint / build (340 passing; 4 new resolver cases: disabled->inert, scores+confirms, always-fallback offered, dismissal->undefined). Co-authored-by: Isaac
*Why*: * CI's test:lint step (prettier . -c) runs before the unit tests and fails on serverlessVersionScoring.test.ts, so the Linux and Windows "Run unit tests" jobs both abort before any test executes — the reported "failing tests" are this single format gate, not logic failures. *What:* * Reformat the import and two expect(...) lines in serverlessVersionScoring.test.ts to the repo's prettier style. *Verification:* * npx prettier --check on all changed serverless files — clean. * yarn test:unit — 340 passing locally. Co-authored-by: Isaac
Contributor
Author
|
🤖 Integration tests ❌ 1 of 35 test jobs failed for |
*Why*: * Observations are untrusted strings (bundle YAML, notebook metadata, workspace default). versionNumber() stripped a leading "v" only for tie-breaks while the map keyed on the raw string, so a "v5" observation and the bare fallback "5" became two separate rows and "v5" could be forwarded to --serverless-version, which the CLI rejects (it accepts the bare integer, not the vN display form). * Non-numeric or unrealistic versions (e.g. "abc", "0", "6", "4.2") were coerced to 0 for ranking yet still surfaced as pickable candidates. *What:* * Add isSupportedVersion() — a bare integer within the supported range 1..5 (MIN/MAX_SUPPORTED_VERSION) — and drop every observation that fails it before scoring; the known-valid fallback is always kept. * versionNumber() now parses an already-validated bare integer. *Verification:* * yarn test:unit — 343 passing (new cases: drops vN form, drops non-numeric/out-of-range, keeps boundaries 1 and 5). * prettier --check and eslint on the changed files — clean. Co-authored-by: Isaac
rugpanov
force-pushed
the
rugpanov/python-setup-wiring
branch
from
July 27, 2026 11:34
0254d5e to
6350a60
Compare
Contributor
Author
|
🤖 Integration tests triggered for |
…lver tests *Why*: * isSupportedVersion() gated only on /^\d+$/ + range, so zero-padded forms like "05"/"0005" passed, were kept as the raw map key (never merging with the canonical "5"), and could be forwarded verbatim to --serverless-version — a value the CLI does not accept. * The resolver's disabled-path test froze collectCalls at 0 (Object.assign copied the getter's value, not the accessor), so "collection is skipped when disabled" asserted nothing. * The picker docs/test described the top row as "pre-selected", but showQuickPick honours QuickPickItem.picked only in multi-select; single-select pre-selection is cosmetic. *What:* * Require canonical bare form in isSupportedVersion() via String(parseInt(v)) === v, so "05"/"+5"/" 5" are dropped alongside vN/non-numeric/out-of-range. * Replace the Object.assign getter with a live accessor and count real collectObservations invocations even when a test overrides the collector; assert collectCalls === 1 on the enabled path so the disabled-path 0 is a meaningful comparison. * Soften picker doc/test wording to "recommendation (starred, listed first)" and note picked is cosmetic in single-select. *Verification:* * yarn test:unit — 343 passing (new: drops non-canonical "05"/"+5"/" 5"; enabled path collects exactly once). * prettier --check and eslint on the changed files — clean. Co-authored-by: Isaac
*Why* resolveServerlessVersion is meant to be the one place that collects a project's version evidence, scores it, and offers the best default. But it also carried the feature-flag gate, which forced its only real caller (the compute picker) to reimplement the collect->score->pick pipeline inline instead of calling it: the caller needs to distinguish "flag off" (enable plain serverless) from "user dismissed the picker" (make no change), and the resolver collapsed both into a single undefined. *What* - Drop `isEnabled` from ServerlessVersionResolverDeps and the gate from resolveServerlessVersion; it now purely collects -> scores -> picks. The flag check moves to callers (isPythonSetupEnabled stays exported here for them). - Update the resolver test: remove the disabled-path case (now the caller's responsibility) and the isEnabled fake. This lets ConnectionCommands.selectServerless call resolveServerlessVersion directly (next commit on the compute-picker PR), removing the duplicated pipeline and giving one home to grow evidence sources (notebooks, workspace default). *Verification* yarn --cwd packages/databricks-vscode build / test:lint / test:unit (342 passing; resolver now: scores+confirms, always-fallback, dismissal). Co-authored-by: Isaac
*Why* Merging serverless-version selection into the compute picker means the chosen version must be remembered with the compute selection, so setup never re-prompts for it. This adds the persistence field. *What* - Add optional serverlessVersion to OverrideableConfigState (+ isOverrideableConfigKey) -- the CLI's bare-integer --serverless-version value (e.g. "5"). It routes through the existing ConfigModel get/set overrideable path automatically. Backward compatibility: the field is optional and independent of `serverless`. Existing configs that predate it have `serverless: true` with no version, which stays valid -- a version-less serverless selection falls back to the scored default. Writing an unrelated key never fabricates a version (covered by test). *Verification* yarn --cwd packages/databricks-vscode test:unit (344 passing; 4 new: key guard, round-trip persist, clear-to-undefined, legacy version-less config untouched). Co-authored-by: Isaac
*Why* The compute picker needs to save the chosen serverless version with the serverless selection, and setup needs to read it back, so ConnectionManager -- the source of truth for the attached compute -- must carry it. *What* - enableServerless(version?) persists serverlessVersion when supplied (also when serverless is already on, so re-picking the version alone is saved); a bare enableServerless() keeps working unchanged. - disableServerless() clears the version (it only has meaning while serverless is the selected compute). - updateServerless() loads the persisted version on reload, so it survives a restart without re-prompting. - Add get serverlessVersion(): the persisted value, or undefined when serverless is off or no version was chosen (consumers fall back to the scored default). Backward compatibility: the no-arg enableServerless() path and version-less persisted configs are preserved; undefined simply means "use the default". *Verification* yarn --cwd packages/databricks-vscode build (clean; type-checks the new overrideable-key access and getter). Existing unit suite unaffected. Co-authored-by: Isaac
*Why*
The serverless-version picker ranks observations; without a real source it
degrades to a lone fallback candidate. A project's bundle already declares its
serverless environment version(s), which is the strongest provenance signal, so
we harvest them to seed the picker.
*What*
- collectBundleServerlessVersions(bundle): defensively walks a parsed bundle and
emits every serverless `environment_version` as a {version, source:"bundleYaml"}
observation for scoreServerlessVersions. environment_version is specific to
serverless environment specs and is a bare integer ("5") -- the same form
--serverless-version takes. The walk tolerates unresolved ${var...} string
nodes and schema shape changes without throwing, coerces numeric YAML values
to bare strings, and de-duplicates preserving first-seen order.
*Verification*
yarn --cwd packages/databricks-vscode test:unit (350 passing; 6 new: single find,
multi-resource dedup, numeric coercion, string-node tolerance, empty/version-less,
non-scalar skip).
Co-authored-by: Isaac
*Why* Selecting "Serverless" in the compute picker only recorded a boolean; the serverless environment version was then either an invisible setting (legacy) or would be asked on every setup run. Merging the version choice into the compute picker lets the user confirm it once, at compute-selection time, persisted with the selection so setup never re-prompts. *What* - ConnectionCommands.attachClusterQuickPickCommand: when "Serverless" is chosen, route through a new selectServerless() step. - selectServerless(): with the python-setup feature opted into, rank the serverless versions from the project's bundle (collectBundleServerlessVersions -> scoreServerlessVersions), show the picker (top candidate pre-selected), and persist via enableServerless(version); dismissing the version picker makes no compute change. With the feature off it is the plain, unchanged enableServerless() -- the picker is byte-for-byte today's. Constraint 1 (backward compat): flag-off behaviour is identical to today, and bundle read failures fall back to scoring with no observations rather than blocking compute selection. Constraint 2 (feature flag): the version sub-step is gated on isPythonSetupEnabled() (the PR #2045 opt-in), so only opted-in users see it. *Verification* yarn --cwd packages/databricks-vscode build / test:lint / test:unit (350 passing; build type-checks the validateConfig access and picker wiring). Co-authored-by: Isaac
…e picker *Why* selectServerless reimplemented the collect->score->pick pipeline inline, duplicating resolveServerlessVersion (which had no caller as a result). Now that the resolver no longer owns the feature-flag gate, the compute picker can call it directly. *What* - ConnectionCommands.pickServerlessVersion delegates to resolveServerlessVersion, supplying only the evidence source (bundle via collectBundleServerlessVersions, degrading to [] on read failure) and the confirm UI (pickServerlessVersion). The scoring/pipeline lives in one place, with one home to grow evidence sources (notebooks, workspace default). - selectServerless still owns the flag branch: off -> plain enableServerless(); on -> resolve a version, undefined = dismissed -> no compute change. *Verification* yarn --cwd packages/databricks-vscode build / test:lint / test:unit (352 passing). Co-authored-by: Isaac
…or' into rugpanov/python-setup-wiring
…cker' into rugpanov/python-setup-wiring
*Why* The Python Environment section must show the single uv-native setup entry when the feature is visible, and the legacy pip checklist otherwise -- the two are mutually exclusive and never rendered together. *What* - Add buildPythonSetupEntry (pure): the single Python Environment row -- a rocket run CTA when not ready, a check status line once ready -- both running the setup command. Plus the PythonSetupEntry interface (isVisible/ready/ onDidChangeState), a narrow slice of the orchestrator so the component carries no controller/gateway dependency. - EnvironmentComponent takes an optional PythonSetupEntry; getChildren returns that single entry when isVisible(), else the unchanged checklist. Refreshes the view on its onDidChangeState. Optional, so the legacy construction is byte-for-byte unchanged when absent. *Verification* yarn --cwd packages/databricks-vscode test:unit (399 passing; 3 new entry-builder cases: run CTA, ready line, single entry). build clean. Co-authored-by: Isaac
*Why* The orchestrator's gate and compute-resolution seams shipped as stubs. This fills them with the real implementations, wiring the pieces from across the stack together for the extension. *What* - makePythonSetupVisibility: the config-view gate -- opted-in (flag on) AND the active project fits (shouldShowPythonSetup: clean uv/greenfield, no competing manager). No project / detection failure -> hidden. - resolveComputeFrom: map the attached compute to a setup-local target. A cluster wins outright; a serverless session resolves to its persisted serverlessVersion (from the compute picker, Diff A) -- no version -> undefined (abort silently, no guessing). - makePythonSetupDeps: assemble the full PythonSetupSetupDeps -- adoptInterpreter via venvInterpreterPath + the MS Python extension, progress/toasts via window. saveState is a documented no-op TODO(DECO-27784) until the storage key lands. *Verification* yarn --cwd packages/databricks-vscode test:unit (408 passing; 9 new: 4 visibility gate, 5 compute resolution incl. cluster-over-serverless and version-less-abort). build clean. Co-authored-by: Isaac
*Why* Final integration: construct the python-setup stack in activate(), register its command, and let the config view dispatch to it. This turns the feature on for opted-in users while leaving everyone else on the unchanged legacy flow. *What* - extension.ts: build PythonSetupManagerDetector (real collector via collectPackageManagerSignals + the active interpreter), PythonSetupCliClient (resolveCliPath: cliPathOverride else bundled), and PythonSetupEnvironmentSetup via makePythonSetupDeps -- projectRoot from workspaceFolderManager, gate from isPythonSetupEnabled + detection, compute from connectionManager (cluster / persisted serverlessVersion), interpreter adopted through the MS Python extension. Register databricks.environment.setupPythonEnv. - ConfigurationDataProvider: thread the (optional) PythonSetupEntry into EnvironmentComponent. - PythonSetupEnvironmentSetup: expose isVisible() (delegates to the gate seam) so it satisfies the component's PythonSetupEntry contract. - package.json: register the setupPythonEnv command. Backward compatibility: the stack is constructed unconditionally but inert -- the gate (flag off, or non-uv project) keeps the entry hidden and the command a no-op, so existing users see the identical legacy checklist. The dependency is optional end-to-end, so nothing changes when it is absent. *Verification* yarn --cwd packages/databricks-vscode build / test:lint / test:unit (408 passing; build type-checks the full activate() wiring). Manual dogfff smoke deferred to a custom-CLI environment. Co-authored-by: Isaac
*Why* The feature must stay hidden until a user opts in. A registered command is palette-invokable by default, which would expose "Set up Python environment (uv)" to every connected user regardless of the opt-in flag or project type. *What* - Add a commandPalette menu entry with "when": "false" for databricks.environment.setupPythonEnv, so it is reachable only via the gated config-view tree entry (which already checks the opt-in flag + package-manager gate), never the palette. Matches the repo's pattern for tree-only commands. *Verification* yarn --cwd packages/databricks-vscode build / test:lint (both clean). Co-authored-by: Isaac
rugpanov
force-pushed
the
rugpanov/python-setup-wiring
branch
from
July 27, 2026 11:46
6350a60 to
47e53f5
Compare
Contributor
|
If integration tests don't run automatically, an authorized user can run them manually by following the instructions below: Trigger: Inputs:
Checks will be approved automatically on success. |
Contributor
Author
|
🤖 Integration tests ✅ all 35 test jobs passed for |
rugpanov
added a commit
that referenced
this pull request
Jul 28, 2026
#2055) ## Why After a successful uv-native Python environment setup, the extension needs to remember what environment was provisioned, so a later change of compute target can be detected as **drift** (the local env no longer matches the selected target) and surfaced to the user. This lands the persistence key. Scoped deliberately to the storage layer: it builds off `main` with no dependency on the (still-open) wiring stack. The **write site** — the orchestrator's `saveState` — is wired separately when the extension-wiring PR (#2054) lands; it currently holds a `TODO(DECO-27784)` no-op that this key will back. ## What - **`PythonSetupState`** type — `{envKey, pythonVersion, timestamp}`: - `envKey`: the CLI's resolved environment key for the target (e.g. `serverless/serverless-v5`). - `pythonVersion`: the provisioned interpreter minor version (e.g. `3.12`). - `timestamp`: ISO-8601 time of the successful setup. - **`"databricks.pythonSetup.setupState"`** `StorageConfigurations` entry, **workspace-scoped** (drift is per-project). Flows through the existing typed `get`/`set` surface — no new accessor code. - **First `StateStorage` unit test** (in-memory `Memento` fake): round-trip, undefined-before-write, clear-on-undefined, and workspace-not-global placement. ## Verification - `yarn --cwd packages/databricks-vscode build / test:lint / test:unit` — 329 passing (4 new). Build confirms the typed key resolves through `get`/`set`. This pull request and its description were written by Isaac.
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.
Ticket
This implements DECO-27786 — Extension wiring & config-view dispatch (NOT DECO-27782, which is the serverless picker, already done in #2052/#2053).
Blocked on
[3]CLI client) — merge to main[6]orchestrator) — merge to main[7]serverless picker) — merge to main[8]compute-picker) — merge to mainsaveStateis currently a documented no-op TODO (see below)Once #2039/#2046/#2052/#2053 are on main, this rebases onto main and the diff collapses to just the wiring commits.
The wiring commits (the actual DECO-27786 work)
dispatch the uv-native entry in the config view—buildPythonSetupEntry(pure) +EnvironmentComponentdispatch (single uv entry when visible, else the unchanged legacy checklist; mutually exclusive).assemble orchestrator deps with real seams—makePythonSetupDeps: the visibility gate (isPythonSetupEnabled+shouldShowPythonSetup) and compute resolution (cluster / persistedserverlessVersionfrom Diff A) filling the orchestrator's stub seams.wire the flag-gated uv setup into the extension— construct detector/client/orchestrator inactivate(), thread throughConfigurationDataProvider, registerdatabricks.environment.setupPythonEnv.hide the setup command from the Command Palette—"when": "false"so the feature is reachable only via the gated tree entry, never the palette.Known TODO (tracked, not fixed here)
saveStateis a no-op (TODO(DECO-27784)). Setup succeeds and adopts the interpreter, but drift-detection state isn't persisted until DECO-27784 lands the StateStorage key. This is intentional — persistence is a separate ticket.Backward compatibility
The stack is constructed unconditionally but inert: the gate keeps the entry hidden (flag off, or non-uv project), the command is palette-hidden, and the
PythonSetupEntrydependency is optional end-to-end. Existing users see the identical legacy checklist.Verification
yarn --cwd packages/databricks-vscode build / test:lint / test:unit— 408 passing (12 new across dispatch + deps factory); lint + build clean on the integration branch.This pull request and its description were written by Isaac.