chore(sandbox): address post-merge review threads on #14 + #17 - #20
Merged
Conversation
added 3 commits
August 21, 2026 22:44
recreateAfterFailedStart calls refreshData() on the pre-restart sandbox before reading its network/env/labels for replacementCreateParams. That call was unbounded: if the SDK stalled on the refresh (rate limit, transient 5xx, network stall), start() hung forever without ever reaching the replacement create — the exact hang the recreate path exists to avoid. Wrap the refresh with the same `awaitLookupOperation` + `lookupDeadline` pattern already used five other places in this file (rehydrate after start, initial getById lookup, cleanup guards). Same default timeout, same error message shape, no new abstractions. No dedicated unit-test regression is added: the default lookup deadline is 10s, which would make a "hangs forever without the fix" assertion a 10s+ test, and the awaitLookupOperation utility already has a fast unit test at findByLabels (`lookup exceeded 20ms` at runtime.test.ts:325). Existing recreate-path fake-sandbox tests continue to pass (fakeSandbox exposes no refreshData, so the optional call is a no-op both before and after). Addresses cubic-dev-ai review thread on PR #14 (src/daytona/runtime.ts line 772). Suite unchanged: 220 tests / 215 passed / 5 skipped / 0 failed (skips are DAYTONA_API_KEY-gated smoke, unrelated to this change). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Session-Id: 34c847c1-1a32-4cba-b341-311f694f1145
The smoke suite's `after` hook loops over createdSandboxIds and calls daytona.get(id) followed by daytona.delete(sandbox). daytona get/delete is eventually consistent: after runtime.destroy(handle) already removes the sandbox, get can still resolve briefly, and the subsequent delete then rejects 404. Only the get was guarded by isTestDaytonaNotFound; the delete rejection landed in cleanupFailures and failed the entire after hook even though cleanup succeeded. Extend the same isTestDaytonaNotFound guard to the delete call so a 404 there is treated as "already gone" rather than a cleanup failure. assertDaytonaSandboxGone still runs afterward and confirms absence in either path, so a real never-deleted sandbox continues to fail the hook. Any non-404 delete error still bubbles. Blast radius: DAYTONA_API_KEY-gated smoke suite only, skipped in CI and in this environment (skip count unchanged: 5 before, 5 after). Worst case if the classifier misclassifies a real delete failure as 404: one cleanupFailures entry silently dropped in the smoke `after` hook — bounded, non-production. Addresses cubic-dev-ai review thread on PR #14 (src/daytona/runtime.test.ts line 1890). Pattern mirrors the adjacent get-guard in the same cleanup loop. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Session-Id: 34c847c1-1a32-4cba-b341-311f694f1145
The modes field on SandboxRuntimeCapabilities was mandatory. External
TypeScript consumers that construct fixtures with the five pre-modes
booleans (asyncExec, reattach, detachedLaunch, warmLease, lifecycle)
stopped compiling on upgrade — the shape had a new required field they
had no way to know about, and adding modes to every fixture forces the
consumer to make claims about a runtime they might only be mocking for
one specific concern.
Two-type split preserves both invariants:
- `SandboxRuntimeCapabilities`: five booleans + `modes?: SandboxCapabilityModes`.
The exported base shape a consumer can construct.
- `ResolvedSandboxRuntimeCapabilities = SandboxRuntimeCapabilities &
{ readonly modes: SandboxCapabilityModes }`: what
`resolveSandboxRuntimeCapabilities` actually returns. `modes` is
required here and always populated by the resolver (defaulting to
"unknown", per the discipline the whole modes design encodes).
Callers who receive the resolver's output still see modes as required
(no optional-chain gymnastics at call sites). Callers who construct
fixtures literal-typed as `SandboxRuntimeCapabilities` no longer need
to invent modes they haven't observed. Nobody has to make a claim they
can't back.
Two new port tests pin the contract: one literal-constructs the base
type without modes and asserts the value shape; one assigns the
resolver output into `ResolvedSandboxRuntimeCapabilities` and reads
modes without a cast, so the compiler enforces the strict-return
invariant.
`index.ts` re-exports the new `ResolvedSandboxRuntimeCapabilities`
alongside the existing base type.
Addresses the duplicate P2 threads on PR #17 (chatgpt-codex-connector
and cubic-dev-ai, both at src/port.ts line 255).
Tests: 222 (+2) / 217 pass / 5 skipped / 0 fail. Skips unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Session-Id: 34c847c1-1a32-4cba-b341-311f694f1145
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 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. Comment |
Contributor
Author
sandbox-lead review pass — PASSHuman read confirming safe to surface. This is the post-merge thread-cleanup PR for the already-merged #14 and #17.
Safe. |
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.
Follow-up cleanup for the eight unresolved review threads that were still open on #14 and #17 at merge time — four on
src/daytona/runtime.ts(from #14) and four onsrc/port.ts(from #17). All threads are P1/P2 fromcubic-dev-aiandchatgpt-codex-connector; no human authors.Resolution shape: 4 FIX, 4 ACK+RESOLVE.
Closes on #14 (4 threads → 2 FIX, 2 ACK+RESOLVE)
src/daytona/runtime.tsline 772 (cubic P2, "boundrefreshDatawith the same lookup deadline") — commit6b286bc. Wraps the unboundedrefreshData()call inrecreateAfterFailedStartwithawaitLookupOperation(..., lookupDeadline(undefined), ...), matching the same pattern used 5 other places in this file. A stalled refresh no longer wedges restart recovery.src/daytona/runtime.test.tsline 1890 (cubic P2, "delete-after-destroy race in smoke cleanup") — commit7142b14. Extends the existingisTestDaytonaNotFoundguard from the adjacentdaytona.get(id)call to thedaytona.delete(sandbox)call in the same cleanup loop, so eventually-consistent 404s on delete don't fail theafterhook.src/daytona/runtime.tsline 750 (chatgpt-codex-connector P1, "restrict recreation to verified Toolbox failures"). Prior lead reply on the thread explains: needs error-classification (401/403/429 vs 502/503/504) with SDK-shape tests we don't have; misclassifying dead-daemon as transient is the opposite failure mode and worse;recreateOnFailedStart: falseescape hatch already documented. Tracked as a fast-follow for whoever adds the classifier tests.src/daytona/runtime.tsline 893 (cubic P2, "networkAllowListas array"). Prior lead reply cites Daytona TypeScript SDK docs pinningnetworkAllowList: string(comma-separated). No array variant exists to preserve.Closes on #17 (4 threads → 2 FIX (dedup'd), 2 ACK+RESOLVE)
src/port.tsline 255 (chatgpt-codex-connector P2 + cubic-dev-ai P2, "keep resolved descriptor source-compatible" / "makingmodesrequired breaks TS consumers") — commit6e166c9. Both threads flag the same source-compat regression. Split into two types:SandboxRuntimeCapabilities(five booleans +modes?: SandboxCapabilityModes, source-compatible again) andResolvedSandboxRuntimeCapabilities(intersection withmodes: SandboxCapabilityModesrequired) as the resolver's return type. Preserves both the source-compat contract and the always-present-after-resolution invariant. Two new tests pin the contract.src/port.ts(chatgpt-codex-connector P2, outdated, "encode the adapter-hidden streaming state"). Directly addressed by the second commit of feat(port): structured capability modes alongside the boolean flags #17 which addedCapabilityAbsence = "unknown" | "not-exposed" | "unsupported"—"not-exposed"is exactly the vocabulary for "provider has it, this package's ports do not reach it." Thread alsoisOutdated: trueon the current file layout.src/port.tsline 86 (cubic-dev-ai P2, "add distinctadapter-bufferedoutput mode"). Design decision, not oversight:outputStreamsdescribes observable API behavior (what the caller receives), not underlying provider capability. Adding"adapter-buffered"would push adapters to make claims about the provider's untested capability, which is precisely the "manufacture false confidence" trap the whole modes design is built to avoid. Provider-vs-adapter distinction, if wanted, belongs on a separate axis (a siblingproviderCapabilityHintsfield), not as another member of this union — worth its own PR if it's a first-class concern.Test discipline
npm run build && npm testafter each commit. Suite: 220 → 222 (+2 tests for the port source-compat contract). 217 pass / 5 skipped / 0 fail on the tip.runtime.test.ts404 tolerance) lands inside theDAYTONA_API_KEY-gatedDaytonaRuntime smokesuite that was already skipping in CI — so skipped count is unchanged (5 before, 5 after) rather than +1 as a naïve diff might imply. This is a coverage-preserving change, not a coverage regression.Follow-ups tracked (not landed here)
assertSandboxExecHealthy(from the ACK'd fix(daytona): recover exec after sandbox restart #14 line-750 thread). Deferred because it needs SDK-error-shape tests to avoid misclassifying dead-daemon as transient. Consumers who don't want automatic replacement can already setrecreateOnFailedStart: false.providerCapabilityHintssibling field is the shape to consider rather than expandingOutputStreamMode.Cross-links
🤖 Generated with Claude Code