Skip to content

feat(pasqal): python provisioning — activation venv + AMICO_PYTHON injection - #189

Merged
kateebonner merged 1 commit into
mainfrom
kate/pasqal-venv-provisioning
Jul 20, 2026
Merged

kateebonner merged 1 commit into
mainfrom
kate/pasqal-venv-provisioning

Conversation

@kateebonner

Copy link
Copy Markdown
Contributor

Implements the Slack-thread decision (option 1: extension-provisioned venv; Aaron + Kate aligned). Issue to be filed from the thread — will re-title fix(#NN) when it exists.

Summary

Fresh installs cannot connect to Pasqal: the fork's validator spawn resolves $AMICO_PYTHON → bare python3, no ambient interpreter has pasqal-cloud, the validator exits 1 ("SDK not installed"), and the panel misrenders the config lane as "Service unreachable" (found live-testing v0.0.3-alpha; root-caused against the shipped amicode.8 binary, whose AMICO_PYTHON support was verified live before this was written).

  • Provisioning owner: the extension. pasqal_python.ts (mirrors pasqal_assets.ts): probe host python3 ≥ 3.10 (PATH + well-known absolute candidates — Dock-launched VS Code inherits the launchd-minimal PATH), -m venv under <opsDir>/venvs/pasqal-connector, pip install -r the staged requirements.txt. Hash-gated: stamp written only after pip exit 0, so failed provisions retry free next activation; fast path is stat+hash, zero subprocesses.
  • Env seam / S37 exception: injected via the single buildServerSpawnEnv builder used by all three spawn sites (boot, solver-mode respawn, vault respawn) — no respawn path can drop it. S37 ("no AMICO_* env propagation") note amended in place: this is server-child plumbing for the fork's validator spawn, NOT amico-run contract; amico-run still receives nothing via env.
  • Fail-closed AMICO_PYTHON: provisioning failure never sets the var (absent, not empty — the fork's fallback is byte-identical to today), logs ONE actionable line per lane (no python / venv failed / pip failed-offline). Slow path runs in the background, never blocks activation; on success it mutates the live spawn env (ServerManager re-reads at start()) and bounces via the existing amicode.restartServer, so a fresh install self-heals without a reload.
  • Override precedence: host $AMICO_PYTHON wins outright and skips provisioning (the $AMICO_PASQAL_VALIDATOR convention; absent-not-empty semantics match the fork's resolver).
  • Token safety: provisioner child env is built from scratch as { PATH, HOME } — poison-token test proves no secret rides argv, env, or failure copy.

Freeze respected

release.yml untouched, no tag cut. amico-run argv contract untouched. launch.ts zero-line diff. Unprovisioned sessions: buildServerSpawnEnv output byte-identical.

Deferred (out of scope)

Fork-side rendering of the config-lane error text (panel currently shows state-generic "Service unreachable" copy; the route already returns the precise message). amico-pasqal launcher interpreter unification (submit path). Windows venv layout (lock targets darwin-arm64/linux-x64 only). Staging of the 3 submit-path connector scripts.

Gate results

  • extension vitest: 713 pass / 15 skip (+13: 10 pasqal_python incl. real-interpreter venv lane, +1 server_auth, +3 gate mutation) · tsc --noEmit clean · prettier clean
  • boot smoke: [smoke] PASS (vendored v1.17.3-amicode.8)
  • new behavioral gate assert_provisioned_python.mjs (mirrors assert_packaged_cli.mjs): local run 4/4 PASS — pin parse, real venv+pip from shipped assets, SDK import at ==0.23.0, validator env-guard smoke. Wired in ci.yml only: fast job (source lane) + vsix-gate job (assets unzipped from the real artifact).
  • Julia solve E2E: n/a (no template/Julia changes)

🤖 Generated with Claude Code

…jection

The fresh-install fix: the fork's validator spawn resolves $AMICO_PYTHON →
bare python3, and on a fresh machine no ambient interpreter has pasqal-cloud,
so the validator exits 1 and the panel misreports 'Service unreachable'. The
extension now owns the interpreter: venv from the STAGED requirements.txt
(hash-gated, stamp-on-success), injected via the single buildServerSpawnEnv
seam so no respawn path drops it; background slow path self-heals via the
existing restart command. Host $AMICO_PYTHON wins outright. Behavioral CI
gate (source + vsix lanes) proves the shipped assets provision a working
interpreter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kateebonner

Copy link
Copy Markdown
Contributor Author

Verification record (merging on this evidence):

  • CI: green — including both new provisioning-gate lanes (source-staged in fast, artifact lane in vsix-gate against the unzipped .vsix), each doing a real venv + PyPI install + SDK import at the pin on clean ubuntu runners.
  • Unit: extension vitest 713 pass / 15 skip (+13 this PR: provisioning module incl. real-interpreter lane + poison-token/env-allowlist adversarial lanes, seam test, gate mutation tests) · tsc clean · prettier clean · boot smoke PASS on vendored amicode.8.
  • Sandbox E2E on the CI artifact (isolated $HOME, no AMICO_PYTHON anywhere): staged → background-provisioned in ~145s cold → venv imports pasqal-cloud 0.23.0 → self-heal restart delivered AMICO_PYTHON=<venv python> into the live server env → real-credential Pasqal connect succeeded (state connected, FRESNEL devices enumerated, token-only pasqal.json at 0600). Second boot takes the stat+hash fast path (zero subprocess); a requirements change re-provisions (stamp rotates); a genuine disk-full pip failure exercised the no-stamp retry lane and recovered on the next activation.
  • Known transient (deferred, fork-side): on a truly fresh machine the first ~2.5 min before self-heal completes can still fail a very eager first connect with the generic "unreachable" copy — the fork-side error-rendering fix covers this window.

🤖 Generated with Claude Code

@kateebonner
kateebonner marked this pull request as ready for review July 20, 2026 20:46
@kateebonner
kateebonner merged commit 0bf3cc4 into main Jul 20, 2026
5 checks passed
jack-champagne added a commit to harmoniqs/opencode that referenced this pull request Jul 28, 2026
The unit job died with exit 137 and reported nothing. Not an OOM (peak
0.37GB of 32GB, zero faults) — a segfault inside @napi-rs/keyring's
native setPassword(), reached from the pasqal submit success path.

Install the in-memory secret store, as the sibling
amicode-connections.test.ts already does. Production is unaffected: the
same write succeeds under `bun run … serve`, verified end-to-end against
Pasqal with real credentials.

Also swap the stub validator from a bun-executed .mjs to a
python3-executed .py. amicode provisions <opsDir>/venvs/pasqal-connector
and passes it as AMICO_PYTHON (harmoniqs/amicode#189), so the production
interpreter is always a real python; bun-as-interpreter tested a
configuration that never ships.

Drop the windows unit lane: opencode.lock.json ships darwin-arm64 and
linux-x64 only, and it was the ~50min long pole while red for an
unrelated reason (#76).

Refs #82, #76
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant