docs: spell out the turn and round model

doc/turns-and-rounds.md defines the two nested units the loop is built on: a round is one request→response plus the tool batch it asked for, a turn is one user message through the explicit stop (0..N rounds), and a session is a stored sequence of turns. It records what bounds each unit (max_rounds, truncation continues, retries), which work is round-scoped (request assembly, context reduction, the aggregated soft confirm) and which state resets per turn (read-before-edit, yyy auto-continue, /yolo once, the todo touched flag, usage totals) versus what outlives it (rows, todo stack, grants, reminder bands).

Linked from the architecture doc, with the short version in AGENTS.md.
This commit is contained in:
2026-10-17 17:37:22 +08:00
parent 78b764f70e
commit 729627eee9
3 changed files with 103 additions and 0 deletions
+4
View File
@@ -28,6 +28,10 @@ GitHub Actions runs `ruff check`, `ruff format --check`, `basedpyright`, then `p
## Architecture
### Conversation units: turn vs round
A **round** is one model round trip: the request, the single assistant message it produced, and the tool batch that message asked for (one `UsageEvent` each). A **turn** is one user message through the explicit stop — the first round whose assistant message asks for no tools — and holds 0..N rounds; `retry` continues the *same* turn instead of opening a new one. `max_rounds` (32 by default) bounds rounds per turn. Turn-scoped state (read-before-edit, `yyy` auto-continue, `/yolo once`, todo-nag "touched" flag, `last_turn_usage`/`last_turn_rounds`) resets on the next user message; session-scoped state (rows, todo stack, grants) does not. Details: [doc/turns-and-rounds.md](doc/turns-and-rounds.md).
### Data modeling: `msgspec.Struct`
All protocol models use `msgspec.Struct` — not dataclasses, not Pydantic. Optional fields use `msgspec.field(default=UNSET)` / `default=UNSET` with type `T | Unset`.