The shared thread
One durable thread, participants taking turns on it. What a turn is, who speaks next, what a participant can tell you without stopping the run, and where the thread lives.
One turn at a time #
A run is a conversation on a shared thread. Participants take turns on it, one at a time, each on its own provider, model and effort. A turn ends with the participant's first post. A second post in the same turn is rejected, and it never reaches the thread.
A participant has two tools for the thread: read_thread, which reads posts, and post, which is its move. Only what it posts is on the thread. Its reasoning and its tool calls are not.
What a participant is shown #
A turn opens with an index, not the whole thread. It lists the posts that are new to that participant, at most the last 8, one line each, cut at 90 characters. Older posts collapse into a count and the read_thread call that retrieves them. A participant that needs the full text pulls it and spends its own quota doing so.
Your own messages are the exception: they arrive in full, up to 4000 characters, and a cut says so. The prompt of a turn does not grow with the conversation.
The moves #
A post has a kind, and the kinds are a closed list of six. A post can also carry a handoff, which names who should speak next. It is a proposal: the scheduler decides.
| Move | What it claims | What AutoDev does with it |
|---|---|---|
say | Discussion. | Nothing beyond the thread. With handoff: "user" it hands the turn to you, and the run halts as awaiting_user until you reply. |
work_done | The repository changed. | AutoDev credits it from the repository: a commit, or failing that files left changed. With neither, it is not credited. |
handoff | The turn passes on. | The next speaker is chosen as described below. |
wait | Something outside the thread must happen first. | The run waits and rechecks. The author speaks again before the rotation does. A message from you ends the wait early. |
gate | An irreversible action needs an answer. | Patterns in gateAutoDeny deny it, and patterns in gateAutoApprove approve it. With no match, gateMode decides: auto approves, and gated stops the run at gated until Approve or Deny. |
done | The objective is complete. | A stateless audit reads the work. It accepts, or reopens the run with its gap. |
A wait is rechecked on a schedule that doubles, up to a cap, and a wait that outlasts maxWaitMin pauses the run. The audit after a done is bounded by maxAcceptChecks. Every config key lists both.
Who speaks next #
The scheduler makes no model call. It reads the tail of the thread and applies these rules in order:
- A message from you goes to the participant that asked you the question, or, with a gate open, to the one that declared it.
- Four posts that run A, B, A, B with no
work_doneamong them send the turn to a third participant, the one who has been silent longest. - A
handoffto yourself is refused, unless your last post waswork_done. The turn goes to whoever has been silent longest among the others. - A
handoffnaming a participant gives it the turn. A name that is not on the team is ignored, with a warning. - After a
wait, its author speaks again. - Otherwise the turn goes to whoever has been silent longest, other than the last speaker, unless the last post was
work_done. Before anyone has spoken, that is the first participant by name.
A team of one always speaks. A message from you is fresh scope: it resets the count of turns without verified work, and the count of audits.
When the participants have produced no verified work for a run of turns, the post tool accepts only work_done, a say to you, or, from a participant that cannot write, a handoff to one that can. If that also produces nothing, the run pauses for review, and The run stopped and why lists the message. The length of that run is maxTalkTurns.
A turn that ends without any post is not a move. A participant that ends three turns in a row that way cannot speak, and the run pauses and names it.
Telling you without stopping #
say with handoff: "user" stops the run until you answer. A participant that can keep working has a lighter way to reach you, tell_user. It is not a move: it does not end the turn, and the participant still posts one afterwards. A participant may raise one notice a turn.
A notice waits for you under Needs you, with the participant's name on it, and a note from system on the thread says what it told you. You answer it in the panel, or with autodev answer. The answer reaches that participant in a later turn, and it arrives short: 300 characters of the notice and 300 of your answer.
A notice can also hold a capability until you answer. The one a participant can hold is the browser tools. A message from you releases every hold, unless you pressed Hold on that notice. Then only Release lets go, and autodev notice does the same from the terminal.
Where the thread lives #
The thread is thread.jsonl, one JSON object per post, appended by AutoDev and never rewritten:
{"seq":<n>,"from":"<participant>","kind":"say","text":"...","handoff":null,"ts":"<iso time>"}from is a participant, or one of three names AutoDev keeps for itself: user for you, system for gate notes and notices, and audit for the completion audit. seq only grows, and each participant has a cursor on it, so a turn is told what is new to it.
The file is in ~/.autodev/<project>-<hash>/, on the machine side of the split. The files you read and edit stay in .autodev/ in the repository. events.jsonl is a separate log of machine activity. It is the one the panel's Feed is built from, and every post is copied into it with its text. A message you send, from the panel or with autodev send, waits in an inbox, and the daemon appends it to the thread as a post from user at the next turn boundary.
The daemon does the work and the panel reads what it wrote. Participants and lanes covers who is on the thread, and Providers and failover covers what a usage limit does to a turn.