Part 1 of #3275 — see its Phase 1 section for the full implementation-ready spec. Can start now (no dependency).
Change (packages/runtime/src/sandbox/quickjs-runner.ts): make the resolveTimeout() budget a VM-active-time (CPU) budget, and add a separate wall-clock ceiling (wallCeilingMs, default 30s) as the stuck-host backstop. Slice-stopwatch every VM entry (evalCode / evalCodeAsync / executePendingJobs); idle host waits + nested-hook execution stay uncharged. Interrupt handler: (cpuMs + currentSlice) > cpuBudget || now > wallDeadline. Distinct errors — exceeded CPU budget of Xms vs exceeded wall-clock ceiling of Yms while awaiting host calls — and map the mid-slice interrupt error to the clean budget message.
Why: the wall-clock budget conflated "tolerate a slow host" with "kill a runaway script", and billed nested hooks to the parent — the #3259 flake. See #3275 §Why.
Acceptance criteria
Decide first (§1.5): wall ceiling as constructor option only, or also OS_SANDBOX_WALL_CEILING_MS.
Separate small PR after soak: drop OS_SANDBOX_HOOK_TIMEOUT_MS=10000 from .github/workflows/ci.yml.
Refs: #3275 (parent) · #3259 (flake) · #3270 (mitigation).
🤖 Generated with Claude Code
Part 1 of #3275 — see its Phase 1 section for the full implementation-ready spec. Can start now (no dependency).
Change (
packages/runtime/src/sandbox/quickjs-runner.ts): make theresolveTimeout()budget a VM-active-time (CPU) budget, and add a separate wall-clock ceiling (wallCeilingMs, default 30s) as the stuck-host backstop. Slice-stopwatch every VM entry (evalCode/evalCodeAsync/executePendingJobs); idle host waits + nested-hook execution stay uncharged. Interrupt handler:(cpuMs + currentSlice) > cpuBudget || now > wallDeadline. Distinct errors —exceeded CPU budget of Xmsvsexceeded wall-clock ceiling of Yms while awaiting host calls— and map the mid-slice interrupt error to the clean budget message.Why: the wall-clock budget conflated "tolerate a slow host" with "kill a runaway script", and billed nested hooks to the parent — the #3259 flake. See #3275 §Why.
Acceptance criteria
while(true){}CPU-burn asserting/CPU budget of Nms/; add the wall-ceiling test, the Flaky:nested-write.integration.test.tssandbox hook exceeds the 250ms deadline on CI (Test Core) #3259 root-cause guard (idle host time not charged → resolves), and a nested-hook-charged-to-itself test.nested-write*.integration.test.tsdrop the explicithookTimeoutMs: 10_000(pass at stock 250 ms).@objectstack/runtimeminor — semantics change, FROM→TO), docs (environment-variables.mdx: CPU-time wording;OS_SANDBOX_WALL_CEILING_MSif adopted), ADR-0102.src/sandbox/suite green.Decide first (§1.5): wall ceiling as constructor option only, or also
OS_SANDBOX_WALL_CEILING_MS.Separate small PR after soak: drop
OS_SANDBOX_HOOK_TIMEOUT_MS=10000from.github/workflows/ci.yml.Refs: #3275 (parent) · #3259 (flake) · #3270 (mitigation).
🤖 Generated with Claude Code