Skip to content

feat(sandbox): accept branch + isNewBranch in POST /api/sandbox body#536

Closed
sweetmantech wants to merge 5 commits intotestfrom
feat/sandbox-branch-input
Closed

feat(sandbox): accept branch + isNewBranch in POST /api/sandbox body#536
sweetmantech wants to merge 5 commits intotestfrom
feat/sandbox-branch-input

Conversation

@sweetmantech
Copy link
Copy Markdown
Contributor

@sweetmantech sweetmantech commented May 8, 2026

Summary

Closes the last open-agents parity gap on the provision endpoint. The web client already sends both fields with every request — they were being silently dropped by the Zod schema, so sandboxes provisioned via api always landed on the repo's default branch.

Body routing matches `connectSandbox.state.source`:

  • `isNewBranch=false` (or omitted) → forwards as `{ branch }`
  • `isNewBranch=true` → forwards as `{ newBranch }`
  • Both omitted → behaves as before (default branch)

Why this matters now

Precursor for the open-agents → api cutover. Without this, cutting over POST /api/sandbox would silently break:

  • Sessions with a recorded non-default `branch`
  • Sessions bootstrapped with `isNewBranch: true` + a generated branch name (`xy/abcd1234` convention)

Test plan

  • `pnpm test` — 2629 / 2629 pass
  • `pnpm lint:check` — clean
  • `npx tsc --noEmit` — clean
  • Smoke: provision with `isNewBranch: true` + `branch: "feat/cutover-test"`, shell into the sandbox, verify `git rev-parse --abbrev-ref HEAD` reports the new branch

🤖 Generated with Claude Code


Summary by cubic

POST /api/sandbox now accepts branch and isNewBranch so sandboxes can check out an existing branch or create a new one, matching the web client/open-agents behavior. Omitting both fields still uses the repo’s default branch.

  • New Features
    • Schema: added optional branch and boolean isNewBranch; rejects non-boolean values.
    • Routing: isNewBranch=true → forward as { newBranch }; isNewBranch=false or omitted → forward as { branch }; both omitted → no branch fields sent.
    • Tests: added coverage for branch/newBranch forwarding and the default-branch case.

Written for commit 98d1c1f. Summary will update on new commits.

sweetmantech and others added 5 commits May 7, 2026 20:47
Closes the remaining open-agents parity gaps in the two read endpoints
that the chat UI hits on session re-entry / tab refocus.

**Status handler (`GET /api/sandbox/status`)**
- **Failed-state self-heal**: when `hasRuntimeSandboxState` matches but
  `lifecycle_state === "failed"`, recovers to `active` + clears
  `lifecycle_error` + refreshes `sandbox_expires_at`. Without this,
  the UI gets stuck on "Paused" after a transient lifecycle eval
  hiccup even though the runtime is still alive.
- **`hasSnapshot` recognizes hibernated state**: now true when
  `snapshot_url` is set OR `lifecycle_state === "hibernated"` AND the
  state has a resumable name. Previously only checked `snapshot_url`,
  so paused-but-resumable sessions reported `hasSnapshot: false`.

**Reconnect handler (`GET /api/sandbox/reconnect`)**
- **Transient-error preservation**: only collapses to `expired` when
  the probe error matches a known permanent-failure pattern
  (`isSandboxUnavailableError`: 404 / 410 / "sandbox not found" /
  "sandbox is stopped" / "sandbox probe failed" / "expected a stream
  of command data"). Anything else (502 / connection reset / timeout)
  is treated as transient: runtime state is preserved, response is
  `connected` with a conservative `safeExpiresAt` (only forwarded if
  still in the future). This avoids forcing a full sandbox rebuild
  on a flaky network.
- **Aggressive cleanup gating**: not-found errors drop the resume
  handle (sandbox is gone-gone, can't be brought back), but other
  unavailable errors keep it via `clearUnavailableSandboxState` so a
  future provision can reuse the name.
- **Expires sync**: on a successful probe, `sandbox_expires_at` is
  refreshed from the live SDK state — without it the FE timer drifts
  from reality.
- **Lifecycle recovery**: on a successful probe, if the row was in
  `lifecycle_state: "failed"`, recovers to `active` + clears
  `lifecycle_error`.

**New helpers (each its own SRP file with a vitest red→green pass):**
- `isSandboxNotFoundError` — 404 / sandbox-not-found patterns
- `isSandboxUnavailableError` — broader permanent-failure dispatcher
- `clearSandboxResumeState` — collapses state to just `{ type }`
- `clearUnavailableSandboxState` — picks between resume-clear and
  state-clear based on the error class

Tests: 2622 / 2622 pass. Lint + tsc clean for changed files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per review: the inline helper in getSandboxReconnectHandler.ts is its
own concern (read epoch-ms expiresAt off a sandbox state) and belongs
in a dedicated file alongside the other state predicates.

Adds `lib/sandbox/getStateExpiresAt.ts` with a focused test (numeric
match, non-numeric reject, null/scalar guard). Reconnect handler now
imports from the new path; no behavior change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the last open-agents parity gap on the provision endpoint.
The web client already sends both fields with every request — they
were being silently dropped by the Zod schema, which meant sandboxes
provisioned via api always landed on the repo's default branch.

Real-world impact this unlocks:
- Sessions with a recorded `branch` (e.g. "develop") now check out
  that branch on provision, matching what's persisted on the row.
- Sessions with `isNewBranch: true` (e.g. session bootstrap minted
  `xy/abcd1234`) now create a fresh ref off the repo's default,
  matching open-agents' behavior.

Body routing matches `connectSandbox.state.source`:
- `isNewBranch=false` (or omitted) → forwards as `{ branch }`
- `isNewBranch=true` → forwards as `{ newBranch }`

TDD: red tests for both branches + a kept-default case → green via
schema extension and a small spread in the source object. The prior
"strips unknown branch input" test is replaced with a "preserves
branch/isNewBranch" test plus a rejection test for non-boolean
isNewBranch.

Tests 2629 / 2629 pass. Lint + tsc clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
api Ready Ready Preview May 8, 2026 2:15am

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Warning

Rate limit exceeded

@sweetmantech has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 33 minutes and 29 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f19c79f7-56a9-4841-a9ef-6c93b2b76d26

📥 Commits

Reviewing files that changed from the base of the PR and between 881d9d2 and 98d1c1f.

⛔ Files ignored due to path filters (2)
  • lib/sandbox/__tests__/createSandboxHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/sandbox/__tests__/validateCreateSandboxBody.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
📒 Files selected for processing (2)
  • lib/sandbox/createSandboxHandler.ts
  • lib/sandbox/validateCreateSandboxBody.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/sandbox-branch-input

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sweetmantech
Copy link
Copy Markdown
Contributor Author

Closing per author — branch / isNewBranch will be treated as ignored concepts going forward, no need to plumb them through the api.

@sweetmantech sweetmantech deleted the feat/sandbox-branch-input branch May 8, 2026 02:23
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