How Lovable Separates Agent State From Context Window and Process

How Lovable Separates Agent State From Context Window and Process

Rouzbeh Delavari, a software engineer at Lovable who spent over twelve years as a staff engineer at Spotify on service and data infrastructure, published a detailed engineering post on September 24, 2026 describing the Trajectory System and Agent Control Plane behind Lovable Chats. The post is grounded in production numbers: on a typical weekday, the Trajectory System appends roughly half a billion events covering approximately 2.6 million user turns. This is not a design proposal, it is a retrospective on running infrastructure at scale, with specific architectural decisions explained and their trade-offs named.

The central observation in the post is that three things are easy to conflate but should be kept separate: what happened, what an agent needs to know right now, and when an agent should run. Lovable’s architecture tracks all three independently through an immutable event log (the trajectory), a prompt that is assembled freshly from that log at each iteration, and a scheduling signal (the activation) that is distinct from the message payload.

The Trajectory: An Immutable Event Log Modeled on Git

Every agent conversation in Lovable is a log of events. Delavari lists the event types explicitly: UserMessage, AgentStart and AgentDone bracketing an agent’s response, IterationStart and IterationEnd around each LLM call, the model’s thinking output, content, and tool_call output, and the full tool lifecycle, ToolParametersValidated, ToolExecutionStart, ToolExecutionEnd, and ToolApprovalRequired when a human must approve. Every tool call is matched to its result. Nothing is deleted: events are immutable and append-only. A revert is recorded as a Revert event, not by deleting anything from the log.

The structure is modeled on Git commits. Each event carries a single parent pointer. Following parent pointers backwards gives the full log. Named heads point at the tip of each line of history, like Git branches.

This structure enables forking for free. From any IterationEnd or AgentDone in a conversation, a new head can be created at that event. The new trajectory shares all history up to that point, exactly like branching in Git, and the fork costs almost nothing because no events are copied. The first event on the fork, a ThreadForkConfig event, simply names the fork point as its parent. Creating a branch from a 10,000-event history requires appending one event.

One deliberate departure from Git: trajectories never merge. An event has exactly one parent. Merging two independent agent histories has unclear semantics, what would interleaving two independent streams of thoughts, tool calls, and results mean? When agents need to share results, they communicate through messages via the Agent Control Plane rather than trajectory merges. Trajectories only branch.

A single builder turn shown as a chain of events with a forked trajectory branching off an IterationEnd, where the ThreadForkConfig event points back to the fork point without copying history
Source: Lovable Engineering Blog, Rouzbeh Delavari, 2026. One builder turn, one parent pointer per event. A fork is a new head positioned at an IterationEnd; its first event, ThreadForkConfig, inherits all earlier events without copying.

Context Is a Projection of the Trajectory, Not the Trajectory Itself

The trajectory is the source of truth. The prompt sent to the model is not the trajectory, it is a projection of the trajectory, assembled freshly at the start of every iteration.

At the start of each iteration, right after IterationStart is written, a prompt builder walks the agent head backwards through the event log and renders a prompt from what it finds. How much of the history the model sees, and in what shape, is a decision made per model call, separate from what the trajectory contains. This separation between the durable record and the ephemeral prompt is where several engineering patterns become possible.

Compaction is the clearest example. When the agent decides to compact, it writes a PromptCompactionStart event onto its trajectory and keeps running. A summarizer then runs as a small agent loop on a side trajectory that branches off the agent’s history at that point. The side trajectory’s events are the summarizer’s reasoning and output, not the main agent’s. When the summarizer completes, its result does not land directly on the agent trajectory. Instead, the side trajectory acts as an inbox: at the agent’s next iteration boundary, the agent admits the finished summary and records it as a PromptCompactionEnd event on its own trajectory. The next prompt build is then a backward walk that, on finding a PromptCompactionEnd paired with its corresponding PromptCompactionStart, uses the summary in place of all older events and renders everything after the compaction point as normal history.

The agent never pauses for compaction. The summarizer runs while the agent keeps iterating. The next prompt build simply finds the completed compaction and uses it. This is async compaction as a projection operation rather than a blocking transformation of the context window.

Agent trajectory showing PromptCompactionStart and End events, with the summarizer running on a side trajectory and the LLM context rendered from the completed summary rather than the full history
Source: Lovable Engineering Blog, Rouzbeh Delavari, 2026. Compaction is two events on the agent trajectory; the summary is produced on a side trajectory and admitted at the agent’s next iteration boundary.

Background agents with shared history work through the same mechanism. A forked agent’s prompt builder walks straight through the parent’s events as its own history, the fork inherits the parent’s full context for free, without copying events, and renders its own prompt from the same event types. Use-case-specific prompts are handled through different builder presets: which notices to include, whether to pull in codebase context, how to frame inherited history. The event log is the same; what the model receives is tunable per agent type.

Streaming Without Polluting the Log

Events in the trajectory are immutable and whole. But an LLM answer arrives as a stream of tokens, and users expect to see text appear word by word. Lovable handles this through a side channel of transient “partials” that exist alongside the trajectory but are never written into it.

When the agent starts receiving a content block from the model, it opens a partial: a PartialOpened message carrying the event’s initial fields and a unique ID. As tokens stream in, the system emits PartialDelta messages, small edits to one field of that open partial, typically “append this text.” Each delta is pushed live to the browser, which applies it to its in-flight copy and renders text incrementally. When the model finishes the block, the agent appends the real, complete event to the trajectory, carrying the same unique ID. That append closes the partial: anyone following the deltas now knows which persisted event they resolve to, and swaps their in-flight copy for the authoritative one.

Partials have no position in the trajectory, no parent, and are never persisted. Only the final events are written. A browser that connects mid-stream gets the persisted events so far plus each open partial materialized once with its deltas already folded in, then follows the live stream. Reconnects, refreshes, and multiple open tabs all converge on the same trajectory, because the trajectory is the only thing ever written.

Two Logs Per Agent: Inbox and Agent Trajectory

Each agent has two event logs, not one. The inbox receives everything sent to the agent: UserMessages from users, and ExternalAgentNotifications from other agents or from the control plane (a subagent finishing, a message from another agent, a scheduled wake-up). The agent trajectory is the agent’s actual running history: what it thought, said, and did.

Whenever the agent runs, it looks at the inbox and copies whatever has not yet been handled onto its own trajectory. This happens at the start of a run and again at every iteration boundary, so a message that arrives mid-response is picked up as an interjection rather than waiting for the whole response to finish.

The split serves a specific purpose: external arrival is decoupled from execution. Other actors can append to the inbox at any time, from any location, without contending with the running agent. The agent admits messages into its own history on its own schedule, at well-defined points. The trajectory stays a clean, ordered record of what the agent actually processed, and losing or duplicating an activation is harmless because the inbox is the source of truth.

The Agent Control Plane

The Trajectory System describes what happened and where it is stored. The Agent Control Plane (ACP) handles when agents run and how agents communicate. Delavari describes ACP’s surface as intentionally small: SpawnAgent, ForkAndSendMessage, SendMessage, NotifyParents, StopAgent, and a few reads.

Every interaction between agents reduces to the same two steps: append a message to the recipient’s inbox, then send an activation. The activation is a wake-up signal, not the payload. The inbox is the source of truth. Agents do not call each other directly, hold connections to each other, or know which machine the other is on. They write to an inbox and let ACP handle scheduling.

Delavari walks through one pattern end to end, a subagent reporting back to the builder that spawned it:

  1. The subagent reaches AgentDone.
  2. Its completion envelope calls NotifyParents, which builds an ExternalAgentNotification with the result and a terminal status (Completed, Failed, or Cancelled).
  3. SendMessage appends the notification to the parent’s inbox. This write is the durable step: the message is safe regardless of what happens next.
  4. ACP sends an activation. If the parent is asleep, a fleet node picks the activation and starts a run that finds the notification in the inbox. If the parent is already running, its next iteration boundary picks up the notification; the activation is acknowledged and dropped because each trajectory has a single exclusive writer and the inbox already holds the message.

The decoupling means a message to a busy agent and a message to an idle agent are the same write. The sender appends to an inbox and is done; the receiver reads its inbox at its next iteration boundary. The activation is the scheduling signal, not the communication channel.

Suspend and Resume: Agents That Survive Deploys

At any iteration boundary, the moment after an IterationEnd is written, the event history on the trajectory is everything the next iteration needs to run. So at that boundary, the agent process can stop. The run exits as suspended; no AgentDone is written; the still-open agent block is the signal that work continues. ACP sends a fresh activation; any fleet node can pick it up, check the inbox, and run the next iteration as if nothing interrupted it.

This decouples long-running agents from deployment infrastructure. When a fleet is restarting, each node stops accepting new activations and lets in-flight runs drain. Most turns simply finish. A turn that is still running suspends at its next boundary and resumes on a freshly deployed node. Sandbox infrastructure makes this cleaner: sandboxes run separately from the agent fleet, and the durable state of a project is its repository. A resumed agent reattaches to its sandbox or rebuilds from the repo. The agent process is disposable; the work lives in the trajectory and in git.

Implementation: Bigtable and Pub/Sub

Lovable stores trajectories in Bigtable. Row keys are designed so that one trajectory’s events sit adjacent in storage, making reading history, which is what constructing the context requires, a fast scan rather than a hop per event. Large payloads such as knowledge files and file changes are offloaded to a content-addressed blob store so the events themselves stay small.

Activations are recorded in an activation log and published over Pub/Sub, ordered per agent, so any fleet node can pick up an activation, boot the agent with its trajectory, and run it. A periodic reconciler re-drives activations that were published but never resolved, making a lost wake-up a delay rather than a lost message.

Limitations and What to Borrow With Care

Delavari is clear that the primitives, event sourcing, actor-like inboxes, control planes, Pub/Sub activations, are established distributed-systems patterns. The novelty is applying them coherently to the specific problem of LLM agent state at production scale, not inventing the primitives from scratch. Teams evaluating whether to adopt this architecture should distinguish between the ideas (which are transferable) and the specific Bigtable + Pub/Sub + Colossus-adjacent implementation (which is proprietary to Lovable’s stack).

The trajectory-as-source-of-truth model is genuinely different from treating the context window as the source of truth, and the practical implications are real: you can replay what any agent saw at any point in time, debug a bad turn by examining the exact trajectory it processed, and branch without copying. However, reads become walks rather than lookups, which the post acknowledges: “The cost is that reads are a walk, not a lookup. We pay that back with storage layout, caching, and compaction as a projection.” At half a billion events per weekday, the storage layout and caching design matter as much as the data model.

The two-log design (inbox plus agent trajectory) is the detail most worth borrowing for teams building multi-agent systems. Allowing external actors to write to an inbox at any time without contending with the running agent, while the agent admits messages at well-defined iteration boundaries, solves a coordination problem that shows up in almost every multi-agent architecture. It eliminates races, simplifies crash recovery (the inbox is the durable truth; losing an activation just means the agent wakes up slightly later), and makes the agent trajectory a clean audit log of exactly what was processed and when.

What This Means for Engineering Teams

For teams building production agents today using LangChain, LlamaIndex, or similar frameworks, the gap between those frameworks and Lovable’s architecture is primarily in durability and composability. Most current agent frameworks treat the context window as the primary state container. When the process dies, the context dies. When you want to branch, you copy. When you want to inspect what happened, you hope you logged enough. Lovable’s design inverts this: the trajectory is the state, the context is a derived view, and process death changes nothing except that a new process picks up the same trajectory from the last iteration boundary.

The minimum viable version of this for a team building their own agents is simpler than Lovable’s full implementation: an append-only event store (a database table with an insert-only policy and a parent pointer column is enough), a prompt builder that assembles context by reading backward from the current head, and an inbox table that holds incoming messages until the agent processes them. The ACP can start as a simple queue. The value comes from the separation of concerns, not from any specific technology choice.

Teams working on optimizing agent inference stacks will find the async compaction model especially relevant. Context management is one of the most expensive synchronous operations in a long-running agent loop. Moving the summarizer to a background side trajectory, one that runs while the main agent continues, and admitting the result at the next natural boundary removes compaction from the critical path without requiring any changes to how the agent loop itself works. The agent loop never knows compaction happened; it just finds a shorter history at the next prompt build.

For teams building agentic products where multiple agents must coordinate, the inbox-plus-activation primitive replaces the more error-prone pattern of agents calling each other synchronously or sharing mutable state. Two agents that communicate only by appending to each other’s inboxes cannot interfere with each other’s execution. A crash in one produces a lost activation at worst, the inbox already contains the message, and the periodic reconciler re-drives the activation. No coordination protocol is needed beyond writing to the inbox.

Key Takeaways

  • Lovable’s Trajectory System processes roughly half a billion events per weekday across 2.6 million user turns; the architecture is production-validated, not a design proposal.
  • Every agent has an immutable, append-only event log modeled on Git commits: each event has one parent pointer, forking is free (one ThreadForkConfig event, no copying), and nothing is ever deleted, even a revert is a new event.
  • Model context is assembled by a prompt builder walking the trajectory backwards at each IterationStart; the trajectory is the source of truth, and the context window is a derived, ephemeral view of it.
  • Async compaction runs a summarizer on a side trajectory while the agent keeps running; the summary is admitted at the next iteration boundary, removing compaction from the agent’s critical path entirely.
  • Each agent has two event logs: an inbox (where external actors write) and an agent trajectory (what the agent actually processed). The inbox-plus-activation primitive reduces all inter-agent communication to two steps: append to inbox, send wake-up signal.
  • Suspend-and-resume at iteration boundaries makes agent processes disposable: any fleet node can pick up a suspended agent’s trajectory and continue without loss, surviving deploys and sandbox failures transparently.

Work With Origins AI

Origins AI builds production AI systems for engineering teams. If you are designing multi-agent infrastructure that needs durable state, crash recovery, and cross-deploy continuity, talk to our team.