mrd-viz: deterministic two-tier backend resolution - #86
Conversation
…ementation plan Design docs backing the deterministic backend-resolution work: current-state analysis, the two-audience target architecture, levels-of-abstraction diagram, and the phased implementation plan. Base commit for the resolver rewrite (PR1).
Replace the 5-candidate search with a pure planBackendCandidates(): a configured override is the ONLY candidate (no silent fallback), otherwise the bundled binary, with the repo .venv tried first only in the F5 Development host. Adds BackendKind and getConfiguredBackendPath (backendPath, then legacy explicit pythonPath). Drops managed-venv and PATH candidates.
selectInterpreter and the guided setup now write the machine-scoped mrdViz.backendPath (no more workspace scope-clearing needed), so the managed venv becomes a first-class override rather than an implicit resolver candidate. Config invalidation watches backendPath and pythonPath.
Lead with the relevant explanation and primary action: a broken mrdViz.backendPath override vs. a bundled backend that won't run on this platform. Update references to backendPath.
Uses mrdViz.backendPath (applied as a container remote setting) instead of the deprecated pythonPath, so the container interpreter can't leak to the host.
…implemented Unit-test planBackendCandidates (override no-fallback, dev vs prod order, binary classification, empty) and the tailored override page. Update BACKEND_INSTALL_MODES (target -> implemented) and DEVCONTAINER (machine-scoped backendPath, leak now structurally prevented).
There was a problem hiding this comment.
Pull request overview
This PR refactors MRD Viz backend selection to a deterministic two-tier resolver, centering resolution around an explicit machine-scoped override (mrdViz.backendPath) or the bundled backend binary, with a Development-host-only escape hatch to prefer the repo venv.
Changes:
- Replaces the previous 5-candidate “silent fallback” resolver with
planBackendCandidates()+ fail-loud behavior. - Introduces machine-scoped
mrdViz.backendPath, deprecatesmrdViz.pythonPath, and persists managed-venv installs / interpreter selection intobackendPath. - Updates the backend-missing webview UX, adds resolver-order tests, and refreshes devcontainer/docs to match the new model.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| mrd-viz/extension/mrd-viz/src/webviewHtml.ts | Tailors backend-missing page messaging/actions based on override vs bundled failure; updates guidance text to backendPath. |
| mrd-viz/extension/mrd-viz/src/test/extension.test.ts | Adds tests for tailored backend-missing UX and deterministic candidate ordering. |
| mrd-viz/extension/mrd-viz/src/extension.ts | Persists managed venv to mrdViz.backendPath, updates config invalidation, and switches interpreter selection to write backendPath. |
| mrd-viz/extension/mrd-viz/src/backendResolver.ts | Implements two-tier deterministic resolution with explicit override, bundled binary, and dev-host repo venv ordering. |
| mrd-viz/extension/mrd-viz/package.json | Adds mrdViz.backendPath (machine scope) and deprecates mrdViz.pythonPath. |
| mrd-viz/docs/DEVCONTAINER.md | Updates devcontainer guidance and leak runbook text to backendPath and legacy pythonPath behavior. |
| mrd-viz/docs/BACKEND_RESOLUTION_IMPLEMENTATION_PLAN.md | Adds an implementation-plan doc describing the new resolution model and phases. |
| mrd-viz/docs/BACKEND_INSTALL_MODES.md | Adds a detailed doc describing install modes and the new target architecture (now implemented). |
| .devcontainer/mrd-viz/devcontainer.json | Switches devcontainer settings from mrdViz.pythonPath to mrdViz.backendPath. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Nice work on the deterministic resolver — 1. Stale
|
| Site | Timeout | maxBuffer | Signal | On error |
|---|---|---|---|---|
validateBackend (backendResolver.ts) |
clamp(t, 1000, 15000) |
node default | no | return {ok:false, detail} |
execBackend (backendRunner.ts) |
options.timeoutMs unclamped |
64 MB | yes | reject if no stdout |
isPython312OrNewer (extension.ts) |
10000 hardcoded |
node default | no | resolve false |
runProvisioningStep (extension.ts) |
600000 hardcoded |
10 MB | no | log + reject |
5. Magic numbers should be named
Add mrd-viz/extension/mrd-viz/src/backendConstants.ts (or similar) and replace the scattered literals. (some of these can likely be consolidated)
export const BACKEND_TIMEOUT_MS_DEFAULT = 30_000; // must match package.json default
export const PROBE_TIMEOUT_MS_MIN = 1_000;
export const PROBE_TIMEOUT_MS_MAX = 15_000;
export const PYTHON_VERSION_PROBE_TIMEOUT_MS = 10_000;
export const PROVISIONING_STEP_TIMEOUT_MS = 10 * 60 * 1_000; // pip installs can be slow
export const BACKEND_RESPONSE_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
export const PROVISIONING_LOG_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
export const PYTHON_MODULE_ARGS = ['-m', 'mrd_viz.cli'] as const;
export const PYPI_PACKAGE_NAME = 'mrd-viz';
export const PROVISIONING_PYTHON_CANDIDATES = ['python3.12', 'python3', 'python'] as const;There may be more constants to consolidate in the frontend too.
6. Dead pip install mrd-viz branch
extension.ts (~line 187):
const installArgs = installTarget === 'mrd-viz'
? ['-m', 'pip', 'install', 'mrd-viz']
: ['-m', 'pip', 'install', '-e', installTarget];The 'mrd-viz' branch only fires when no local mrd-viz/backend/ is on disk — i.e. the PyPI path, which per this PR's docs 404s. Options:
- Drop it and fail loud (matches the resolver's philosophy).
- Keep with
// TODO(publish-pypi)so the intent is explicit. - At minimum move the package name to
PYPI_PACKAGE_NAME(see §7) so the same string doesn't live in the webview HTML too.
Also: pass --disable-pip-version-check --no-input to both pip calls. Removes "A new release of pip is available…" noise and prevents pip from blocking on a prompt in restricted environments.
7. Minor
getConfiguredBackendPathisexported inbackendResolver.tsbut has no external callers — drop theexport.PYTHON_MODULE_ARGSis duplicated as a string inpostCreate.shandmrd-viz/backend/tests/test_cli.py; cross-language, can't dedupe, just noting.
…et/toast/doc - Override candidate/logs and the backend-missing page now name the setting the value came from (backendPath vs legacy pythonPath) instead of always backendPath. - Setup snippet installs editable from a checkout (mrd-viz is not on PyPI). - Interpreter-selection toast is setting-neutral (path may be a binary). - Reconcile BACKEND_INSTALL_MODES intro (pre-refactor, not current). - Tests for the legacy-setting labeling.
- centralize magic timeouts/buffers/names in backendConstants.ts - add runProcess() execFile wrapper; use it in resolver, runner, provisioning - use PYPI package name mrd-viz and add --disable-pip-version-check --no-input to pip; TODO(publish-pypi) - drop unused getConfiguredBackendPath export - refresh stale mrdViz.pythonPath -> mrdViz.backendPath references in docs and postCreate - update backend-missing test fixture to realistic post-refactor sources
|
Addressed issues:
|
The un-scoped legacy setting could leak across machines (e.g. a Linux path synced into Windows User settings) and, treated as an exclusive override, suppressed the bundled binary and forced the backend-not-found page. Only the machine-scoped mrdViz.backendPath is honored now. - resolver reads only mrdViz.backendPath; remove legacy inspect() branch and settingKey plumbing - remove deprecated mrdViz.pythonPath declaration and config-change watch - simplify backend-missing intro to name mrdViz.backendPath; drop two legacy tests
Replaces the 5-candidate backend search with deterministic two-tier resolution.
Why
The old resolver probed 5 candidates in a fixed order (override -> bundled -> managed venv -> repo .venv -> python on PATH), silently using whatever answered. That produced the "which Python is actually used?" confusion and let a broken candidate be picked and fail late. Design: docs/BACKEND_INSTALL_MODES.md.
What
mrdViz.backendPathis set it is the only candidate (fail loud if broken); otherwise use the bundled binary. The repobackend/.venvis tried first only in the F5 Development host.mrdViz.backendPath(interpreter or binary), machine-scoped so a path can't be committed to a workspace or synced across machines - this structurally prevents the host-path-leaks-into-containerENOENTbug.mrdViz.pythonPathis deprecated but still honored when explicitly set.backendPath, so the managed venv is a first-class override rather than an implicit candidate.Notes
planBackendCandidatesordering/no-fallback/binary-classification + the tailored page. All 18 pass; compile + lint clean.