The problem
WorkspaceCommand.stdin is part of the WorkspaceRuntime interface (services/api/src/workspaces/runtime.ts). The Docker runtime (docker.ts) and the fake (fake.ts) honor it; the Vercel runtime throws workspace_stdin_unsupported (vercel.ts). I found no caller of stdin outside the three runtimes, so nothing is broken today. But the same command succeeds on two runtimes and fails on the third, and the test suite cannot see this class of difference: the fake backs 12 test files, the Vercel runtime is tested separately with a module mock (vi.mock("@vercel/sandbox")), and the Docker test is a single case that runs only under FACILITY_E2E_DOCKER=1.
Behaviors I compared by reading the code do agree today: resume: false on stopped compute throws workspace_not_running in all three; cancel and timeout use workspace_command_canceled and workspace_command_timeout in all three; destroy of a missing workspace is a no-op in Docker and Vercel (and tested for the fake in workspace-runtime.test.ts). Smaller differences: Vercel clamps timeoutMs to 5 hours, and Vercel expose can run a native-preview health check the others do not.
The evaluation issues for new providers (#415, #360) ask whether a provider satisfies the WorkspaceRuntime contract, but there is no shared, executable definition of that contract to check it against. workspace-runtime.test.ts already has fake-only cases (suspend and compute replacement, idempotent deletion, restore into a fresh workspace, cancel, path-escape and port validation) that read like contract tests but are not parameterised.
What you propose
- Decide
stdin first. Either make it part of the contract (Vercel emulates it, for example through a temporary file) or remove it from WorkspaceCommand. Either way the fake must match the decision. I would like maintainers to choose; if the answer is "remove it", this part is a small PR on its own.
- A shared contract suite. A parameterised test file taking a
WorkspaceRuntime factory, seeded from the existing fake-only cases in workspace-runtime.test.ts. First scope: exec (streams onOutput, honors the abort signal and timeoutMs, stdin per item 1), resume: false not waking stopped compute, inspect, suspend, and idempotent destroy.
- Where it runs. Against the fake in the default suite; against the Vercel runtime using the existing SDK mock, where the mock can support the case; against Docker under
FACILITY_E2E_DOCKER=1. Cases a provider cannot meet (for example Vercel-specific expose checks) are declared as explicit, named exceptions rather than skipped silently.
- Out of scope for this issue: a provider registry or any database change, and the E2E path filter in
ci.yml.
New files only for the first PR (no edits to docker.ts, which has open PRs #422 and #324, or to workspace-runtime.integration.test.ts, touched by #452, #424 and #324).
What you considered instead
I'm willing to prepare the suite as a first PR once the stdin decision is made. I have not verified how far the Vercel SDK mock can go before it needs rework, and would report that in the PR.
The problem
WorkspaceCommand.stdinis part of theWorkspaceRuntimeinterface (services/api/src/workspaces/runtime.ts). The Docker runtime (docker.ts) and the fake (fake.ts) honor it; the Vercel runtime throwsworkspace_stdin_unsupported(vercel.ts). I found no caller ofstdinoutside the three runtimes, so nothing is broken today. But the same command succeeds on two runtimes and fails on the third, and the test suite cannot see this class of difference: the fake backs 12 test files, the Vercel runtime is tested separately with a module mock (vi.mock("@vercel/sandbox")), and the Docker test is a single case that runs only underFACILITY_E2E_DOCKER=1.Behaviors I compared by reading the code do agree today:
resume: falseon stopped compute throwsworkspace_not_runningin all three; cancel and timeout useworkspace_command_canceledandworkspace_command_timeoutin all three;destroyof a missing workspace is a no-op in Docker and Vercel (and tested for the fake inworkspace-runtime.test.ts). Smaller differences: Vercel clampstimeoutMsto 5 hours, and Vercelexposecan run a native-preview health check the others do not.The evaluation issues for new providers (#415, #360) ask whether a provider satisfies the
WorkspaceRuntimecontract, but there is no shared, executable definition of that contract to check it against.workspace-runtime.test.tsalready has fake-only cases (suspend and compute replacement, idempotent deletion, restore into a fresh workspace, cancel, path-escape and port validation) that read like contract tests but are not parameterised.What you propose
stdinfirst. Either make it part of the contract (Vercel emulates it, for example through a temporary file) or remove it fromWorkspaceCommand. Either way the fake must match the decision. I would like maintainers to choose; if the answer is "remove it", this part is a small PR on its own.WorkspaceRuntimefactory, seeded from the existing fake-only cases inworkspace-runtime.test.ts. First scope:exec(streamsonOutput, honors the abort signal andtimeoutMs,stdinper item 1),resume: falsenot waking stopped compute,inspect,suspend, and idempotentdestroy.FACILITY_E2E_DOCKER=1. Cases a provider cannot meet (for example Vercel-specificexposechecks) are declared as explicit, named exceptions rather than skipped silently.ci.yml.New files only for the first PR (no edits to
docker.ts, which has open PRs #422 and #324, or toworkspace-runtime.integration.test.ts, touched by #452, #424 and #324).What you considered instead
stdin. Cheapest, and may be the right outcome for item 1, but it does not give Experiment: evaluate DigitalOcean Harness Runtime as a sandbox provider #415 and Evaluate Docker Sandboxes for stronger workspace isolation #360 a contract to test a new provider against.stdindifference went unnoticed.I'm willing to prepare the suite as a first PR once the
stdindecision is made. I have not verified how far the Vercel SDK mock can go before it needs rework, and would report that in the PR.