The product becomes Contrast
Everything became Contrast this week, all at once: a new name, a new brand mark, and a redesigned website. The rebrand rode on top of real product work. You can now comment directly on the design canvas and turn those comments into agent tasks. Underneath, the data layer finished the move it started last week and ended fully off Postgres, onto native SQLite.
Platform
The product is now Contrast
One name now carries everywhere: Contrast. It’s on the homepage, the app chrome, the command-line tool, the documentation, and the sign-in, auth-status, and team-invite pages. A new brand mark, a split half-circle, runs across the mobile app icon, launch screen, sign-in lockup, empty states, and the agent pill, plus the website favicon and email logo. The wordmark was redrawn with tighter geometry. The browser-tab icon now ships with no background tile, so it follows your light or dark preference instead of vanishing against the opposite tab strip. The old mascot and character content are gone from the product, though the friendly Sootbean character stays on as the SootSim agent mascot. None of this touches your data: it’s a naming and visual change, and your accounts, projects, deployments, and files are untouched.
A redesigned website
The marketing site got a full pass to match the new brand: typography refreshed and now set in Figtree, reworked navigation and header, a new isometric graphic replacing the old flat diagram in the deploy section, and the product showcase recentered into a forward-facing landing pose. The developer page was redesigned too, with numbered chapters, corner ticks, more generous spacing, and scroll-reveal animations, and it drops the old per-section color themes for a cleaner look.
Comments and tasks on the design canvas
Comments now live directly on the design canvas. Drop a pin anywhere and it anchors to the specific element it points at, so it stays put even when the layout shifts around it, and everything syncs live so the whole project sees updates as they happen. Pins show up on both the desktop map and the phone touch canvas; tap one to open a shared thread. Approve a comment with Make task and it becomes a durable backlog item rather than a throwaway note, and when an agent picks up a task made from a comment, it waits for your explicit approval before acting. The old floating design panel gives way to a single sidebar rail with Comments and Design tabs, and the kanban backlog carries a collapsed Design comments strip that deep-links back into the canvas.
The canvas itself grew up alongside the comments. On the phone it’s now a full-screen destination with an honest loading state and natural pinch and pan, and it stays warm across tab switches, so you land back on it instantly instead of watching it reload. First-time opens are faster too: it loads the main map up front and the design-system panel only when needed, and it stops repainting in the background once you leave. Panning is bounded now, so the board can’t be flung off-screen and lost.
A rebuilt, rebranded mobile app
The mobile app traded the old mascot for the Contrast brand: a new app icon with a softer textured finish, a dark launch screen that hands off seamlessly to login, and a clean logo lockup. Navigation was rebuilt as a single horizontal pager, projects, a tabbed center, and preview, with a floating bottom tab band in place of the old system tab bar. Tap a tab and its icon tints on the same frame instead of lagging; hidden screens hold their state while paused; and per-screen actions moved into the tab band, so the header stays consistent as you move around. Opening the app now drops you straight into your last active workspace instead of a project picker, first-time users get a welcome starter project, the connecting indicator stops flickering on brief reconnects, and your saved sign-in survives the update.
Push notifications arrived as well, opt-in behind a Settings toggle and shipping in a fresh TestFlight build since they need a native capability. And every loading spinner became a single 3D version of the brand mark: a half-ring and half-disc counter-rotate around a shared axis, collapse to a thin line edge-on, then ease to rest facing you as your project connects.
Live request logs and traffic for your deployed apps
Your deployed apps now show their real request logs and working traffic counts in the production view, both on the web and in the mobile app’s production tab. The logs stream the same activity the platform already records for every deployed app, and traffic numbers resolve correctly now, where before they read the wrong app name and came back empty. On mobile, tapping the production overview opens these live logs directly, so you can watch what your live app is actually serving without leaving the workspace.
Agent builds that no longer die on a broken preview
Several AI build runs had burned their whole budget failing on a web preview that was already broken before any agent started, three runs in a row lost to the same bug. The root cause was in code generation: a generated preview file emitted an invalid JavaScript keyword as a bare name, which made the module impossible to parse, and because a core dependency loads that module, every build’s web preview died at boot with an unexplained error. That’s fixed now. A missing piece that Expo-based apps need was added, and the preview names the exact module that failed instead of showing a blank syntax error. A build run also aborts early if the preview crash-loops before the agent starts, and one broken web preview no longer freezes otherwise-healthy native work. Separately, web deploys kicked off by an agent build had been hanging and timing out because the path that triggers a deploy had no actions wired into it; deploys now launch correctly from every path that starts one.
App Store screenshots: saves fixed and a lighter review grid
Saving an App Store screenshot deck had failed silently for about three weeks. Every save returned an error that nothing surfaced, so teams thought their work was saved when it wasn’t. Saves work again, a clear Not saved warning now appears in the rail when one fails, and an empty save can no longer wipe out a whole team’s decks. The review grid got lighter, too. It had been downloading the full App Store-resolution image for every thumbnail, so a large deck pulled about 16MB; it now loads a small variant sized to how the thumbnail actually shows, cutting that same deck to under a megabyte, roughly 20 times less, while the full-resolution image still backs the focused card and the export. Two more fixes round it out: the screenshot review view opens immediately instead of quietly booting the whole simulator first, and apps that start native-driven animations on launch boot on the screenshots page instead of hanging on a spinner.
Tech
The application database finished its move to native SQLite
Last week, local development moved onto a native Rust host over a single SQLite file. This week the rest followed. The whole application and deploy control plane came off Postgres, and the week ended Postgres-free. Every application and Zero write now routes through a native SQLite service, and callers across the product were converted along with it, auth and billing, iOS publish leases and credentials, deploy and GitHub registries, project and preview paths, SootSim runner jobs and reports, and factory execution leases among them. Deployed Cloudflare apps were switched onto the same service, and the Cloudflare Durable Object deploy path was reworked onto SQLite, its test file shrinking from about 1,080 to 350 lines. The cleanup was substantial: the generated schema dropped about 4,000 lines, roughly 30,000 lines of generated Drizzle migration snapshots were deleted in favor of native SQLite schema generation, and the final Postgres-to-SQLite compiler and its convergence layer, about 1,240 lines together, were removed.
Because this runs against real production rows, data preservation was guarded end to end. Composite keys and legacy account ownership were retrofitted with ALTER-style migrations rather than table replacements, timestamp text is decoded and preserved, affected-row counts are kept so billing atomicity stays correct, and the schema shape refuses to adopt an incompatible SQLite schema rather than corrupt rows. Identity stays deliberately unchanged: the SQLite filename and Zero namespace keep the deployed name even though the brand is now Contrast, so no account, project, or deployment is orphaned.
Generated apps’ preview data backend replaced PGlite with the Orez browser host
The in-browser data backend behind generated apps’ web and native previews was cut over from the PGlite and orez-web WebAssembly stack to the Orez browser host, the same Rust sync-host path production uses. Previews now run a real Zero client that ingests row updates using physical table and column names, with schemas resolved at runtime so mapped-schema operations stop being dropped; browser SQL is prebundled for project workers, and the Postgres parser is kept out of them. The entire old PGlite backend is gone, packages/orez-web at more than 6,000 lines, including its WASM SQL blob and a websocket patch bridge. Getting there meant pinning orez to 0.5.20 for the physical-name row-update fix and on-zero to 0.6.14 for the sync-host adapter and http-pull subpaths.
Deployed apps get a dedicated per-app Rust sync host
Building on last week’s move of Contrast’s own sync onto Rust hosts, generated Cloudflare apps now run their sync on a dedicated per-app Orez Rust host, while push, pull, permissions, and auth stay in the app worker. Registry Orez 0.5.18 supplies resumable snapshots plus delegated-push and root-feed fixes, and the migration was validated against all three production examples. A companion fix keeps preview pushes routed to the owning project’s server, so a preview can’t push into the wrong app’s sync host.
Zero mutators made replay-safe so agents stop dying mid-build
A Zero mutator that threw on a replay conflict could jam an entire sync client into a permanent error state. A task-delete guard replaying against a since-reassigned task did exactly that, killing a factory agent under an 11,800-mutation rejection storm. The fix makes state-race mutators total: props validation is hoisted above the first read, any residual state-race throw is gated behind a server-only branch so client replays settle as no-ops, and user-facing paths still await the authoritative server result, so real rejections still surface. That covers six mutators, including iOS publish attempts, tasks, agents, and kanban. Separately, six custom-named mutators that had bypassed the automatic CRUD permission now authorize before writing, and three new lint rules enforce the shape repo-wide. What you feel is agents and kanban boards that keep running instead of silently dying.
Design overviews render in the cloud instead of crashing phones
Generating the design overview on a phone used to run the full capture pipeline on-device, blow past the WebView memory ceiling at about 2.7GB peak, and crash. Captures now run wherever the build execution lease lives, a desktop tab or a hosted runtime that boots the client headlessly, get saved centrally, and the phone only downloads and renders the finished result. Staleness is tracked as a whole-project hash of file revisions, and a capture is marked in-progress only once a real producer exists, so a free-plan or unattended request no longer strands the view on eternal progress.
The GitHub preview runner: 3,691 lines of bash became a pinned bundle
The runner that drives arbitrary third-party repos for preview and branch builds was a 3,691-line bash script. It’s now a roughly 40-line bootstrap that fetches a version-pinned runner bundle from a new locked contrast-runner workspace, pinned at 0.8.0, and execs node; a fetch failure exits non-zero, so a dead cloud can never green a job. The change landed staged, served inert behind a parity flag first, then cut over. Parity review caught two real boot blockers before that cutover: the runner has to export the agent LLM proxy and the repo directory to its child processes, or every CI run dies at startup. Every browser launch now routes through the sanctioned reaped Chrome-for-Testing path instead of a bash channel invocation.
Cloudflare app boots hardened so they stop wedging deploys
A boot-wedge incident drove more hardening of the path that starts each app’s data backend, extending last week’s resumable-boot work. Orez 0.5.11 split the backend into a root chunk the per-package upload allowlist never attached, so a clean deploy failed on every boot with “No such module”; the allowlist is now a general root glob, so any future chunk split attaches automatically. A terminal boot-failure count that could never clear, leaving CI red after a real fix, is now scoped to the recording build, so a new deploy clears the stale count and retries. Pending and active boots are persisted over alarms and awaited rather than lost, warm probes stop after a terminal failure instead of hammering a dead deploy, and native warmup is bounded to fifteen minutes.
SootSim text layout skips redundant paragraph rebuilds
Text measurement dominated layout cost in load profiles, around 80% of layout CPU, most of it re-shaping the same text at different width constraints while yoga probes several widths per pass and paint only lays out at the final box. Since shaping is constraint-independent, text measurement now reuses its unwrapped single-line result across any wider constraint, and the paint path lays out its cached paragraph again instead of rebuilding it, except where width is genuinely baked in, such as manual single-line ellipsis and nested-text placeholder rects.
SootSim
- Scroll indicators now fade out smoothly instead of occasionally freezing partway through the fade, and the simulated modern iPhone’s rounded-corner frame geometry was refined to match the real device more closely.
- The
sootsimcommand-line tool accepts shorter top-level shortcuts for its runtime commands, so driving the simulator takes less typing. - Paging scroll clamps a fling to a single page from where the gesture started, matching how UIPageViewController and UIScrollView settle, so a hard fling snaps to the next page instead of racing across every page.
- Dragging on the factory floor no longer briefly blanks cached images, closing a flicker now covered by pixel-probe regression tests.
- Render and shell preview workers run their own teardown and acknowledge shutdown, so pooled preview remounts stop leaking orphaned workers; dead sims surface as “sim unreachable”; and trace panels show each step’s real measured duration instead of a clamped window that had printed a 6,983ms launch as 1ms.
- Guest apps that start native-driven animations during mount no longer crash on a cold boot, which the Bluesky demo hit every launch: pending animation drives now publish on the next binding snapshot instead of throwing before the shell is ready.
- The in-app WebView host now mirrors WKWebView’s isolation for hosted pages, with an integration test covering it.
- Apps that asked for the black “Sign in with Apple” button had been drawing it white; the button now renders the color the app requests.
Mobile app
- A polish pass moved body text to Figtree so weights line up on iOS, sized the Apple, GitHub, and email sign-in buttons to match Apple’s native button, lifted plan progress dots above a header blur that had hidden them, corrected hairline borders that were rendering about 50% too heavy, and cleaned up the voice-recording stop control.
- Floating panels and action sheets share a consistent frosted-glass look and stay visible when you close and reopen them, and the picture-in-picture close button is fully solid so it is easy to tap.
- Mobile project orchestration initializes through a properly scoped sync client, and the native demo-seed request now carries the session token the sync socket uses, so demo data actually seeds instead of reaching the server anonymously.
Previews and templates
- Signing into a shared or hosted preview no longer hangs on the splash screen; pipeline-produced previews, meaning pull-request previews, branch builds, and test recordings, stay openable by anyone with the link, while private one-off shares keep their private default.
- Screenshot mode gained an in-app help dialog explaining the camera keys, with the 3D sub-controls carrying their own separate help.
- Apps that start native-driven animations on launch now boot on the organization screenshots page instead of hanging on a spinner, which the 3pc login screen hit every load.
- Built apps keep sign-in and sign-up actions above the on-screen keyboard across every app and game template, and the mobile starter’s settings screen gained a soft gradient-blur header.
- Preview dependency builds now bundle native-seam web dependencies reliably and inline their optimized stylesheets, so previews render with their intended styling.
- Two load-order fixes for the in-browser preview worker’s auth: telemetry constants are prebundled before the worker makes auth requests, and better-auth is pinned into the dependency optimizer so its dynamic import stops hanging, where 8 of 12 routes had been failing.
Auth and accounts
- Sign-in links opened from an email client no longer vanish: only real popup sign-ins are closed now, so email-launched magic links stay open and complete.
- Shared team invite links that anyone can use had stopped working after the first person joined; they now stay valid until you rotate them, with already-accepted joins still honored, and the join page picked up the Contrast wordmark and a solid themed background.
- The demo login treated an already-registered demo user as a fatal error; it now falls through to normal sign-in, matching the other examples.
- Sign-in links and invites brand as either SootSim or Contrast depending on the product they came from, with the sender name following the brand.
- The OAuth cookie domain is derived from the configured auth apex, and a middleware 301 redirects legacy hosts to it with path and query preserved, activating the moment the auth URL flips with no separate deploy.
- Public user profiles are now visible only to project collaborators, enforced through a project-access permission and shipped with a data-preserving migration.
Sync and data
- The Zero wire cookie switched from the snapshot content hash to a persisted per-client-group monotonic counter, so a busy project stops doing a full re-pull on every poll once the native-SQLite cutover routed pulls through http-pull; legacy hash-era cookies migrate past a ceiling set above every genuine legacy value.
- The native SQLite pool now queues clients per namespace while keeping independent transaction IDs, so overlapping transactions such as concurrent team-join stop being rejected, and the coordinator survives hot reload so old and new pool generations can never overlap a transaction.
- Post-commit server effects moved from a hand-rolled async-task array to on-zero’s enqueueTask with its barrier runner, after upgrading the Takeout family to the release that ships it.
- Zero sync now authorizes local browser origins and native user credentials, preview-worker session cookies are kept host-only, and cloud preview sessions stay pinned to their own worker origin so one preview cannot be mistaken for another; local native sync health checks and SQLite admin requests are now authenticated.
Deploy and infrastructure
- Legacy sootbean.com traffic now 301-redirects to contrast.dev with path and query preserved, via a Cloudflare worker on the zone routes, so old external preview-workflow fetches keep resolving through the rename.
- The admin analytics and errors views had been silently empty since the Cloudflare cutover because their keys were baked into the client bundle at build time; they are now injected as runtime vars into the app worker, with a telemetry smoke check that fails until the fix ships.
- The Node-only Apple auth poller had been falsely activating inside Cloudflare workers through nodejs_compat; an explicit worker-global check gates it to Node only, fixing CORS-preflight behavior on workers.
- The production Docker image dropped the node-gyp toolchain of python3, make, and g++, eliminating python3.11-minimal’s post-install, the main qemu-amd64 segfault site that had intermittently killed the Docker prebuild during deploys.
- Cloudflare Durable Object deploys cache schema validation for the object’s lifetime, demo and app worker flips carry forward the secrets that partition production data, and the iOS publish-attempt admin sweep skips unprovisioned projects and stays within the request timeout.
- Apps declare push in package.json and only then does the iOS build enable the Apple Push capability so the provisioning profile carries aps-environment, with a post-sign guard that fails the build if a push opt-in app’s signature is missing it, backed by a shared contrast-notifications package for device registration and deep-linking.
- The 2026-07-15 rename had re-pointed the built mobile app and its over-the-air update worker at new identities; those delivery identities were restored so existing installs keep receiving updates.
Factory and agents
- Anonymous workspaces can now acquire an execution slot and drive a factory build in the browser without being signed in.
- The factory status indicator was reduced to two clear states, cloud and offline, with a new icon.
- Anonymous local factory rooms arbitrate sibling browser tabs with an exclusive Web Lock, so two tabs cannot both drive the same anonymous factory.
- Client runner-assignment writes are now confirmed by the server with an attempt id plus a recovery endpoint, and metadata writers spread the existing row so planning keys survive an assignment instead of being replaced.
- The PR walkthrough recorder now reports a no-surface run, such as a backend-only PR, as a successful no-op instead of a red check, while a genuinely blocked run still fails loudly and reports only observed facts.
- A task auto-pickup scan no longer strands a runner during Zero replay: the submit selects the pickup task at the call site instead of during a rebase replay.
Editor and workspace
- On a cold start, the editor’s type checking and inline errors could permanently give up before ever loading, leaving that session with no type information for its whole life; the 3-second startup timeout was raised to 60 seconds so the editor waits for the language tooling to boot cold, which takes 10 to 30 seconds, validated with one run finishing at 55.6 seconds.
- Selected and active states across the workspace, the agents pane, file tree, design comments, docs menu, logs, network, elements and fonts panes, and settings, now render through one shared set of surface styles, and the orange accent shade is shared between the light and dark schemes instead of being defined twice.
Fixes & polish
Beyond the headline work, 288 fixes and 7 performance improvements landed this week. A few worth calling out. Saving an App Store screenshot deck had failed silently for about three weeks, every save erroring behind an autosave that never checked the result; it works again now, with a visible Not saved warning when one fails. On a cold start, the editor could permanently give up loading type checking and inline errors, leaving that session with no type information at all, fixed by giving the language tooling time to boot. And shared team invite links that anyone can use had stopped working after the first person joined; they now stay valid until you rotate them.