Skip to content

feat(#160): Pasqal device path — per-solve device selection + mandatory non-free confirm gate - #187

Merged
kateebonner merged 3 commits into
mainfrom
amico/issue-160-pasqal-device-path
Jul 20, 2026
Merged

feat(#160): Pasqal device path — per-solve device selection + mandatory non-free confirm gate#187
kateebonner merged 3 commits into
mainfrom
amico/issue-160-pasqal-device-path

Conversation

@kateebonner

Copy link
Copy Markdown
Contributor

Summary

Closes #160. The agent-driven Pasqal device path: pick a device exposed by an existing Pasqal Connection and route one solve's pulse to it, with a mandatory confirm gate before any non-free (paid emulator / real QPU) run. Two amico subcommands:

  • amico pasqal devices — lists the devices the connection exposes, each tagged free / non-free. Read only from the non-secret connections status cache ($AMICODE_CONNECTIONS_FILE — exactly what the fork's connections route writes). Never opens the token file, never spawns the validator.
  • amico pasqal submit --device <id> --artifact <pulse.toml> [--confirm <h>] [--dry-run] — routes one solve to one explicitly chosen device.

Acceptance criteria

  • AC1 — per-solve selection: --device is mandatory; nothing auto-selects. Device metadata comes from the persisted cache.
  • AC2 — pulse contract: lands the spike's Piccolo→Pulser pulse-translation contract (golden-fixture tested).
  • AC3 — token safety: the verb spawns the amico-pasqal launcher as a subprocess, so its own process never reads ~/.amico/pasqal.json; the launcher is the sole token reader + env-injector. No token appears in the digest, JSON output, or launcher argv. Auth is via the SDK token-provider (token only — no username/password anywhere). Proven by poison-token leak tests on both the TS verb and the Python connector.
  • AC4 — actionable failures: disconnected connection, expired token, and empty/stale device list each disable the path with a distinct, non-zero-exit message (reconnect / revalidate) — never a silent failure.
  • AC5 — mandatory paid-confirm gate: a non-free target is refused unless --confirm matches the secret-free submission digest (bound to device + pulse content + project), checked before --dry-run and before any spawn. Default-deny: only EMU_FREE is free; no auto-fallback to a paid target. Defense-in-depth: the Python connector independently re-guards paid submissions (--yes).

Testing

  • amico-run vitest 337 pass (30 files; +23 new incl. adversarial gate-bypass + token-leak) · tsc --noEmit clean · prettier clean
  • Python connector suite 55 pass (python3 -m unittest discover -s tests; +12 submit-CLI, pulse-contract golden)
  • Merges cleanly against current main; no overlap with the marketplace/release workflow changes.

Notes

  • Flag is --artifact (not --pulse) to respect the S31 physics-flag guard, matching catalog_verb's pulse-artifact idiom.
  • Real-QPU job lifecycle management is out of scope (unknown devices are rejected).

🤖 Generated with Claude Code

kateebonner and others added 3 commits July 20, 2026 03:31
Port the pulse contract, its hermetic test suite, and the demos from the
kate/pasqal-cloud-connectivity spike onto the product connector dir
(packages/extension/scripts/pasqal-connector), beside the #164 validator.

- pulse_contract.py: the single source of truth for pulse.toml → validated
  Pulser Sequence — device limits read from the Device at call time, real
  constraint violations RAISE, sub-DUST_TOL optimizer dust clipped silently.
- translate_and_simulate.py: local QuTiP re-simulation (the physics oracle).
- tests/test_pulse_contract.py + tests/fixtures/pulse_golden.toml: the AC2
  contract gate — schema/device/atoms rejections + golden-fixture simulation.
- demos/: the spike's solve_*.jl + *_demos.py (demos stay demos; scope Out).

Gate: `python3 -m unittest discover -s tests -p test_pulse_contract.py` → 27 pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… AC5)

The device-path submission connector, adapted from the spike's
submit_optimized.py. Whereas the spike authenticated with username/password on
env, this connector takes the runtime credential as a TOKEN only
(PASQAL_TOKEN + PASQAL_PROJECT_ID via the amico-pasqal launcher's env
injection) and authenticates through the SDK's token-provider mechanism (a
TokenProvider subclass whose get_token() returns the stored token). No
password/username exists anywhere in the flow (AC3).

- Per-submission device selection (--device); the default only ever picks the
  FREE emulator (EMU_FREE), never a paid target — no auto-fallback.
- Paid-submission gate, defense in depth (AC5): a non-free device is refused
  BEFORE any auth/network unless the caller passes --yes; unknown devices
  rejected (real-QPU job management is out of scope).
- Secret hygiene: fixed failure strings only; exception text (which can echo a
  bearer token) is never printed — proven by a poison-token leak test.
- --dry-run builds + validates the sequence with no credentials or network.

TDD: tests/test_submit_cli.py written first (12 tests, red → green). Full
connector suite `python3 -m unittest discover -s tests` → 55 pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…te (AC1/AC3/AC4/AC5)

The agent-driven device path, wired behind the Pasqal Connection. Two
subcommands on the `amico` CLI:

  amico pasqal devices
      lists the devices the connection exposes (each tagged free/non-free),
      read ONLY from the non-secret connections status cache
      ($AMICODE_CONNECTIONS_FILE — exactly what the fork's connections route
      writes). Never opens the token file, never spawns the validator.

  amico pasqal submit --device <id> --artifact <pulse.toml> [--confirm <h>] [--dry-run]
      routes ONE solve's pulse to ONE explicitly chosen Device.

AC1: pick a device from the persisted device metadata + submit (through the
     amico-pasqal launcher). --device is mandatory; nothing auto-selects.
AC3: token safety — the verb spawns the amico-pasqal launcher as a SUBPROCESS,
     so its own process never reads ~/.amico/pasqal.json; the launcher is the
     one place the token is read + env-injected. No token appears in the
     digest, JSON output, or launcher argv (proven by a poison-token test that
     also plants a poison "token" key in the cache — scrubbed by whitelist read).
AC4: a disconnected connection, an expired token, and an empty-or-stale device
     list each disable the path with a distinct, actionable block (reconnect /
     revalidate), non-zero exit — never a silent failure.
AC5: the MANDATORY paid-confirm gate — a NON-FREE (paid emulator / real QPU)
     target is REFUSED unless --confirm matches the secret-free submission
     digest (bound to device + pulse content + project). Checked BEFORE
     --dry-run and BEFORE any spawn; a wrong/absent confirm never spawns; there
     is no auto-fallback to a paid target. Default-deny: only EMU_FREE is free.

Flag is --artifact (not --pulse) to respect the S31 physics-flag guard, matching
catalog_verb's pulse-artifact idiom.

TDD: test/pasqal_verb.test.ts written first (23 tests incl. adversarial
gate-bypass + token-leak), red → green. Gates: amico-run vitest 337 pass,
tsc --noEmit clean, prettier clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kateebonner
kateebonner merged commit de23529 into main Jul 20, 2026
5 checks passed
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.

Pasqal device path — optimized pulse to a Pasqal Cloud device through the Pasqal Connection

1 participant