Problem
k8sSandboxExtension deliberately runs a two-tier transport (extension.ts:30-34):
fastTransport — persistentExecInPod, ONE long-lived bash with nonce-framed commands, serving the small request/response ops (read/write/edit/ls/find).
streamTransport — per-call exec, for the ops that need onData / abort / long-running semantics (bash, grep, user !).
But opts.transport overrides both (extension.ts:38, :49-51), and run-leaf.ts:786 passes exactly that for a leased gRPC presence record:
k8sSandboxExtension({ config: selected?.config ?? null, transport: selected?.transport })
So on the gRPC path the fast tier does not exist. Every file op becomes a full SandboxExec.Exec round trip through the relay, ending in a fresh bash -c in the sandbox. On the kubectl path the same op is one framed line on an already-running bash.
This is a silent asymmetry between transports of the kind ADR-0024 and transport.ts's conformance battery exist to prevent — the battery pins the output cap and the exec-timeout ceiling across all three implementations, but nothing pins whether the fast tier is available, so a caller above the seam cannot see or control the difference.
Why it matters more now
File ops are the highest-frequency tool calls in a coding agent. Under P6 (#244) a single host runs W workers × S multiplexed sessions against K sandbox containers, so per-op process churn concentrates: what is one extra round trip at 1:1 becomes W×S round trips and W×S bash spawns against K containers.
E6/E7 measured per-sandbox duty at 2–8% of leaf wall-clock, and E7 validated mixed-ref converge correctness at 6 concurrent refs on one pod. P6's rungs go well past that, so whether duty holds under this load is an open question rather than a settled one. P6 §5.2 records per-file-op latency and sandbox-container CPU precisely so a sandbox-bound run is not misreported as a harness density limit.
Why this is not P6's to fix
The fix lifts both substrates equally. Folding it into P6 would improve the VM arm and the Knative arm at the same time, while adding transport surface to a slice whose job is to measure the deployment tier. P6 instead stays correct without it by pinning both E9 arms to the relay + gRPC transport (§5.3), so the comparison varies the deployment tier and nothing else.
Shape of the fix
Two independent pieces; the first is small.
1. Let the extension take the two tiers separately. Today opts.transport means "both". Either add an optional fast override, or accept { fastTransport?, streamTransport? } with transport kept as the both-tiers shorthand for back-compat. Purely a signature change; no protocol involved.
2. Give the remote path a persistent channel. persistent-exec.ts is transport-agnostic almost everywhere — framing (framing.ts, wrapCommand, nonce matching), producer-side cap, transparent fallback and timeout handling are all generic. It is kubectl-specific in exactly two places:
- the binary name at
:84 — spawnFn('kubectl', …)
buildPersistentKubectlArgs at :12 — exec -i -n <ns> <pod> -- bash
So a container-exec variant (docker exec -i <container> bash, or podman) is a parameterized argv rather than a new protocol, and it declares the same producer-side-cap truncation mechanism the conformance battery already knows.
A relay-mediated persistent channel is the larger alternative: it needs a long-lived shell concept on the wire (sandbox.proto), since Exec is currently one command per RPC. Worth considering only if the local-container variant proves insufficient — it is the option that would also serve a genuinely remote worker.
Suggested acceptance
References
🤖 Generated with Claude Code
Problem
k8sSandboxExtensiondeliberately runs a two-tier transport (extension.ts:30-34):fastTransport—persistentExecInPod, ONE long-livedbashwith nonce-framed commands, serving the small request/response ops (read/write/edit/ls/find).streamTransport— per-call exec, for the ops that needonData/ abort / long-running semantics (bash,grep, user!).But
opts.transportoverrides both (extension.ts:38,:49-51), andrun-leaf.ts:786passes exactly that for a leased gRPC presence record:So on the gRPC path the fast tier does not exist. Every file op becomes a full
SandboxExec.Execround trip through the relay, ending in a freshbash -cin the sandbox. On the kubectl path the same op is one framed line on an already-runningbash.This is a silent asymmetry between transports of the kind ADR-0024 and
transport.ts's conformance battery exist to prevent — the battery pins the output cap and the exec-timeout ceiling across all three implementations, but nothing pins whether the fast tier is available, so a caller above the seam cannot see or control the difference.Why it matters more now
File ops are the highest-frequency tool calls in a coding agent. Under P6 (#244) a single host runs W workers × S multiplexed sessions against K sandbox containers, so per-op process churn concentrates: what is one extra round trip at 1:1 becomes W×S round trips and W×S
bashspawns against K containers.E6/E7 measured per-sandbox duty at 2–8% of leaf wall-clock, and E7 validated mixed-ref converge correctness at 6 concurrent refs on one pod. P6's rungs go well past that, so whether duty holds under this load is an open question rather than a settled one. P6 §5.2 records per-file-op latency and sandbox-container CPU precisely so a sandbox-bound run is not misreported as a harness density limit.
Why this is not P6's to fix
The fix lifts both substrates equally. Folding it into P6 would improve the VM arm and the Knative arm at the same time, while adding transport surface to a slice whose job is to measure the deployment tier. P6 instead stays correct without it by pinning both E9 arms to the relay + gRPC transport (§5.3), so the comparison varies the deployment tier and nothing else.
Shape of the fix
Two independent pieces; the first is small.
1. Let the extension take the two tiers separately. Today
opts.transportmeans "both". Either add an optional fast override, or accept{ fastTransport?, streamTransport? }withtransportkept as the both-tiers shorthand for back-compat. Purely a signature change; no protocol involved.2. Give the remote path a persistent channel.
persistent-exec.tsis transport-agnostic almost everywhere — framing (framing.ts,wrapCommand, nonce matching), producer-side cap, transparent fallback and timeout handling are all generic. It is kubectl-specific in exactly two places::84—spawnFn('kubectl', …)buildPersistentKubectlArgsat:12—exec -i -n <ns> <pod> -- bashSo a container-exec variant (
docker exec -i <container> bash, or podman) is a parameterized argv rather than a new protocol, and it declares the sameproducer-side-captruncation mechanism the conformance battery already knows.A relay-mediated persistent channel is the larger alternative: it needs a long-lived shell concept on the wire (
sandbox.proto), sinceExecis currently one command per RPC. Worth considering only if the local-container variant proves insufficient — it is the option that would also serve a genuinely remote worker.Suggested acceptance
test/conformance.ts) declaringproducer-side-cap.transport.ts:24-32applies totruncated.References
packages/k8s-sandbox/src/extension.ts:30-51,persistent-exec.ts:12,:84,harness/src/run-leaf.ts:786🤖 Generated with Claude Code