Argument

Vibe coding platforms differ on the layer no list reads: what they do with your session between turns

Every list comparing vibe coding platforms in 2026 grades the same five things: signup friction, free tier, full-stack reach, framework support, and price per month. All five matter. None of them tell you what the platform does with your work between two agent turns. That decision is the one that decides how it feels when an agent turn goes sideways, and it is the difference I think most makers actually care about once they have shipped one or two real apps with these tools.

M
Matthew Diakonov
10 min

Two architectures, one looks fine and the other looks identical until something goes wrong

There are roughly two ways a vibe coding platform can store the state of your project between agent turns. The first is chat history as state. The platform writes your messages and the agent’s replies to a database, the agent reads the chat back at the start of every turn, and the file system on the underlying box is treated as a scratch buffer that the next turn may overwrite. The second is commit history as state. Every accepted turn becomes a real git commit. The chat is decoration. The recovery surface is git.

From the outside the two look the same. You type, the agent replies, your preview updates. The split shows up the moment something goes wrong. In the first design, recovering from a bad turn is a model call: you ask the agent to please go back. In the second, recovering is git checkout to an earlier SHA. One is a hope. The other is a one-line shell command that the agent does not even need to run.

What mk0r’s session record actually carries

To make this concrete, here is the shape of the session record mk0r persists for every active build. You can read it yourself in src/core/e2b.ts around lines 100 to 102, where the ActiveSession type is declared. The two fields that carry the whole recovery story are historyStack: string[] and activeIndex: number. The first is an array of git SHAs, oldest at index 0. The second is the cursor into that array. Undo moves the cursor back. Redo moves it forward. Either operation runs a single git checkout inside the VM and updates the cursor. The model is not in the loop.

Persisted, not remembered

The same historyStack and activeIndex are written to a Firestore document keyed by your session key on every change. That is what makes the recovery story durable across a tab close, a reload, or a cold deploy of the parent app. The git tree lives inside the sandbox. The pointer to it lives outside. Either side can be reconstructed from the other.

One agent turn, traced end to end

Here is what one accepted turn looks like under the hood. This is not a marketing diagram, it is the actual order of calls between the page in your browser, the platform server, the sandbox, and the git tree inside it. The thing to watch is how many of the boxes touch git. Every line that ends in a git operation is a place the chat-as-state design has nothing to write to.

One accepted turn inside an mk0r sandbox

BrowserAPISandboxGit treePOST /api/agent/turn (your prompt)ACP /session/prompt to in-VM agentagent edits files in /appHMR preview update via _mk0rBridgeexec: git add -A && git commitnew SHA appended to historyhistoryStack and activeIndex written

The two lines worth lingering on are the third and the sixth. The third is the agent writing files in /app. That is the step every platform does. The sixth is the new git SHA being pushed onto the stack. That is the step that turns a turn into an addressable point you can return to. Skip that step and the only address you have for “the state right before this turn” is a chat scrollback position, which is not an address at all.

The bigger trick: closing the tab does not lose anything

The other half of the recovery story is what happens when you stop touching the page. On most cloud sandbox platforms an idle box gets reaped, your dev server shuts down, your in memory state evaporates, and the next visit boots a fresh environment. mk0r is wired differently. Trace the steps below.

You close your laptop. Three hours later you come back.

1

Tab closes

The page sends no more keep alive pings. The platform stops scheduling work for your session. Nothing is lost yet.

2

Idle timeout fires inside E2B

The sandbox's lifecycle is configured at src/core/e2b.ts line 309 as { onTimeout: 'pause', autoResume: true }. Pause freezes the running processes to disk. It does not kill them.

3

You reopen the page

The page reads your session key from localStorage and calls /api/vm/session. Server side, ensureSessionLoaded around line 1451 looks up your Firestore record.

4

Sandbox auto resumes

reconnectSandbox at line 335 calls Sandbox.connect(sandboxId), which is documented to auto resume a paused sandbox. The dev server, the file tree, and the git tree wake up in the same state you left them.

5

historyStack and activeIndex are rehydrated

The pointer to your last commit comes back from Firestore. The git tree inside the sandbox confirms the SHA. Undo, redo, and revert all work without re running any agent turn.

6

You keep building

From your perspective the page just reloaded. From the platform's perspective an entire sandbox came out of cold storage with its working state intact.

The same scenario, on a chat-history platform vs a commit-history platform

To make the difference legible, here is the same concrete scenario rendered against the two architectures. The user prompt is the same in both cases: build a single-page habit tracker, then a few turns later say “actually, throw it all away and start over with a meditation timer instead, but keep the color palette”. Then close the tab.

Same scenario, different session architecture

The agent reads the chat scrollback and tries to honor 'throw it all away'. It rewrites App.tsx. Some files from the old app stick around because the agent forgot to delete them; some break because they imported the old components. You ask the agent to clean up. It mostly does. You close the tab. You come back tomorrow. The sandbox was reaped overnight. A fresh one boots. Your files are restored from a snapshot but the in-memory state is not. The chat is back, so the agent has 'context', but the actual file system is one snapshot behind your last edits, which were never flushed.

  • Recovery is a model call
  • Stale files stick around between turns
  • Idle timeout reaps the sandbox
  • Restore is a snapshot, not the live state
  • The chat scrollback is the only address for the past

The questions to actually ask any vibe coding platform

You do not need source code access to apply this lens to any platform. You only need to run a few small experiments and read the platform’s own docs honestly. Below is the checklist I now run before committing a project to any of them, including this one.

Five questions, in order of how much they tell you

  • Is undo a chat message or a control. If you ask the agent to revert and the only mechanism is the agent doing the revert itself, you have chat history as state.
  • Does the platform expose a commit log or a checkpoints list. A visible list of named past states is a strong sign of commit history as state. A chat scrollback is not.
  • What happens when you close the tab mid-build. Reopen 30 minutes later. If you land in the exact previous file state, the platform is persisting more than chat.
  • Can you see a git directory or a file panel. The presence of a real .git inside the project is the most direct evidence the platform is treating turns as commits.
  • Is the session architecture documented. A platform that publishes how it stores work between turns is making a claim it has to live with. A platform that does not is asking you to trust the marketing.

The reasonable counterargument

The honest pushback on all of this is that most people shipping prototypes on a vibe coding platform never hit the recovery edge case in the first place. They build a small thing, ship it, move on. For that user, the difference between chat-history and commit-history is academic. That is fair. The reason I still think the question is worth asking early is that the failure mode is not gradual. You do not slowly notice your platform has chat-history-as-state. You notice it the day you regret a turn and there is no way back except asking the agent to redo work it cannot reliably redo. The first time is the worst time, and by then you have already invested the project.

The other reasonable pushback is that a strong enough model can recover from any turn if you describe what you want back. There is something to that. A frontier model is much better at “please undo the last change” than a weaker one. But the empirical evidence on the open web cuts the other way. The most common complaint about AI generated apps in 2026 is not that the agent did not understand the ask, it is that the agent confidently made the project worse and the user could not get back to a known good state. Commit-history-as-state turns that into a one click fix. Chat-history-as-state turns it into another model call.

Where mk0r fits, honestly

mk0r is not the right pick for everyone. If you live inside an IDE and you want an AI pair sitting next to your editor, Cursor or Windsurf will fit better than any of the web builders. If your project needs role based access control, a relational schema, and form driven CRUD pages on day one, Lovable, Bolt, or Replit ship those primitives in ways mk0r does not. mk0r is the right pick when you want a real openable mobile first app from a single sentence, the confidence that closing the tab does not lose anything, and the recovery surface to be git instead of a chat scrollback. The session architecture is the part of the bet that this page is defending.

Whichever platform you pick, the most predictive single thing about how it will feel three weeks in is what it does with your session between turns. Most lists never go there. Going there yourself, with the five questions above, is the only honest way to compare the field.

Want to see the historyStack rewind a session live?

On a quick call I will open src/core/e2b.ts to the lines this page cites, build a small app, deliberately wreck it with a bad turn, and rewind to a clean SHA in one click. Bring an app idea and a half-formed comparison list.

Frequently asked questions

Why is session architecture the right thing to compare across vibe coding platforms?

Because once you accept that most platforms ride on a small set of frontier models, the model is roughly fixed across the field. What is not fixed is what each platform does with the output between turns. A platform that stores each agent turn as an addressable git commit lets you rewind to any earlier good state in a second. A platform that stores the same turn as a chat message lets you scroll up but not actually return to that state without asking the model to redo work. Same model, same prompt, very different recovery profile. That is why you should look at the layer underneath the model before you compare anything else.

What does mk0r's session record actually contain?

Open src/core/e2b.ts in the appmaker repo. Around lines 100 to 102 the ActiveSession type carries `historyStack: string[]` (an array of git SHAs) and `activeIndex: number` (a cursor into the stack). Every accepted agent turn becomes a git commit, the SHA gets appended to historyStack, and activeIndex moves forward. The same record is persisted to a Firestore document keyed by sessionKey, so a fresh page load can restore the exact stack and cursor. Undo and redo are arithmetic on activeIndex; revert is a git checkout to a stored SHA. The chat panel is decoration on top of that. The git tree is the truth.

What happens when I close my laptop in the middle of a build?

Two things, in this order. First, the E2B sandbox's lifecycle is configured at src/core/e2b.ts line 309 as `{ onTimeout: 'pause', autoResume: true }`, so when activity stops, the sandbox pauses instead of terminating. Pause freezes process state on disk; it does not kill it. Second, the next time you load the page with the same session key, ensureSessionLoaded around line 1451 reads your Firestore record and calls reconnectSandbox at line 335, which auto-resumes the paused sandbox. The dev server, your file tree, your git history, and your activeIndex all come back in the same state you left them. The 1 hour sandbox timeout is the upper bound; the pool's POOL_MAX_AGE_MS at 45 minutes (line 1896) is the safety margin.

Is this any different from the chat-history-with-checkpoints pattern other platforms have?

Yes, and the difference is not cosmetic. A chat with checkpoints is still chat-as-state: the platform decides which messages are checkpoints and stores a snapshot at each. You depend on the platform's idea of what is worth checkpointing. A commit-history-as-state design inverts that. Every accepted turn is an addressable commit by default. There is no 'this turn was big enough to checkpoint' heuristic. The cost is more git commits than you might want, but git was designed to handle that fine. The win is that revert and undo are git operations, not platform features that may or may not be there when the turn you regret is the one that was not checkpointed.

Where do I see the undo/redo wired up in the code?

src/core/e2b.ts has undoTurn around line 1731 and redoTurn nearby. They both move activeIndex by one and run a git checkout to historyStack[activeIndex] inside the VM via the /exec endpoint. There is no model call in the loop. That is why undo on mk0r feels closer to undo in a text editor than to 'ask the agent to please go back'. The agent is not in the path. Git is.

How do I check the session architecture on any vibe coding platform without insider access?

Three quick experiments. One: ask the agent to do something obviously wrong, like delete the homepage. Then look for an undo control. If undo is a chat message saying 'go back', the session is chat-as-state. If undo is a control that reverts the file system in under a second with no model spin, the session is commit-as-state. Two: close the tab in the middle of a turn and reopen it five minutes later. If you land in the same place with the same files and the same preview, the platform is persisting more than chat. Three: open the project on the platform if there is a 'view files' panel, and look for a .git directory or a visible commit log. The presence of one is a strong signal.

What is the tradeoff with treating every turn as a git commit?

The history gets noisy. If you ask for ten small changes in a row, you have ten small commits. That is fine if you treat the stack as a working history rather than a release log; you can squash later when the project graduates from sandbox to a real repo. A bigger tradeoff is that the rollback target is whatever the agent committed, which may be a half-finished change if a turn errored mid-way. The mitigation is that mk0r commits at the end of accepted turns, after the dev server confirms the build still works, not on every file write. So an errored turn does not always land on the stack. You can read the commit logic in src/core/e2b.ts.

What does this mean for picking among vibe coding platforms today?

It means look one layer below the marketing. Read whatever the platform publishes about how it stores your work between turns. If the answer is 'we save the chat', that is one architecture. If the answer is 'every turn is a commit and you can revert any of them', that is another architecture. Both are legitimate; they fail differently. mk0r picks the second one because the failure mode of the first is the one most makers describe online: they nudged the agent into a worse state and could not get back. If that is your concern, prioritize commit-as-state. If your concern is something else, like full-stack reach or a polished editor experience, prioritize accordingly. The point is that the choice should be conscious, not a side effect of which list you read.

Pick the platform whose session architecture matches how you want recovery to feel. Then ship.

Build with commit-history-as-state
mk0r.AI app builder
© 2026 mk0r. All rights reserved.