How a build flows
You type a prompt and press send. Within a couple of seconds something answers you in words, and within half a minute an app paints. The agent keeps working while you look: it reads what you linked, waits for the draft to land, then makes the app real, and ends by telling you in one sentence who can see and change what, and asking which fun feature to add next. This page is the map of what runs in between, drawn from the code that runs it.
While work is active, the animated face, elapsed time, and current status sit together on their own final line beneath the conversation and build details. The timer appears once the task starts doing work and includes its initial wait.
You can close the Vibes menu while a build runs and keep using your app. A small status line slides out to the left of the Vibes switch, showing the build phase and elapsed time. It tucks back beneath the switch when you open the menu or the build settles. With reduced motion enabled, it appears and disappears without sliding.
One thing is talking to you: the root session. There is one per conversation, it lives beside your app on the server, and it owns everything in the chat: answering a question, making a change, and deciding which of those your message called for. It has tools. Some finish at once. Some, like the builder and the research worker, take a while and run in their own bounded context; their results land back in the same conversation when they are done. The builder never talks to you. Its stream is what repaints your preview.
This page describes the target flow, the one the platform is being built toward. Parts of it already run in production and parts are still being built; the ledger at the end says, box by box, which is which. In this flow there is one session and one build flow: no separate first-turn mode, no discuss or work mode, no wizard, no recovery persona, and no classifier in front of the conversation. A greeting does not use up the ability to make an app, and file existence is input data, not another mode.
The same shape from the middle
Every message after the first re-enters the same session at step ①, and the root does one of a few things with it. Both authoring tools stay available throughout; "build for shape, edit what you know" is a heuristic, not a rule.
- A change to the shape of the app — a new screen, a rework, a redesign —
goes to the builder:
codegen_buildreturns a handle, the new screens stream into the preview, and the result lands back in the conversation. - A change the root already knows how to write — a server file, a rule about who can see what, a fetched fact, a named person, an hour, a small visible change — is made directly: read the file, change the lines, validate, commit a version. Seconds, and the preview swaps per commit.
- Both at once when an ask is both. The direct edit does not wait for the build unless it touches the same file.
- A change to how the app looks — colour, font, spacing, mood — is a
saved setting served over the app's code, so
set_app_styleapplies it at once with no build. - A question about the app is answered from the app's own files, or from a delegated research worker when the answer lives on a page you linked.
- A change asked while a conflicting build is running is queued as a handle that runs after it. Independent work carries on. The root tells you it is queued, and tells you when it has landed.
- Work across several of your apps goes through
vibe_context: the root focuses the connected apps, and every edit or build is bound to one write target at one revision, with other apps only as labelled reference.
The CLI's vibes-diy generate takes this same path: it opens a chat, the root
names and builds the app, and the CLI follows that conversation — so everything
on this page describes a generate too. Only edit --direct still goes
straight to the builder with no session.
The rules the session keeps
- One durable session per conversation. A new message, a tool result or an idle event returns control to the same session. It survives the socket and the request; a restarted server resumes from persisted state. The tab is not the session.
- Tools return handles at once and results later. The build is a slow tool, not a special case; fast tools resolve in the same round.
- The root decides at every landing. Draft landed: show it, and ask one question only if the answer changes what gets built. Everything in hand: make it real. Last commit landed: wrap up.
- Blinded draft, always fast follow. The first draft is blinded to
access-rule authoring so it can show the app quickly. The root then always
follows, inside the same session and without waiting for another message,
with a public-world-capable
access.js, the UI enhancements those rules require, and anybackend.jschanges. Public-world-capable means the rules account for untrusted visitors and enforce record permissions on the server. - The first completion is a sentence about security. When that first
fast follow lands, the root states the actual security model in one
sentence, then asks about enjoyable new features. The root owns the
question and the
▸options. - Queued is true. A conflicting edit runs after the build it conflicts with; the runtime serializes conflicting writes and lets independent work continue.
- Done means a result arrived. Acceptance, staged source, a save, a release and observed behaviour are distinct outcomes, and the root claims only the one it has.
- Research is delegated at the root's discretion. It can read a source directly or hand a question to a research worker that returns concise sourced findings and each source's status: read, unread, refused or unavailable. Partial and failed research are visible outcomes.
- Two authoring tools, one judgment, one commit path. Both obey the same validation and commit rules. Authorization, app scope, revision conflicts, transforms, lint, the access-function dry run, network policy and auto-publish are enforced at commit, never in the prompt.
- Guides load on demand. The backend and access guides enter the context on the first relevant file operation. A sysadmin guide exists for imports, releases, schedules and admission, loaded only when the task is one of those; loading a guide grants no authority.
- The root has its own model setting. It defaults to Opus and can be switched to another supported model. The builder, the research worker and the style mapper run on the existing codegen setting, defaulting to Sonnet. Direct edits are authored by the root's own model with no extra call.
- Admission is administration, not app code. "Anyone with the link"
and "only people I approve" are postures managed outside the app's
source.
App.jsxis unaware of the choice;access.jsandbackend.jscarry the app's own record rules under either posture, and the same source can serve a public copy and a private one.
What each box is, in the code
Each row names the code path that is the evidence for a box in the diagram,
so you can check the page against the platform rather than take its word.
Paths are relative to the
VibesDIY/vibes.diy repository.
The status column is the build ledger. A row reads today when the code already matches the box, a stage letter when it lands with that stage of #5489, gap when the platform decision is made and the code is not started, and proposed when the shape is stated and still open. The column is deleted when the last row reads today.
| Box | Where it is in the code | Status |
|---|---|---|
| One durable session per conversation: start, resume, idle; persisted transcript, pending handles and app bindings; a dead server resumes from the record | a session lifecycle in the Durable Object on top of the durable open-turn row, vibes.diy/api/svc/intern/open-turn-store.ts (today one row per turn, opened in public/llm-dispatch.ts) |
B |
| Tools return a handle at once and a result later; the loop waits on any handle, a message, or idle | vibes.diy/api/svc/intern/llm-tool-loop.ts, driven by intern/agent-turn.ts |
A |
| The root's first instruction names the app and picks capabilities; the mint adapter validates and reserves, never reinterprets; no pre-allocation classifier and no first-turn routing | the decoder mintDecisionFromToolInput in intern/mint-decision.ts becomes the adapter; the classifier is retired; the first-turn rule in intern/agent-router.ts still preserves draft and explicit-address builder entry |
B |
codegen_build: one build contract for an empty app or existing source; returns handle B1; the terminal lands as its result; the builder's own bounded context (BuildTask in, changes / need_context / blocked_contract out) |
vibes.diy/api/svc/intern/agent-build-tools.ts and intern/agent-dispatcher.ts (today create_vibe / edit_vibe, answering on the ack); the terminal is the post-persist block.end in public/stream-assembly.ts; the input/result boundary |
A; boundary proposed |
| Blinded draft, always fast follow, and the one-sentence security model at first completion | the root's standing instruction, vibes.diy/api/svc/intern/agent-session-instruction.ts (the account conversation uses the persistent session); the builder prompt loses its own "end every turn with one improvement question" |
C, D |
| The build's terminal wakes the session as a resume, with no budget | vibes.diy/api/svc/intern/agent-wake.ts (today one wake per user message, AGENT_WAKE_MAX_BUDGET = 1) shrinks to the resume path |
A, retired in D |
| Your message is delivered into the running session; the router delivers instead of opening a turn | intern/agent-router.ts and public/prompt-chat-section.ts |
B |
| A conflicting edit is a queued handle; one build per target app and conflicting-file serialization stay runtime rules; independent apps do not block each other | the busy arm of intern/agent-build-tools.ts becomes a deferred handle; the lock in intern/codegen-build-lock.ts |
B |
| Research worker: a delegated tool with its own bounded context, reusing the existing fetch and research loop; returns findings and source status | the research lane's fetch, retrieval and citation checks in intern/fetch-url-tool.ts and intern/url-activation.ts move inside one tool; the automatic first-build research slot retires |
C |
Direct edits: edit_file (path, old_string, new_string), write_file, validate, commit_version beside read_file; both authoring tools share one commit and result path carrying appRef, operationId, baseRevision, resultRevision, outcome and evidence |
new tools beside intern/agent-read-tools.ts; commit reuses the persist, auto-publish and billing path a build already takes in public/llm-dispatch.ts |
C |
vibe_context: focus the session's connected apps; every edit or build bound to an immutable app ref and revision |
replaces the one implicit app per conversation and the mutable acting handle read in intern/agent-actor.ts |
proposed |
| The look is a saved setting served over the code; the style mapper runs on the codegen setting with only the requested look and allowed schema | set_app_style in intern/agent-style-tools.ts |
today |
| The root's own model setting, default Opus; helpers on the codegen setting, default Sonnet; both resolved values and each invocation's actual model in the trace | the single modelId resolved in public/prompt-chat-section.ts feeds the session and the builder today; the served model is on the wire as prompt.req.request.model and prompt.model-resolved |
gap |
| Guides on demand, including the sysadmin guide for imports, releases, schedules and admission; loading grants no authority | the backend and access skill docs in prompts/pkg/llms/ enter the context on the first relevant file operation instead of by selection |
C |
Admission posture and the members list are administration outside app source; App.jsx is unaware of posture |
the envelope in vibes.diy/api/types/db-acls.ts; rules in Two layers of access and The access model |
today; the stable identity contract that keeps all three files posture-independent is an open question |
| Tool calls and their results on the wire, so the chat can show what the session is doing and replay it | #5428, persisted | B |
First paint: each streamed code.end pushes the new source into the running iframe, onHotSwapOk acknowledges the swap; a committed version swaps the same way |
vibes.diy/pkg/app/hooks/useInVibeGeneration.ts and the delivery-ack seam pkg/app/routes/vibe/preview-delivery.ts; background in How your app is served |
today; per-commit swap in C |
The wrap-up and its ▸ options are the root's |
suggestionChips in useInVibeGeneration.ts (today parsed from the newest build block, per prompts/template-parts/shared/20-end-every-turn-with.md) reads the root's wrap-up |
D |
| Per-dispatch budgets and a session idle timeout belong to the runtime, never a wake budget or a turn count in the prompt | intern/codegen-build-lock.ts (AGENT_TURN_CEILING_MS, BUILD_LOCK_DEADLINE_MS) and DISPATCH_TOTAL_BUDGET_MS in public/llm-dispatch.ts |
D |
| Publishing needs no attached model call: access rules and their UI are completed by the fast follow, and an unfinished draft stays visibly pending | autoPublishOnPersist reusing publishApp in public/llm-dispatch.ts; the access wizard's own server turn is retired and the publish gate now reads whether the version being published carries an access.js |
C |
What it is not
- Not a turn machine. There is no wake-up with a budget, no second or third turn, and no rebuilt transcript. Your messages and the tools' results are both messages in one loop.
- Not a set of modes. No first-turn versus follow-up routing, no discuss or work mode, no wizard, no recovery persona, no classifier in front of the root. File existence is input data, not a mode.
- Not a draft that stays blind. The first draft skips access rules to arrive fast; the root always completes them before it wraps up, without being asked.
- Not a queue you cannot see. A conflicting edit is a handle. It runs after the current build, and the root says so, and says when it landed.
- Not the builder talking. The builder still never speaks. The security
sentence, the wrap-up and the
▸options are the root's, said after the last result landed. - Not "done" on dispatch. Starting a build or an edit is not finishing it. The root claims a result only when that result has arrived.
- Not a model router. The root runs on its own setting and the helpers on the codegen setting; the choice between the builder and a direct edit is the root's judgment, not routing.
- Not admission in app code. Who may enter the app is a posture managed
outside the source; the app's own record rules live in
access.jsunder either posture. - Not a change to how your app is served. The build stream, the hot-swap, the one-build-per-app rule, the mint and the private-first publish are the same as before.
Where this comes from
This page follows the owner's target prompt architecture of 4 September 2026, published as jchris/prompt-boundaries, which builds on #5489 with the platform decisions and review clarifications made since. When a box moves there, it moves here in the same change.
Related
- The system prompts — what the builder is given, and the skill guides the root loads on demand.
- How your app is served — what happens once a release exists: first paint, the edge cache, deploy pins.
- Your app has a backend — the files the fast follow usually writes.