tarek.kharsa

home / lessons / opencode · lesson 1

ArchitectureOpenCode · lesson 1 of 82 min read

Admit the prompt first, run it later

A durable inbox between “you pressed Enter” and “the model sees it.”

Problem

In most agents, pressing Enter saves your message and starts the model in one step. After a crash, a retry or a message sent mid-task, nobody can say what the model actually saw.

Idea

Save the prompt to a durable inbox first. Move it into the conversation later, at a safe point between model calls.

Try it: the prompt inbox

send, steer, queue, retry, crash

Prompts land in the inbox first. At each turn boundary, the agent moves them into the conversation. A crash loses the running work, never the inbox.

sessions.prompt({ id, sessionID, prompt, delivery: "steer" | "queue" })
  -> same ID again: same receipt, no duplicate
  -> same ID, different prompt: fails loudly

How it works

  1. AdmitYour prompt becomes one row in a session_input table. It's saved and replayable, but the model hasn't seen it.
  2. PromoteAt the next safe boundary, one atomic step removes the row from the inbox and appends it to the conversation.
  3. RunThe model works on the conversation. If the process dies, whatever is still in the inbox is waiting for the next run.

Steer or queue

Steer (default)

Joins at the next turn boundary, while the current task keeps going.

"Also add a test."

Queue

Waits until the agent would otherwise go idle, then starts one at a time.

"After that, update the docs."

What isn't saved, on purpose

The running part, a drain, isn't a stored thing: "A Session Drain has no durable identity or transcript boundary." Interrupting stops the work but keeps the inbox, and interrupting an idle session does nothing. After a crash, recovery starts from what was saved (prompts, history, tool results), not from an invented "job" record.

Steal this

Put a durable inbox between "the user sent it" and "the model sees it." Make prompt IDs idempotent, make steer and queue explicit, and don't save the parts that are only coordination.

Use it in your code

A prompt for your coding agent

Run it from the root of your repo. It checks whether the idea fits before it changes anything.

Check whether this app needs a durable inbox between "the user sent it" and "the work starts".

The idea: save each incoming request (a prompt, a job, a message) as a durable row first, then move it into processing at a safe point. A retry with the same ID returns the same receipt instead of duplicating work. "Steer" (apply to the current task) and "queue" (run after it) are explicit modes. A crash loses only in-flight work, never accepted input.

1. Find where user input or jobs are accepted and processed, and show me whether accepting and processing happen in one step.
2. Tell me whether a crash, retry or concurrent submission could lose, duplicate or reorder input. If not, say so and stop.
3. If it could, propose the smallest inbox: a table keyed by a client-supplied ID, an idempotent insert that returns the existing receipt on retry and fails on a conflicting reuse, and one atomic step that marks a row as consumed when processing picks it up.
4. Implement it, with tests for a duplicate submission, a conflicting ID, and a crash between accepting and processing.

Keep "who is running right now" in memory unless there's a real reason to store it.

Read the source

Where this comes from

OpenCode (anomalyco/opencode), the open-source coding agent (these lessons are from its upcoming v2). The files behind this lesson: