Sandbox layer: sync/async parity fixes and hardening - #70
Merged
Merged
Conversation
sfroment
marked this pull request as ready for review
September 25, 2026 12:16
sfroment
added this pull request to stack #72
September 25, 2026 13:03
STOPPED/SLEEPING/UNHEALTHY previously polled until wait_ready timeout; now raises SandboxDeploymentError naming the status. DEGRADED counts as ready (JS classifyServiceStatus parity).
MissingApiTokenError/InvalidPortError keep ValueError parents so published 1.5.x except-clauses still catch them. get_from_id now fails fast with NoSandboxSecretError instead of returning a handle that dies on first connected op.
4xx leaked raw httpx.HTTPStatusError; now SandboxRequestError with status and body. SandboxServiceError subclasses it, so 5xx remains catchable under both names. Network errors retry with the same exponential backoff (Go CLI parity) and wrap as SandboxError.
Default cleanup_on_failure=True mirrors JS: a failed wait deletes the orphaned sandbox (which otherwise burns instance-hours) and the error says so; False restores the old keep-and-retry behavior. Service-creation failures wrap as SandboxError and delete the app this call created, never a caller-provided app.
ServicePool CRUD + claim/get_claim/list_claims/wait_claim_ready, fully mirrored sync+async over the existing generated ServicePoolsApi/PoolClaimsApi. Claims are idempotent by request_id (UUID, preserved across retries) and retry 429/5xx with linear backoff, matching the JS SDK.
Lists type-SANDBOX services with CLI-parity pagination (100 per page). Handles carry no executor secret; the first connected op raises NoSandboxSecretError naming the get_from_id fix instead of a generic SandboxError.
SandboxCommandError was exported but never raised. exec(..., raise_on_error=True) now raises it (carrying the CommandResult); default keeps returning failed results.
The async twin silently built a different sandbox than sync: always-on (idle_timeout 0) with mesh disabled, where sync scales to zero (300) with mesh auto. Aligned, and pinned by a parity suite that compares every sync/async twin's signatures, defaults, order, and kind. cleanup_on_failure also stops deleting a caller-provided app whole — only the service this call created — and sync update_network_policy passes self.host like its async twin.
_create_sync built the definition twice and the async twin rebuilt it inside every snapshot branch; the builders are pure, so one build per twin is behavior-identical.
Cleanup helpers now report whether the delete happened so failure errors stop claiming the sandbox was deleted. Also document delete()'s app-vs-service semantics and pin the unknown-provenance (get_from_id/list) whole-app default.
SandboxFileIO joins the class pairs; the sync-only allowlist now covers every pair; light-sleep override cells pinned.
Regenerated to match the current API surface (the earlier regen claim was empty), with an explicit module list in generate_docs.sh so test doubles never leak into the reference docs.
Ports the pool and claim examples from #64 onto the spec-based pool API and covers the two other new public surfaces: lazy Sandbox.list handles and opt-in exec errors.
A claimed service is detached from the pool and caller-owned: delete it when done. The example now spawns its own pool, shows get_claim, and keeps the request_id replay demo.
sfroment
force-pushed
the
feature/sdk-parity-fixes
branch
from
September 25, 2026 14:11
ec06567 to
0186953
Compare
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.
Summary
The sandbox layer's sync and async twins had shipped drift, plus a hardening
batch: service pools, lazy list handles, opt-in exec errors, fail-closed
readiness, and create-failure cleanup. Examples cover every new public
surface (28–31), porting the pool/claim walkthroughs from #64 onto the
spec-based pool API.
Evidence
Before:
mainsilently builds different sandboxes per flavor:The parity guard written first fails exactly on this:
After:
160 passed; two fresh-context review rounds verifiedsync/async
CreateServicepayloads identical (mesh auto, deep sleep 300)and that the JS mirror expects exactly these defaults (JS mirrors sync:
DEFAULT_IDLE_TIMEOUT = 300).Merge Danger
Door: two-way — behavior changes are deliberate and revertible; public
interfaces unchanged and pinned.
Blast Radius: SDK-wide.
Observable changes: (1) async-create defaults now match sync (scale-to-zero,
mesh auto); (2)
is_healthy()/wait_ready()raiseSandboxDeploymentErroron terminal states instead of polling to timeout (fail closed); (3)
cleanup_on_failuredeletes only the service when the app iscaller-provided; (4) pools work supersedes #64 (this branch's version is the
newer, spec-integrated one — close #64 on merge). Release-note the
scaled-to-zero raise for 1.5.x monitors.