v0.3.1 (2026-06-25)
v0.3.1 is an architecture-consolidation release. Where
v0.3.0 re-architected server-side execution into a
per-run-Job pipeline (backend run queue → dispatcher → driver), it left the
CLI and the Tauri desktop app
running test cases locally, through an in-process core runner backed by a host
Docker/Podman. v0.3.1 removes that local execution path entirely: the CLI and the
desktop app are now thin enqueue + watch clients of the backend run queue —
exactly like the web console — so there is now a
single way a run executes everywhere. The only remaining runner is the
driver.
This is still pre-1.0 software intended primarily for project developers. v0.3.1 trades a capability — launching a run on your own machine with nothing but a container runtime — for a simpler, uniform topology: one execution path, one set of manifests, no second runner to maintain.
Breaking changes
Section titled “Breaking changes”The CLI and desktop app no longer execute runs locally
Section titled “The CLI and desktop app no longer execute runs locally”tcab and the Tauri app used to drive a run through the
core on the host, against a local Docker/Podman.
That path is gone. Both now POST the run to the backend’s /jobs queue, a
dispatcher claims it and creates a per-run
Kubernetes Job running the driver, and the
launcher watches the run over GET /jobs/{id}/live. The practical consequences:
- Neither needs a container runtime anymore. What they need instead is a
reachable backend (
TCAB_BACKEND_URL) and a logged-in account. For local development this means the k3d service stack must be up —tcab runand the desktop app target it just as the web console does (see Running and thedeployments/local/Makefileflow). tcab runis now enqueue + watch. It prints the queued job id, streams the live event stream until the run finishes, and reads the produced run record back to print its summary.--auth-modewas added;--out-diris now optional (the backend holds the artifacts — passing it only writes the fetched record JSON locally).- Removed flags and variables.
--work-dir/TCAB_WORK_DIRand--orchestrator-dirare gone fromtcab run; there is no host staging directory to point at and no local orchestrator-dir override (the run executes in the cluster). The CLI/desktop runner-side overrides (TCAB_CONTAINER_RUNTIME,TCAB_WORK_DIR, and the publishing release credentials) no longer apply to these clients. pushis a read-back confirmation. A backend-driven run’s record and artifacts are released by the driver during the run, so by the time an operator sees a produced run it is already stored.tcab push <run-id>…reads each run back by run id and reports whether the backend holds it and where its source and playable build resolve.reviewandpublishlikewise operate by backend run id. (This mirrors the web console, whosepushis a near no-op.)
The local-only commands — seed, prompt, validate, catalog, harnesses,
orchestrators, and the account commands login / register / logout — are
unchanged.
Removed desktop URI schemes
Section titled “Removed desktop URI schemes”The Tauri app’s local execution commands and its tcab-proof://, tcab-asset://,
and playable-build URI schemes were removed. A run’s loadable media — proof
artifacts and an asset-generation run’s
images and action log — now loads over HTTP from the
artifact service, exactly as in the browser
(the backend reports its public URL via GET /config). The desktop CSP was opened
to reach the backend, auth, and artifact origins.
Features
Section titled “Features”Unified HTTP transport for both consoles
Section titled “Unified HTTP transport for both consoles”The desktop UI now uses the same HTTP transport as the web console, promoted
out of apps/web into the UI library and exported as
@test-cabinet/ui/transport. The two consoles are now the same console over the
same BackendClient, differing only in delivery (a desktop binary vs. a browser
bundle).
A shared Rust job-queue client
Section titled “A shared Rust job-queue client”A shared job-queue client (launch_run / job_status / list_active_jobs /
watch_job / subscribe_notifications) and its wire types were added to the
core, so the CLI and any other Rust client enqueue and watch runs through one
contract.
Retained
Section titled “Retained”The desktop’s local adversarial arena
Section titled “The desktop’s local adversarial arena”The one thing the desktop app still runs locally, in-process is the
adversarial arena — quick matches and
tournaments are CPU-bound wasm the desktop plays itself — and its
tcab-tournament:// scheme, which serves a tournament’s host-produced replay
media, is kept. Everything else the desktop does is enqueue + watch + report over
HTTP. See Arena.
Playable builds now load in the console iframe
Section titled “Playable builds now load in the console iframe”The standalone artifact service was missing the
trailing-slash /runs/{id}/build/ route — the exact URL the driver emits as a
run’s playable-build link (it doubles as the build’s <base href>) and the one the
console loads into its iframe. The worker served it, but the standalone service had
only carried over /runs/{id}/build and /runs/{id}/build/{*path}, and axum’s
{*path} capture rejects an empty path, so the build 404’d despite uploading fine.
The route now maps to the build root.
Completed adversarial runs now appear in the arena
Section titled “Completed adversarial runs now appear in the arena”A backend-driven adversarial run’s controller
wasm is mirrored into the backend store at finalize so the run is pittable in the
arena (Quick Match / tournaments) without being
published. That upload was silently failing: the backend’s run-media upload routes
inherited Axum’s default 2 MiB request-body limit, but a full-length proof
replay serializes to tens of MiB (a time-limit Pac-Man match is ~20 MiB), so the
canonical replay’s POST was rejected with 413 — and because the controller was
uploaded after the replays, that rejection aborted the upload before the
controller ever reached the store. The run completed and produced its replays, but
never showed up alongside the baselines in the arena.
The run-media and tournament-replay upload routes now accept a generous body size, and the controller wasm is uploaded ahead of the replays in both the driver and the CLI push, so a run’s arena visibility no longer depends on its (much larger) replays uploading first. Runs produced before this fix need to be re-run to mirror their controller into the store.