Skip to content

feat(cloud-agent-next): wire Vercel sandbox billing into SandboxControl - #6798

Merged
eshurakov merged 3 commits into
eshurakov/vercel-billing-identityfrom
eshurakov/vercel-billing-wiring
Sep 28, 2026
Merged

eshurakov merged 3 commits into
eshurakov/vercel-billing-identityfrom
eshurakov/vercel-billing-wiring

Conversation

@eshurakov

Copy link
Copy Markdown
Contributor

Summary

Second chunk of a two-PR stack (stacked on the identity PR). Wires Vercel sandbox usage billing into
the control-plane Durable Object SandboxControl and turns the path on: a persistent,
generation-fenced billing continuation is armed before the sandbox is created, admission happens
before createSandbox, drains reconcile, and settlement is delivered through the heartbeat tick.

Depends on the base branch of this PR (the identity chunk); review that one first.

What it does

  • Admission before create — for a creating record whose caller owns createCommands,
    runEnsureReady opens the meter interval (or re-acknowledges it) and arms the continuation before
    createSandbox. Definite rejection: enforced blocks with billing_blocked via failCanonicalCreate;
    shadow continues this create with no interval. Uncertain start: enforced releases creating and
    zero-settles the retained generation; shadow keeps the generation and still creates — a meter outage
    never blocks a shadow create.
  • Persistent continuation — one schedule-table entry per open generation, established before
    provider work can remove the creating deadline, marked settlement-due on terminal reconcile and
    removed only on ack or the 60-minute abandon. alarm() re-arms it and, when runtimeDeleted, runs a
    settlement-only path (no getSession, no driveCanonicalStop).
  • Prepare vs deliver — afterDrain / afterCanonicalCommit await local preparation and alarm
    composition; meter delivery happens separately, off the billing and heartbeat queues, so a
    heartbeat-triggered budget stop cannot deadlock.
  • Evidence — create() stores the session createdAt on the generation binding before returning
    the ref; observe()/stop() store createdAt/stoppedAt/abortedAt. An owned 404 is terminal.
  • Provider — create() no longer calls admission; the shared alive/terminal classifier is exported
    and used by both observe() and the billing host.
  • Shared admission correction in metered-billing-lifecycle.ts: accept an unmeasured context only when
    there is no pendingStop and the start ack matches; clear only definite rejections.

Verification

  • Focused unit tests: 11 files, 276 passed.
  • Full cloud-agent-next unit suite: 255 files, 7750 passed, 3 skipped.
  • Worker integration suite: 38 files, 750 passed.
  • pnpm lint 0/0 and pnpm typecheck exit 0 for cloud-agent-next and container-usage-meter.
  • container-usage-meter test:postgres: 15 passed against real Postgres.
  • packages/container-usage unchanged.

Not verified

Live Vercel end-to-end has not been run: it requires an enrolled org (VERCEL_SANDBOX_ORG_IDS) plus
the Vercel runtime artifacts and credentials, and the operator SKU rows. Once enrolled, reconcile a
real session's persisted confirmed_seconds against the Vercel session createdAt → stoppedAt.

Scope

Only the sized presets vercel-small / vercel-large are metered. providerSupportsEnforcedBilling('vercel')
stays false, so Vercel is not chosen as the enforced default provider; the unsized default Vercel
destination stays unmetered and fail-closed under enforcement.

Meter Vercel sandbox allocations through the control Durable Object:
admit an unmeasured interval before createSandbox, arm a durable
continuation, pin the measurement cursor to the create-response
createdAt, and settle through the heartbeat tick after the allocation
stops.

- vercel-provider: remove admission from create(), persist the
  create-response createdAt and terminal timestamps into the generation
  binding, and share the alive/terminal status classifier.
- admission: accept an unmeasured context only without pendingStop and
  with a committed start ack; clear the context only on a definite
  rejection. Gate a fresh create on a predecessor generation's
  settlement; make uncertain admission retryable across the control
  boundary.
- alarm: run the settlement-only path when the runtime is gone, rearm
  the continuation before returning, respect retryNotBefore, reconcile
  orphan continuations, and feed billingDueAt into composeControlAlarmAt.
- meter: clip a reported segment at the interval's receipt time and
  recover a missing interval from the stop context.
@eshurakov
eshurakov force-pushed the eshurakov/vercel-billing-wiring branch from c5edb06 to c58f0ce Compare September 28, 2026 10:04
Comment thread services/cloud-agent-next/src/persistence/SandboxControl.ts
Comment thread services/cloud-agent-next/src/sandbox-control/billing-schedule.ts Outdated
@kilo-code-bot

kilo-code-bot Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

The incremental commit defers the billing continuation while a settlement delivery is in flight and fixes the ensure absent-payload no-clobber; both previously reported findings are resolved on the changed lines.

Files Reviewed (4 files)
  • services/cloud-agent-next/src/persistence/SandboxControl.ts
  • services/cloud-agent-next/src/sandbox-control/billing-schedule.ts
  • services/cloud-agent-next/src/sandbox-control/billing-schedule.test.ts
  • services/cloud-agent-next/src/sandbox-control/vercel-billing-control.test.ts
Previous Review Summary (commit a993b11)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit a993b11)

Status: 2 Issues Found | Recommendation: Address before merge

Executive Summary

The Vercel billing wiring is well covered by tests; the main risk is a tight alarm loop in launchVercelSettlement while a settlement delivery is in flight, plus a latent no-clobber bug in the new BillingScheduleTable.ensure.

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
services/cloud-agent-next/src/persistence/SandboxControl.ts 2462 scheduleAlarm() runs before the in-flight guard, re-arming the expired continuation on every alarm while a settlement delivery is outstanding, producing a tight alarm loop (unbounded if the meter RPC never settles).

SUGGESTION

File Line Issue
services/cloud-agent-next/src/sandbox-control/billing-schedule.ts 180 ensure() returns early when the entry is absent and payload is undefined, so an optional-payload call never arms anything. Latent today.
Files Reviewed (14 files)
  • services/cloud-agent-next/src/container-usage.test.ts
  • services/cloud-agent-next/src/metered-billing-lifecycle.ts - 1 suggestion considered, no issue
  • services/cloud-agent-next/src/persistence/SandboxControl.ts - 1 issue
  • services/cloud-agent-next/src/sandbox-containers/containers-billing.test.ts
  • services/cloud-agent-next/src/sandbox-control/billing-schedule.test.ts
  • services/cloud-agent-next/src/sandbox-control/billing-schedule.ts - 1 issue
  • services/cloud-agent-next/src/sandbox-control/control-effect-port.test.ts
  • services/cloud-agent-next/src/sandbox-control/control-orchestration.test.ts
  • services/cloud-agent-next/src/sandbox-control/control-orchestration.ts
  • services/cloud-agent-next/src/sandbox-control/lifecycle.test.ts
  • services/cloud-agent-next/src/sandbox-control/vercel-billing-control.test.ts
  • services/cloud-agent-next/src/sandbox-control/vercel-provider.test.ts
  • services/cloud-agent-next/src/sandbox-control/vercel-provider.ts
  • services/container-usage-meter/test/postgres.test.ts

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0

Review guidance: REVIEW.md from base branch eshurakov/vercel-billing-identity

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.

2 participants