Skip to content

refactor: extract shared buildSessionConfig to delete the SESSION_CONFIG drift point#712

Merged
ColeMurray merged 2 commits into
mainfrom
refactor/shared-session-config
Jun 8, 2026
Merged

refactor: extract shared buildSessionConfig to delete the SESSION_CONFIG drift point#712
ColeMurray merged 2 commits into
mainfrom
refactor/shared-session-config

Conversation

@ColeMurray

@ColeMurray ColeMurray commented Jun 8, 2026

Copy link
Copy Markdown
Owner

Summary

Responds to the deep-review comment on #711: the one-line fix patched the Daytona omission but preserved the design flaw that caused it — every provider still hand-rolls the SESSION_CONFIG shape, so the next provider can silently drop a field again.

This makes the contract explicit in a single shared typed builder and has the providers call it instead of assembling ad-hoc objects, deleting the drift point rather than patching one more provider-local copy.

[deep review] This fixes the immediate Daytona omission, but it preserves the exact design flaw that caused the bug: every provider still hand-rolls the SESSION_CONFIG shape. […] can we make that contract explicit in a shared typed builder and have providers call it instead of manually assembling ad-hoc objects? That would delete the drift point rather than patching one more provider-local copy.

Builds on #711 (already merged to main).

What changed

  • New packages/control-plane/src/sandbox/sandbox-env.ts — exports SessionConfigPayload (the canonical contract, mirroring the runtime's Python SessionConfig in sandbox-runtime/.../types.py) and buildSessionConfig(input), the single source of truth for the serialized shape.
  • providers/daytona-provider.ts / providers/vercel/provider.ts — both buildEnvVars methods now call buildSessionConfig(config) instead of each hand-rolling the object. Net −20 lines of duplicated assembly.

The mcp_servers key is built as present-but-undefined so JSON.stringify omits it when absent — the runtime treats an absent key and an empty list identically (session_config.get("mcp_servers") or []).

Why this is the real fix

The Daytona bug existed because Daytona's local copy of the SESSION_CONFIG object simply never added mcp_servers (and its Record<string, string> type even prevented it). With a shared builder there is exactly one place the shape is defined, so a provider cannot diverge — the entire class of bug is removed, not just this instance.

Behavior preservation

This is a pure refactor. The existing provider tests pass unchanged (Daytona 28, Vercel 13) — including the mcp_servers assertions — which proves the serialized SESSION_CONFIG is identical before and after. Added 3 focused unit tests for buildSessionConfig itself.

  • Full monorepo unit suite: 2026 passed (149 files)
  • tsc --noEmit, ESLint, Prettier: clean

Scope / notes

  • Modal is intentionally untouched — its Python path already uses the typed SessionConfig Pydantic model (not an ad-hoc object), so it was never a drift source.
  • No Terraform change needed. sandbox-env.ts is imported only by the two providers (per-session spawn). The Vercel base-snapshot source hash in vercel.tf covers providers/vercel/{bootstrap,base-snapshot,client}.ts but not providers/vercel/provider.ts, so SESSION_CONFIG assembly is not part of the base image — the paths=() array does not need updating. (Re: the terraform-source-hash-paths gotcha.)

Follow-ups (deliberately out of scope to keep this reviewable)

Per the post-merge review, the same sandbox-env.ts is the natural home for the next de-duplication steps (V6): a shared buildSystemEnv, and folding the verbatim-duplicated resolveTunnelPorts / deriveCodeServerPassword / buildTags helpers into canonical homes. Left for separate focused PRs.

Summary by CodeRabbit

  • Refactor

    • Consolidated session-configuration construction across sandbox components to ensure consistent serialization and preserve server passthrough and branch handling.
  • Bug Fixes

    • Sandbox restore now reliably includes the expected server settings in restore requests.
  • Tests

    • Added tests verifying mapping to the runtime contract, omission of absent branch, and correct handling of server lists.

Every sandbox provider hand-rolled the SESSION_CONFIG payload, which let
the shape drift per provider -- the root cause of the Daytona mcp_servers
drop fixed in #711. Extract a single typed builder (sandbox/sandbox-env.ts)
owning the canonical contract (mirrors the runtime's Python SessionConfig)
and have the Daytona and Vercel providers call it instead of assembling
ad-hoc objects.

Behavior is unchanged: the existing provider tests pass as-is, proving the
serialized payload is identical. The drift point is removed so the next
provider cannot silently reintroduce the same class of bug.
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8956b40c-c76b-4002-9f22-ee08fe8dc42e

📥 Commits

Reviewing files that changed from the base of the PR and between cdd12c6 and 047b1ee.

📒 Files selected for processing (2)
  • packages/control-plane/src/sandbox/client.test.ts
  • packages/control-plane/src/sandbox/client.ts

📝 Walkthrough

Walkthrough

This PR centralizes SESSION_CONFIG assembly by adding types and buildSessionConfig, tests that builder, updates ModalClient.restoreSandbox to use it, and migrates Daytona and Vercel providers to call the shared builder instead of constructing session config inline.

Changes

Session Config Builder Extraction

Layer / File(s) Summary
Session config type contracts
packages/control-plane/src/sandbox/sandbox-env.ts
SessionConfigPayload and SessionConfigInput types define the runtime shape and provider-agnostic input contract for building session config payloads.
buildSessionConfig function
packages/control-plane/src/sandbox/sandbox-env.ts
Maps provider inputs to the canonical SessionConfigPayload shape, conditionally omits branch when absent, and consistently sets mcp_servers for deterministic JSON serialization.
Test suite
packages/control-plane/src/sandbox/sandbox-env.test.ts
Verifies snake_case field mapping, conditional branch omission, and JSON serialization behavior when MCP servers are not configured.
Modal client restore
packages/control-plane/src/sandbox/client.ts, packages/control-plane/src/sandbox/client.test.ts
ModalClient.restoreSandbox now uses buildSessionConfig(request) for the session_config payload; test asserts mcp_servers is preserved in the outbound request.
Provider migrations
packages/control-plane/src/sandbox/providers/daytona-provider.ts, packages/control-plane/src/sandbox/providers/vercel/provider.ts
Both providers replace inline sessionConfig object construction with calls to buildSessionConfig, centralizing payload assembly.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • open-inspect

Poem

🐰 I stitched one config to rule them all,
A builder neat within the hall,
Providers call, tests give a cheer,
mcp_servers kept ever clear,
Hopping tidy, session set — hooray!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: extracting a shared buildSessionConfig function to eliminate SESSION_CONFIG duplication across sandbox providers.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/shared-session-config

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.

Comment thread packages/control-plane/src/sandbox/sandbox-env.ts

@open-inspect open-inspect Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Reviewed PR #712, refactor: extract shared buildSessionConfig to delete the SESSION_CONFIG drift point, by @ColeMurray. The change centralizes SESSION_CONFIG assembly for Daytona and Vercel into buildSessionConfig, preserving the existing serialized shape while removing duplicated provider-local construction.

Files changed: 4, additions/deletions: +112/-20.

Critical Issues

None found.

Suggestions

None blocking. The helper intentionally preserves current Daytona/Vercel behavior rather than expanding to every optional Python SessionConfig field, which is appropriate for this refactor scope.

Nitpicks

None.

Positive Feedback

  • The shared builder removes the exact drift point that caused the previous provider mismatch.
  • Tests cover snake_case mapping, optional branch omission, and JSON serialization behavior for absent MCP servers.
  • Existing provider tests continue to assert the end-to-end environment payloads.

Verification

  • npm test -w @open-inspect/control-plane -- sandbox-env providers/daytona-provider.test.ts providers/vercel/provider.test.ts: 44 tests passed.
  • npm run typecheck -w @open-inspect/control-plane: passed.

Questions

None.

Verdict

Approve.

Addresses deep-review feedback on #712: ModalClient.restoreSandbox
hand-rolled the same snake_case session config object, leaving a third
drift point the builder did not own. Route it through buildSessionConfig
so a new SESSION_CONFIG field cannot be silently missed on the Modal
restore path.

Behavior-preserving: Modal decodes session_config into the Pydantic
SessionConfig (branch/mcp_servers default to None), so the builder
omitting absent keys is equivalent to the prior explicit nulls. Adds a
regression test asserting restore carries the full session_config.
@ColeMurray ColeMurray merged commit fcc8254 into main Jun 8, 2026
20 of 22 checks passed
@ColeMurray ColeMurray deleted the refactor/shared-session-config branch June 8, 2026 06:33
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