# source
The DotBot panel's data source — the one seam between the editor panel and the
DotBot agent surface.
**This implementation is a fixture.** Every reading it hands back is a constant
written into its `init.luau`. It talks to no plugin, no provider and no network,
spends nothing, and holds no credential. It exists so the panel can be built and
looked at while the real surface is built elsewhere.
The contract is shaped after the `dotbot.*` trusted exports, so swapping the
fixture for the real surface is a change to this one file:
| Half | Members |
|------|---------|
| Read-only, answered in the frame | `available`, `agents`, `agent`, `terminalAgents`, `accounts`, `accountsFooter`, `models`, `model`, `levelsFor`, `level`, `usage`, `conversations`, `currentConversation`, `agentOfConversation`, `alternativesTo`, `history`, `sendable`, `events` |
| Asking a read again | `refreshConversations`: the conversation list is asked again on its next read. `conversations` and `history` translate each plugin answer once and hand back the same list until the plugin answers again. |
| Start / poll, because a trusted handler may not yield | `connect.start` / `.poll`, `openSignIn.start` / `.poll`, `disconnect.start` / `.poll`, `addAccount.start` / `.poll`, `renameAccount.start` / `.poll`, `send.start` / `.poll` |
| Read-write but unpriced | `setModel`, `setReasoning`, `modelOf`, `levelOf`, `stop` |
| The turn each tab runs, and what waits for it | `submit`, `turnOn`, `turnRunning`, `queued`, `stopTurn`, `unqueue`, `release`, `forgetTurn`, `pollTurns`, `observeTurn` |
| The half only a fixture has | `fixture.enable`, `fixture.scenario`, `fixture.approve`, `fixture.setConnected`, `fixture.dropEventsBefore`, `fixture.reset` |
A `start` answers `{ ok = true, id }` or `{ ok = false, reason = "busy" }`. A
`poll` answers `pending`, `awaitingPerson`, `done`, `refused` or `error`. A
device-code flow carries its code, its page and its expiry on the
`awaitingPerson` answer, because that is what a person has to be shown while the
job waits.
Two `done` answers carry a flag that says the fixture did not do the thing:
`openSignIn` reports `opened = false` with the address it would have opened, and
`send` reports `delivered = false` with the reason. The panel shows both rather
than leaving a button that looks like it worked.
`fixture.enable(false)` takes the data away, and the panel says
"DotBot is not available in this build" instead of drawing a conversation
nobody had.
## One turn per tab
A tab runs one turn at a time, whoever started it: a send from the composer, or
a turn the plugin woke into on its own. `turnOn(tabId)` is that turn's record,
and the Working row, the Stop button and the composer all read it.
`submit` starts a turn on an idle tab; while one runs it queues the message
(`"queue"`, the default) or stops the running turn and runs the message once
that turn has ended (`"interrupt"`). A turn's `end` starts the next queued
message. `stopTurn` holds the queue, so a stopped tab stays stopped until the
person sends again (`release`). `unqueue` takes a queued message back to edit
or drop.
## Scenarios
`fixture.scenario(name)` puts the readings into one state at a time:
`normal`, `fresh`, `notice`, `warning`, `blocked`, `signInExpired`,
`codeExpired`, `codesUnavailable`, `notLinked`.