One row of panes, built together
A project is now one row of panes you scroll through, and a place two people can build in at once. Underneath, an agent turn traded its live browser for a warm container dedicated to the project, and creating a project’s database moved inside the object that owns it. The simulator opens on a real phone instead of a black rectangle.
Platform
A project is one row of panes
Editor, board, preview, deploys, agents: side by side, full height, reached by scrolling sideways. The stacked canvas they replace is gone, along with its overview stage and dock, so there is one way around a project instead of two. Task details and build logs open as panes too, not dialogs thrown over everything.
Two ways to arrange it. Stage is free order: a summoned pane sorts to the front, and the one you are working in never jumps out from under you. Sections groups panes into labeled rows, where a drag keeps a pane inside its section. The mode lives in the project’s address, so a reload restores it. Global controls moved to a bar down the left edge, a pane’s own controls collapsed to a capsule until you hover, and a keyboard switcher reaches any pane, even from inside a running preview.
Build a project with someone else
Invite someone and the two of you can drive one project at once, each with a turn running. The project’s cloud build agent answers prompts from any member, so a collaborator arriving mid-build joins the run already going. Invites became reusable share links; paste one into a chat app and you get a real join card with the project’s name and artwork. Everyone sits in one row of faces, whoever is driving first.
The monthly free grant belongs to a person rather than a workspace, so a project spends the combined remaining grants of everyone in it: invite a collaborator and the project can build more. Pricing says it plainly now. Building together is free, and paying is for unattended work in the cloud.
Apps agents build come out with real data and real color
Last week agents began having to prove a screen’s data with a real query. The system runs that proof now instead of the agent writing it up: every data-backed screen names the query it reads, and that query runs against the live app before the screen counts as finished. A screen that renders zeros where the plan wants content fails review.
Color got the same treatment. An accessibility-aware theme builder generates the whole palette instead of hand-picked hex values, so contrast works by construction rather than luck. The agent reads the project’s resolved configuration out of the running preview, so its choices come from that project rather than a generic list. Seeded rows must carry real images.
The simulator opens on a live phone
Opening the simulator used to mean a near-black rectangle for a few seconds. Now it opens on a real captured home screen, status bar and dynamic island and icons, handing off to the live phone with no visible seam because the picture came from the engine’s own canvas. Nothing needed to run an app comes down until you launch one: 442KB less transferred before first paint, 768ms off the median.
That home screen carries a full Messages app matched to an iPhone running iOS 26: floating glass header, grey incoming bubble with its tail curl, liquid-glass compose pill, a real text field on the keyboard seam. It was then pixel-diffed against a real device capture, with bubble tails re-fit by least squares, taking the tail region from 8.4% pixel difference to 0.7% incoming and 8.2% to 0.6% outgoing. What is left disagrees with iOS by under 0.2px.
The mobile app, rebuilt around what agents are doing
The factory view was a photograph of a floor covered in agent chips that never said what an agent was doing or for how long, and at real density they piled onto each other. Now it is a plain list, each row naming the work and the elapsed time. The run control flips the instant you tap it and holds until the real state agrees, and agent rows go quiet when a project pauses.
Task detail gained fixed Backlog and Delete actions, so acting on a task no longer means scrolling past its description. The design map opens with a loading camera framing the entry screen and zooming out once its picture records. Captured screens replay from the text as it was actually laid out, so words break where they really broke. The agent picker is a swipeable strip of pills in the split seam: horizontal swipes scroll, taps select, only vertical drags resize.
Tech
Agent turns run in a warm container, with no browser involved
Main chat needed a live browser session to answer, so the first message paid for waking one up. Turns now run in a container dedicated to the project, warmed the moment you open it. Getting there meant a turn with no DOM: the sync client learned to connect headlessly with a real auth identity, reaching synced state in 2.4s, and the main chat tool graph was cut free of its router, stage and UI edges. on-zero 0.9.4 wires refreshAuth through headless connect, so a long-lived host recovers from token expiry instead of answering 401 forever.
One container per project, never a pool: the sync client publishes exactly one active instance per isolate, so pooled projects would answer each other’s threads. Turns are FIFO per project with a bounded timeout, and a turn resolves only once its queued sync mutations have drained.
Provisioning a project’s database moved inside the object that owns it
Creating a project replays the full migration history into a fresh namespace, and the app worker drove that one RPC per statement across workers: roughly a thousand sequential round trips, measured on production at 35.9s of wall time against 2.8s of Durable Object CPU, 92% idle. It is one RPC inside the Durable Object now, on orez 0.9.0’s applicationSqlLocalClient, same module, same order. Concurrent provisioners coalesce there too, since the object is the only place that sees every racer regardless of isolate; one namespace had been running nine overlapping provisioning sessions across 80.6 seconds.
The schema-ready marker lived in memory, so every eviction re-ran the full reconcile on the next request, bounded by an inherited 5s default across 3 attempts against measured first-time reconciles of 4.4s, 6.1s and 13.8s. The marker is durable now, cleared before each reconcile rather than written optimistically, the budget is 30s across two attempts, and the schema-not-ready 503 advertises retry-after 10 rather than 2, cutting four fifths of the reconnect churn. The ledger reconciles against sqlite_master before it is trusted, so a run killed mid-transaction repairs itself.
Streaming per row and per field, and a phone that pulls only what it draws
Assistant text streamed through two global emitters that each held a whole map and re-emitted with a comparator hardcoded to return false, so every token of every agent woke every mounted chat. It rides orez’s per-row field overlay now, subscribing per row and per field, so a token for one message can only wake the components that asked for it. The field spec lives in an import-free module the app and the generated worker share, because a host that does not know a field refuses it, which looks exactly like streaming that silently stopped.
A production replay found the phone pulling 123 agent-event rows and 74KB, not one of them a type any mobile surface renders, plus 142 file rows and 56KB where the document surfaces wanted seven. Narrowing the queries bought nothing on a cold sync: native still talked to the app’s own mount, whose cache-miss path snapshots every visible row and ignores the desired queries it acknowledges. Native points at the query-aware Rust sync host now, which forwards the Authorization header back to the app for identity and actually runs the client’s queries.
Serving our own brotli instead of Cloudflare’s
Cloudflare compresses responses itself at roughly brotli quality 4 and never looks at a stored .br sibling, so anything precompressed at quality 11 was thrown away. Assets are stored compressed with the encoding declared, and our own bytes go through untouched. gzip and identity clients are unaffected, since Pages decompresses a declared at-rest asset for them, and all four bodies decode to a single sha256.
The boot was also fetching its emoji-metrics font twice. Three render hosts each built the prefetch list on their own and one passed raw URLs, so the compositor worker’s buffer map missed the bundled key and fetched the font itself. Chrome workers do not share the HTTP cache for in-flight requests, so that was a second full 49,799-byte transfer landing about 500ms into the home entrance animation.
A daily spend ceiling Cloudflare does not provide
Cloudflare enforces no account-level spend cap: usage notifications only send email, and the enforcing budgets it shipped in June cover AI Gateway alone. So we built one. A spend model prices every billable dimension across the account rather than Durable Object rows alone, and a containment gate runs on a schedule beside the existing $200/day rows-written kill. Containment does not refuse control-plane writes by default, on purpose: 93.2% of measured write spend belongs to a single production namespace, so arming it would be a production write outage.
Idle activity was cut alongside it: stale sync ingress retired, scheduled polling reduced, CI stopped touching Cloudflare when nothing needs it, and the monitor stopped waking production to observe it.
Workspace and design
- Elevation collapsed to one four-level scale, and dark mode stopped inheriting light-background shadows at 2.5 to 4x alpha.
- Hover was a fully opaque palette step; at half alpha one token works over page, card and crosshatch.
- Selection reads the same across nine surfaces: rows inset and rounded, the selected one lifted, accent bars gone.
- The brand mark hung 47px left of center in the app icon; every square use is centered now.
- The duotone icons became a cleaner regular set, with filled variants for active controls.
- The mascot animates through expressions, reacts while agents work, and its asymmetric eyes track your cursor.
- Picking a document opens it to the right of the list instead of replacing it.
- Panes animate into new positions, resize from any seam, and hold still while the layout settles.
- Board cards lead with the task title; id, difficulty and state drop to a meta row.
- Tool-call failures fold to six lines behind show-more, while the model still gets the full diagnostic.
- Escape leaves the composer, tooltips clean up over a preview, and the quick switcher always closes on activate.
- Sideways scrolling on a row of cards in chat moves that row, not the workspace behind it.
- Moving the UI primitives into a package let the Tamagui compiler see them: 335 more JSX sites flattened across 275 files.
- Two textures had never rendered on any host, real device included: Tamagui’s Image drops style props beyond src, width, height and objectFit.
AI agents
- Big work groups under a parent task with a progress bar and a dependency-ordered timeline, windowed to six rows.
- An idle agent reviews the board against what the app should be, splits a crowded backlog, and runs a design pass.
- Agents can replace a whole file in one write when they already hold its contents.
- A reply that hits an error retries itself, and a conversation always settles into a finished state.
- The naming question no longer holds a new app idle: the brief goes to the build first.
- Touch a preview and it stays yours until you hand it back, while agents keep building and screens you head toward wake early.
- Pausing and resuming a build is one lifecycle, and a collaborator can fund a run.
- A chat session binds to the project its tool allowlist came from, so switching projects cannot ask for tools that were never built.
- Models without vision get one tool that captures the preview and asks the vision model a single focused question.
- Skill routing became one typed capability graph: a spec declares capabilities, one traversal scopes skills, launch guards and validation.
- All 21 skill sources carry their exact predicate now instead of applying to every build, and finance, travel and pet priming was neutralized.
- Agents gained a source-backed knowledge base for Contrast, live reference lookups, and five workflow skills pulled in on demand.
- Worker and designer sessions went from 117 tools at ~32k tokens to 88 at ~22k; planner runs 69 at ~16k.
- The full skill catalogue is exposed before any agent edits anything, and planning is gated on having read the relevant ones.
The agent factory
- Screens already dispatched in parallel; they now go out alongside the shared data shape and navigation shell rather than behind them.
- A new interactive grader boots the app and uses it: crossing auth, clicking real controls, checking whether the primary action saves.
- QA held one slot per project, starting two of five passes and starving one for 4m33s; passes now run together when their files are disjoint.
- Tasks auto-merge at every difficulty once they clear the mechanical bar, with difficulty selecting post-merge verification depth.
- The only pre-merge reviewer left checks for a diff that deletes or renames source outside the task’s own files.
- Merges land while the assembled QA pass runs, and hitting the ceiling gives it a final turn to flush its findings.
- Native screens are captured and judged as pixels in paired light and dark shots, with the judge told which theme it sees.
- Capture probes the surface’s primary action, so a reviewer judges the flow a button opens rather than the last screen visited.
- A 478-line grammar that parsed the spec’s prose for which screens an app needed is gone; projects carry typed attributes as data.
- Evaluation targets became a declaration separate from entry surfaces, after one wave shipped three screens with two marked undemonstrated.
- Query proofs execute mechanically against a minted tenant session, and an unregistered query name is refused with the real ones named.
- A proof the system cannot run records a platform outage instead of blaming an agent that wrote correct code.
- The orchestrator stopped polling: the observer reacts to events, QA triggers on merges, and the pair runner shrank to a third.
- An approved task has its branch prepared and dispatched automatically, and the scheduler explains what it is waiting on.
- Ownership and receipt gates that deadlocked legitimate work now warn and report instead of refusing the write.
- Only the orchestrator compacted context, leaving hands-on workers at 120-160k input tokens per call; they compact at 100k to 50k now.
- The centerpiece screen was the one held back by identity and theme dependencies; it starts beside its siblings now.
- Templates declare their initial product surfaces and the steps to drive them, so a new app must have driveable primary routes.
- A campaign brief lets one operator run several consecutive build waves, each round’s fix list generated from the analysis report.
- Guidance had agents putting accessibilityLabel on every input, which leaks to the DOM on web; shared files use aria-label.
- A new audit measures every element where a user sees it and samples occlusion by paint order, catching widows a screenshot hides.
- The agent pool ceiling moved from six to eight, and an eight-agent run was accepted in production.
- A cold machine reaches project redirect, durable graph, runtime-ready and both preview gates in about 20 seconds, against 6 warm.
SootSim
- Skia apps run: real paragraph text rendering, upstream-matching object disposal, and the Skia.Data and Skia.Image namespaces.
- One’s native toolbars draw through the real components, SF Symbols included, and keep painting inside stacked sheets and menus.
- Wallet passes, banknotes, charts, bells, stars, calendars and bookmarks draw as real glyphs, and near-miss names land right.
- Swipe-back uses iOS’s own formula, translation plus velocity times 0.3 against half the screen, with UIKit’s smoothed estimator.
- A new screens debug channel names each rejection and prints the arithmetic behind it, and debug flags forward over the shell protocol.
- Layout inspection reports border colour with a real width and can narrow to particular elements at full detail.
- Inspection answers a screen’s canonical route and its weakest text-contrast ratio measured from rendered pixels.
- A tap refuses to fire when another target covers its hit point, so nothing reports success on a button behind a sheet.
- Yoga throws on any non-numeric dimension and the catch above abandoned the whole pass; one calc() cost a run 105 aborted layouts.
- Captures encode as WebP q0.8 at 2x, a 1206x2622 frame going 207KB to 27KB, while copy, download and diffing keep lossless PNG.
- Swap-chain buffers retain the frame they last showed, and repaints clipped to a fractional damage rect left a one-pixel flickering column.
- A WebView that unmounts and immediately remounts keeps its handle.
- The host fetch proxy pipes through Node streams instead of Bun’s fetch: large guest requests stream with no timeout and no sniffing.
- Importing expo/fetch used to throw at module load; the compat layer serves upstream’s own web fetch for that specifier.
- Requests from an app behind the preview proxy keep their method, so a POST arrives as sent instead of collapsing to GET.
- The compat package’s 977-line stub registry became a typed native-seam loader validated at runtime, net 500 lines smaller.
- Implicit-text accessibility rules combine the base heuristic with upstream iOS Text.js, shared by overlay, worker sync and tree dump.
- The status bar’s pixel readback was costing frames; sampling now avoids the synchronous stall.
- Rail flyouts share one material, close on Escape, and wait 90ms before switching siblings; going home fires on the click itself.
- SootSim site repositories paint from cache immediately, and the dashboard reads through sync in place of ~1,800 lines of API routes.
Previews and templates
- Every starter template serves its native preview through the same path, with no per-template wiring.
- Light and dark switching applies to every mounted preview at once, and an embedded simulator inherits the host page’s appearance.
- Apps built from the templates can sign in inside a preview despite its different address, while unrelated origins are refused.
- A freshly mounted preview absorbs the reload that used to arrive ~500ms in, so the runtime you look at is the one that started.
- Booting a hosted runtime goes through a lean single-page route rather than the app shell’s full route tree.
- An agent driving an app keeps one preview session for the whole drive, and readiness means stable rather than “something painted”.
- Web preview screenshots go through the browser already driving the runtime and refuse an out-of-viewport target instead of returning a blank.
- Lists built with LegendList resolve their native entry points during preview bundling.
- The shared template Dialog dropped a browser-only key listener, and overlays no longer fire twice on a tap during the close animation.
- Templates and game examples ship designed app icons, one grey family and stroke weight across the set.
- A new flight-tracker demo: flights list, detail with native bottom sheet and toolbar, forecast, context menus, nested sheets, passport screen.
- Todo rows open on tap, with only the check well toggling completion.
- The native preview pane and the marketing site share one phone frame, so a preview never jumps swapping placeholder for live app.
Mobile
- Tapping send starts the build immediately instead of waiting a sync round trip, cutting 3.4s; status checks went 1.5-2s to 186ms.
- Streaming replies hold together across the handoff from your local message to the real server turn.
- Design-map pinch-zoom re-anchors when you add or lift a finger, and one failed image no longer blanks the whole map.
- An Inspect and Fit rail lets you tap any element and send its snapshot to the project’s agent.
- Flick a preview up or down to tuck it aside, direction deciding the corner; minimized previews dock clear of the tab band.
- Ask for a project icon from your phone and the work streams into the thread while the designer runs.
- The project grid asks for all its icons in one go, so a slow first sync no longer cancels the request every time a row lands.
- A refused chat send gives you a buzz and a toast, and the brandmark spinner uses one timing everywhere.
- Composer controls, the speech stop button and the plan board got real touch targets, expanded through hit slop so nothing moved.
- Preview cards across every tab share one geometry that contains and top-aligns rather than cropping.
- Plan progress floats as collapsible bottom glass, and the members row appears in mobile chat.
- Mobile onboarding was simplified and its hero headline moved onto the standard type scale.
- An incomplete capture pass publishes failed rows instead of being stamped ready with a fraction of its screens.
Deploy and infrastructure
- The live production link has a copy button next to it after a deploy.
- A local deployment gets its own certificate-valid HTTPS origin, announced on your network, so a simulator or real phone can open it.
- Resolving a branch pulls its current state into the merge in progress and keeps conflict repairs you already made.
- Deploy cards share one layout for web and iOS, and the iOS section moves between tabs with arrow keys.
- A Cloudflare deploy waits for apex assets to actually serve before reporting success.
- A deployed app worker reconciles its application schema before serving any runtime route.
- Deploy-time reconciliation runs on one shared retry policy, stopping when a namespace’s write circuit closes while reads keep working.
- The homegrown Cloudflare deploy package was deleted for published Orez Lite workers and the data-worker factory: ~8,800 lines out, SQLite-only.
- Wrangler’s registry credentials last 15 minutes; dependencies now shard into size-balanced layers and the uploader resumes with fresh ones.
- An MDX compile stack that cannot run on workerd was pruned from the app worker, freeing 505,906 gzipped bytes.
- Production-equivalent builds measure 10,284,328 bytes, 201,432 under Cloudflare’s cap, with the check now measuring the full upload.
- A new store keeps an AES-256-GCM deploy environment in Workers KV, so a machine can read back what it deploys with.
- Marketing pages stopped shipping the in-browser project server and its SQLite engine, which manual vendor chunking had pulled out of its async chunk.
- A tab open across a deploy that asks for a chunk from the superseded build reloads onto the new build directly.
- Ownership backfill migrations bring account ownership onto older rows and replay incomplete groups without re-running what succeeded.
Sync and data
- Soot and every template dropped a custom pull and push layer for the engine’s native hosts; it wrote on every pull, so an idle app with no changes still generated writes.
- The sync stack runs on the Orez Lite native host instead of a checked-in 3.3 MB binary, pinned at 0.10.5 everywhere.
- Every collection query that syncs to the client must declare a bounded window, enforced by a lint rule; 18 modules picked up limits.
- Agent reads say when they are truncated: a batch caps at 60 children and hitting the sentinel row is a hard error.
- Two fixes took 21 Durable Object sessions off a seven-icon grid: token verification runs first, and expiry quantizes so unchanged bytes keep their URL.
- The team dashboard’s serial chain of lookups collapsed into one transaction.
- A build request from chat travels as a durable synced record and routes to the orchestrator that actually holds the execution claim.
- Home-surface project queries wait for the selected namespace to mount, so a query lands in the database it was written for.
- The durable-operation sweep skips a namespace with a damaged schema instead of abandoning the pass, and stopped re-paging the same 100 projects per tick.
- Server-side pushes stopped being copied into the zero-http source journal.
- A write-amplification alarm compares billable to logical rows: healthy 24.3x, a regression ~1,450x, alarm at 200x behind a 20-row floor.
- The mutation rate limiter now covers project namespaces, so one project cannot burn shared write budget.
Accounts, billing and the CLI
- A first signup lands you in Contrast immediately with the workspace prepared behind you; verification had measured 24.7s, 44s and 82s.
- Apple sign-in works in the browser, sharing the same login dialog, and sign-in links are kept out of server logs.
- The first time you enter a workspace the mascot offers a walkthrough, with a plain yes or no in the speech bubble.
- Upgrading adds a one-time prepaid credit the first time you subscribe: $10 on Pro, $25 on Pro+.
- Hosting draws on the same usage balance as everything else, so one number reads across pricing, billing, the domain dialog and the docs.
- Pro+ got a larger allowance, and its build hours pool across the account rather than per member.
- How much a project can write through sync scales with your plan rather than one flat ceiling.
- Billing screens read live account state, and the plan and seats area opens about three and a half seconds sooner.
- A new drive mode puts owner chat and build decisions in the terminal: steer the whole squad, hand them images, speak on the thread.
contrast ide opentakes login and project flags,ide closeshuts down the tabs your session started,ide shotcaptures the workspace or one pane.- CLI tools say plainly when you are not driving: reads still run, and a write is refused by name with the command that fixes it.
dev shottakes repeatable--tapand--wait-forsteps by test id or text, so it can drive any repo’s auth flow before it shoots.- A project’s build setup lives in a config file read as data, so a mistake names the key and the line.
- Project context is gathered while a project opens, and the conversation you had before it existed follows you into it.
Website
- The front page opens on a field of dots that ripples as you move across it, drawn live and fully formed on the first paint.
- The graphics explaining what Contrast does were redrawn, deployment shows the logos your app can land on, nav, pricing and docs type got tighter, and popovers fade out visibly instead of vanishing.
- Two counter-drifting rails of quotes sit above the apps section on their own clock, with reduced motion swapping in a rail you scroll by hand.
- Sections got per-section control over how strongly they hold on scroll, with hold time separated from height.
- On a phone the home page no longer throws a sheet at you on arrival, and the editor preview phones never showed no longer loads.
- Moving between pages keeps the header and page frame in place, and the hero animation starts on navigation.
- App Store slides became a real editor: a font picker rendering every family in its own face, six uniform text controls, and copy blocks that drag anywhere and stick through export.
- The 3D device stage picked up AgX tone mapping, full-resolution bokeh, a rebuilt room whose key light actually grounds the phone, and numeric framing with no readback.
- The launch film’s opening act is real footage of a Messages session recorded in the simulator, its timing read back out of the pixels.
Fixes & polish
Beyond the headline work, 1028 fixes and 62 performance improvements landed this week. Three are worth calling out. If the sync connection drops or the server is busy, a small reconnecting status appears, with a Reload button if you want to force it; the connection comes back on its own and you keep your place, your scroll position and your in-flight edits. A freshly mounted preview absorbs the reload that used to arrive about half a second in, while dependencies were still being evaluated, so the runtime you look at is the one that started. And resolving a branch pulls that branch’s current state into the merge in progress, keeping the conflict repairs you already made.