docs: plan the headless console (interactive chat/command CLI for the bot host)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-09-07 06:32:47 +02:00
parent 43abc32edf
commit a427d7db05

View file

@ -0,0 +1,68 @@
# Headless console — an interactive CLI for the bot host
Date: 2026-09-07
Status: ACTIVE (owner direction 2026-09-07: "the headless client should have
a CLI as well. Like we have the chat loaded in headless so we can see what it
does and we can talk via it if we want and control plugins like /moss bla or
/say hello")
Belongs with the "one plugin, two hosts" model recorded in
`docs/plans/2026-09-06-campaign-vt-vtank-oracle.md`. Independent of Campaign
VT's slices (host side only) — runs in parallel.
## What it is
When the headless host runs with a terminal attached (or `--console` /
`ACDREAM_HEADLESS_CONSOLE=1`, documented in `docs/launch-options.md`), it
becomes a chat-shaped REPL for the session:
- **Output.** The ordered event stream rendered one line per event, chat
first-class: `[Tell] Bob: hi`, `[Fellowship] …`, `[Local] …`, system text,
plugin output (everything a plugin writes through the host log/chat sink),
and lifecycle/command outcomes (`connected`, `entered world`, `portal →
Holtburg`, `command rejected: …`). Same channel names and ordering the
graphical SpewBox shows; no colors required (ANSI channel colors only when
the terminal supports them, off when piped).
- **Input.** Each typed line goes through the SAME command pipeline the
graphical chat box uses: a `/`-prefixed line is dispatched to the plugin
command registry first (`/vt …`, any plugin verb) and otherwise to the
client/server slash commands (`/say`, `/tell`, `/t`, `/f`, `/a`, emotes,
`/help`), a plain line is `/say`. No second parser, no bot-only shortcuts:
it must reuse the exact seam the retained chat box calls (find it — the
CH-campaign command registry and the chat-send route), through the
Runtime command surface Slice K already exposes ("chat and slash/server
commands").
- **Control.** `/quit` (graceful logout, exit 0), `/status` (session
generation, position, macro state if the plugin reports one). Ctrl+C keeps
today's graceful behavior.
- **Multi-session.** Out of scope for the first cut: the console attaches to
the single-session host (K1's production mode). A multi-session prefix
(`@name …`) is a later addition; leave the seam shaped for it (input routed
through a session-scoped adapter, not a static).
## Rules
- Headless stays Runtime-only: no App references, no rendering types. If the
chat-send or command-dispatch seam currently lives in App, lift the
presentation-free part into Runtime/Core with a dependency guard, and leave
the graphical client calling the same lifted code (behavior-preserving).
- Reads of stdin never block the scheduler: a reader thread feeding a
thread-safe queue drained on the session tick (Slice K's monotonic
scheduler contract).
- Every line of output goes through one renderer over the typed event stream
— the same events the K2 bot API emits — so tests assert on the rendered
text without a terminal.
- Portable: Windows and Linux; no ANSI when `Console.IsOutputRedirected`.
## Tests (Headless test project, no live server)
- Scripted stdin → rendered stdout: chat lines, a plugin command reaching
the plugin's registered verb, `/say` producing the same outbound message
the graphical route produces (assert on the command the fake Runtime
received), plain text → say, `/quit` → graceful teardown, unknown verb →
the same error text the chat box prints.
- Reader-thread/scheduler test: input queued while the tick is busy is
drained in order on the next tick, never on the reader thread.
- Launch-options doc row + the documentation test.
One connected proof at closeout (real ACE, `testaccount`, type `/say hello`
and see it echo; type `/vt start` with MossTank loaded) — the owner may run
it or the lead may, it is not a visual gate.
## Ledger
- 2026-09-07 planned; implementer dispatched.