Vibes DIY
Vibes DIY / Docs
creator docs · concepts · codegen

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.

You a person Your browser chat + preview The root session one loop per chat · own model Tools & workers bounded · results land later first message, press send open chat → the session starts (or resumes) transcript, pending handles and app bindings are persisted ① understand and begin “Making a club notebook…” words at 1–2 s, before any code exists the first instruction also names the app and picks its capabilities; the mint adapter validates, never reinterprets ② start the blinded first draft codegen_build(brief, skills, look) handle B1 at once — one build contract B1 · DRAFT no access rules yet · streams… ③ research, delegated when useful research(question, sources) a worker in its own context; fetch logs stay there, findings and source status come back ④ WAIT on any handle, a message, or idle build stream: hot-swap on each code.end, acked by onHotSwapOk preview paints grid → app, 10–30 s+ talks meanwhile a message lands in the same context answered here; a conflicting edit is queued after B1, independent work carries on — queued is true research findings land B1 lands: files, revision, preview ⑤ decide at the landing show the draft; ask ONE thing against it, only if the answer changes what gets built ⑥ make it real — always fast follow public-world-capable access.js, the UI those rules need, any backend.js — without waiting for another message edit_file / write_file validatecommit_version. Direct, by the root itself: facts in hand, server files, who sees what, a named person codegen_build(instruction) or delegate: a new surface or a rework B2 new screens stream… narrates WHILE it edits what is changing, what it assumed preview swaps: per committed version, and per B2 stream preview swaps again B2 lands ⑦ every handle resolved, every commit landed WRAP-UP: the security model in one sentence, then one fun-feature question with ▸ options “Anyone admitted can read… ▸ …” ⑧ nothing pending → IDLE, state persisted next message: same session, same context, from ① next message
you · what you see root session · owns the conversation and every judgment build · a slow tool, streams to the preview research and direct edits · handles that land
A first build on the web. One root session owns the conversation; the builder makes a blinded first draft; research is delegated when that helps; the root always fast-follows with the app's real access rules; it wraps up only when everything has landed, then idles with its state saved.

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_build returns 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_style applies 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

  1. 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.
  2. 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.
  3. 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.
  4. 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 any backend.js changes. Public-world-capable means the rules account for untrusted visitors and enforce record permissions on the server.
  5. 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.
  6. Queued is true. A conflicting edit runs after the build it conflicts with; the runtime serializes conflicting writes and lets independent work continue.
  7. 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.
  8. 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.
  9. 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.
  10. 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.
  11. 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.
  12. Admission is administration, not app code. "Anyone with the link" and "only people I approve" are postures managed outside the app's source. App.jsx is unaware of the choice; access.js and backend.js carry 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.js under 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