- Overage stays $1.00 per sandbox hour (ORCA_CLOUD_OVERAGE_CENTS_PER_HOUR) and is now billed by
the minute: a per-minute metered TEST price (unit_amount_decimal = rate / 60 to 12 decimals,
transform_quantity divide_by 60, round up), created the same way by stripe-test-setup.ts.
capSeconds rounds the cap down to whole minutes and spentCents mirrors the invoice, so the
cap math matches Stripe. The meter still counts seconds.
- Opting in re-prices an overage item still on an older price (e.g. $0.75 / h) instead of adding
a second item on the same meter, and removes any extra stale one.
- Alerts at 75%, 90% and 100% of included hours (warnAtPercent).
- New top-level entitlement.suspendRunning: payment grace expired, or at the limit (out of hours
with overage off, or at the spend cap). The start check and usage answers carry it, so the
control plane puts running sandboxes to sleep (snapshot kept, never deleted). Payment fields and
/v1/internal/suspensions stay payment-only, so no data-retention clock starts at a cap.
- Copy (desktop billing panel, plan card, build sheet, phone, CLI): "billed by the minute" after
the rate; at the limit "Running sandboxes go to sleep and new ones can't start. Nothing is
deleted, and your files are safe."
* fix(sandbox-control): unblock dev deploy: apply the build-runner delete-class migration without the binding (step 1 of 2)
* test(sandbox-control): build-runner config test allows the binding-less step of the rollout
- Command-finished events carry the pane's leaf; a run learns its pane while its tab has one,
ignores other panes, and Stop interrupts the run's own pane.
- Workspaces whose setup ran in the environment build (hooks report environmentProvisioned) start
the autoRunAfterSetup action the first time they are opened, once per workspace.
Cloudflare generates no preview URLs for a Worker that implements a Durable Object, so every
--preview-alias upload of orca-sandbox-control-dev 404ed. The runner DO moves to
orca-sandbox-build-runner-dev (wrangler.build-runner.dev.jsonc); the control plane binds it with
script_name and deletes its own class (migration env-build-runner-v2). The runner only keeps the
alarm: each step calls back into the control plane's BuildTicker RPC entrypoint, which runs the same
tickBuild with the control plane's D1, provider and secrets, so nothing is duplicated.
* fix(w8): DS-41 phone polish: grey backdrop behind white groups in light, light amber at 3:1+, access rows read '{device}, last used {date}' with a 'Removes this phone's access' hint
* fix(w8): DS-41 review: white text on light amber badges (onStatusAmber, 5:1), grey backdrop only behind grouped lists (card screens keep white), 'connected {date}' label when a phone was never used
* feat(w8): DS-28 new cloud workspace: pick the repository from this phone's recents and the team's projects ('Other repository…' for any other), and say 'Sleeps after N minutes idle' (new screen, empty Home)
* copy(w8): DS-28 with the picker, the note says 'Pick none for a blank project'
* fix(w8): DS-28 review: explicit 'No repository (blank project)' row, the field stays while typing an unlisted repo, choosing 'Other repository…' again keeps the text
* fix(w8): DS-28 nit: typing in the field selects 'Other repository…'
- nonconcurrent now matches Conductor's run_mode: starting it stops that action in the project's
other workspaces; the project's other actions keep running (W9 finding).
- A VM launcher's ORCA_PORT=3000 is the first workspace's block (persisted, stable across wake), so
its agents, terminals, setup and run actions all agree; later worktrees on the VM get registry
blocks that never overlap it. The daemon no longer hands the host's block to panes without one,
and setup/archive hooks resolve the workspace block before they run.
- local.run-actions: autoRunAfterSetup after a good setup and not after a failed one, Run button,
one trust prompt, Stop -> Stopped, menu Restart replaces the tab, Cmd+Opt+Enter start/restart
- run.nonconcurrent: echo-loop started in B stops A's
- run.ports: ORCA_PORT/ORCA_PORT_COUNT/CONDUCTOR_PORT per terminal, distinct blocks, stable across
an app restart, served locally; cloud half: the action in the VM reachable here via auto-forward
- run.cloud-resume: action back after Sleep/wake and after Restart sandbox
Dry routine run on dev: Copilot CLI answered 'No authentication information found' with
COPILOT_GITHUB_TOKEN=orca-ph-…; it only takes GitHub-shaped tokens (github_pat_/gho_/ghu_; classic
ghp_ refused). The stand-in for that one credential is now github_pat_orcaph_<sandbox>, still
swapped by exact match on api.github.com (Copilot endpoints only) and the Copilot API.
DS-34:
- One billing store (billing-summary-store) replaces the summary, the hook's local state and the
Run-on picker cache; it keeps the last good result through a failed read and still drops answers
from before a team or account switch (DS-17).
- Skeleton while Plan & billing loads.
- '{{count}} sandbox hours left' and '{{count}} sandbox hours over the included amount this month'
as i18n plurals; no bare 'hours'; the unreachable 100% branch is gone.
- Usage warning driven by usageWarningPercent ('Your team has used {{percent}}% of this month's
sandbox hours.'), in Settings, the plans page and the account menu.
- capTooHigh states the maximum ('The spend cap can be at most {{max}}.'), from offers.maxSpendCapCents.
- Plans dialog focuses its heading on open, so Tab reaches the interval toggle before the cards.
- Payment notice uses InlineError; the build card's overage buttons land on the overage section.
- Account menu names the team once and adds the hours-left line.
DS-44: warning tone at 90%+, '$' as an input prefix, neutral paid check, 24px dialog close hit
area, no <p> inside the CheckoutPanel <dl>.
- Agent and worktree come from the terminal orcad verifies from launch
evidence (verifiedCallerTerminalHandle, shared with turn-sender); the
terminalHandle param is gone and a worktree hint is kept only when the
host knows it.
- secrets.* are accepted, and their events sent, only on cloud orcad
(ORCA_IS_CLOUD=1 + ORCA_SANDBOX_ID); already-set never reads orcad's env.
- touchRequest emits secrets.requestUpdated; natural expiry emits
secrets.requestSettled {status: expired}.
- orca-env skips the control plane's reserved names and prefixes, with a
test that reads them from secrets.ts.
- reason drops bidi controls and turns C0/C1 controls into spaces.
Customers keep flat sandbox hours. Each stopped session's activeCpuDurationMs
(from Vercel's stop response or GET session) is stored in a new session_cpu
table (migration 0042), idempotent per session. Recording is best-effort and
never blocks suspend, stop or usage reporting; a 5-minute cron sweep retries
recent sessions still missing CPU. Operator-only GET /v1/internal/fair-use
reports per-account 30-day utilisation sum(active_cpu_ms)/sum(wall_ms*vcpus)
with fairUseFlag above 60%. Nothing reaches usage_outbox, billing or clients.
- Menu items name their action (Run/Run again/Restart/Stop/Show output <name>).
- Running tooltip 'Show <name> output' + '⌥⌘↩ restarts it'; primary label states running/stopping/
last run failed; chevron 'Run actions, N running', min-w-6.
- Polite live region announces '<name> stopped/failed/finished'.
- Shared focus outline; name in text-foreground; hollow dot once a run ended.
- Restart and Stop disabled while Stopping…; exit 0 reads 'Finished'.
- Ports rescan as soon as a run ends; each port has Open in browser · Share… (Share opens the
Ports panel, where forwarding and share links live).
- Fixtures: failed, exited.
- Orca account pane: signed-out and reconnect-required lines name Orca Cloud.
- Every unauthorized failure (lifecycle toast, cloud workspace errors, share links,
cloud secrets) now says "Sign in to Orca again to continue." with a "Sign in" action.
- Team page busy row keeps full contrast: spinner + aria-busy, no opacity dimming.
- destructive-chip text uses a new --destructive-text token (light: 85% mix with
foreground) so it clears AA on its tint.
- TeamDialogHost quietRefresh depends on the stable refresh callbacks.