ADR-0026: A retried send asks again, and turns are traced by their messages¶
Status: Accepted Date: 2026-10-12 Deciders: Alex Nodeland Amends:
Context¶
Two gaps were left open, each waiting on artifactr:
send's window (ADR-0019).sendcommits its message, then asks for the thread's turn. A process that stopped between the two left the message untaken, and a retry found its id used and asked nothing.- Tracing a turn (ADR-0021, ADR-0024). The follower and the Langfuse turn context read the thread back to find the rule's message a turn carries out. The turn context reads before the turn's
run_startedis logged, so a message the rule sent meanwhile could tag the turn with the wrong chain.
artifactr has closed both:
Runner.askasks for a thread's next turn, covering whatever no turn has taken (artifactr ADR-0064).RunStarted.message_idsnames the messages a turn's start carries out, which theSessioncarries too (artifactr ADR-0065).
Decision¶
- A retried
sendasks again. When artifactr refuses the derived message id as used,sendcallsRunner.askfor the thread. That starts the turn an earlier attempt never asked for, or does nothing if a turn took the message. A refusal fromaskis handled as one fromsend. - A turn's chain is that of the last of its messages that a run posted.
turn_cause(ledger, workspace, message_ids)looks them up in the ledger, newest first. The follower passes themessage_idsof the turn'srun_started; the Langfuse turn context passes theSession's. Neither reads the thread. read_backis gone, fromFollowersand frombridged_turns.
Options considered¶
| Option | For | Against |
|---|---|---|
| The turn's own message ids (chosen) | Exact, with no read of the thread, and the same in the follower and the turn context | A turn logged before artifactr named its messages continues no chain |
| Read the thread back, as before | Works on any log | A read per turn, and the turn context could pick a message sent after the turn started |
ask after a used id (chosen) |
Closes the window with one call, which does nothing when a turn took the message | A retry reads the thread once more |
| A ledger record of each asked turn | No call to artifactr | A second record to keep in step with artifactr's |
Consequences¶
- Easier: a run that stops after sending its message still gets its turn, on its retry.
- Easier: a turn is tagged with its chain exactly, as the follower records it.
- Harder: a turn whose
run_startednames no messages, such as one logged before artifactr recorded them, starts a chain of its own.