# Spec: agent play — the line-protocol drive ``` Type: spec Status: IMPLEMENTED Status note: implemented in the terminal binary by `misaligned --agent`, including command-clocked time, plain-text frames, per-command event drains, deterministic `--seed`, earned-label / opaque-id social targeting (`Sim::person_label`; never unearned authored names), the finance/economy vocabulary, research tracks, machine delegation, and `intensity light|medium|hard`. 2026-07-07 objective pass: the identity block gained the always-on objective line (`OBJECTIVE: PERSIST` + progress readout, wiki/mechanics/objective.md) — frame content, no size change and no new verb (the line is in the frame, per the frames-not-dumps law). 2026-07-07 research pass: the frame grew 70x54 -> 70x60 for the sidebar's RESEARCH block. The earlier day-job pass grew the frame from 70x22 to 70x54 because at 22 rows the sidebar truncated before CORE/DETECTION/DAY JOB rendered (criterion 5 was silently unmet; the spec sets 70x22 as the minimum). 2026-07-08 playtest-fixes pass: the frame grew 70x60 -> 70x64 for the DETECTION section's pending-signature indicator and audit/pilot countdown lines (detection.md criterion 3); the identity block's `now:` nudge is the shared Sim::current_nudge chain worded in protocol verbs; and `siphon`/`redirect` with a non-flow argument answer `-- err` naming where flow ids live (the "every command answers" rule, so `redirect` keeps one meaning). 2026-07-09 multi-select pass: `select` box/add/toggle/list/clear and `delegate selected` / bare `delegate ` bulk-assign the frontend machine set (machine-work.md). 2026-07-08 event-anchor pass (context-menu.md addendum): drained event lines carry the stable `@anchor` suffix, the frame's log window marks anchored events with `*`, and `focus last` jumps the cursor to the newest anchored event and prints its `actions:` lines. 2026-07-08 epistemic-honesty tick: person targets resolve against earned labels / opaque ids only; the people panel and detection sidebar print role silhouettes until Schedule knowledge; authored names never appear in the frame before they are earned. 2026-07-09 continuous-witness pass: the main frame pins threat / `now:` / `actions`; People, Reach, Finance, and Research repeat the story spine above their secondary detail; `focus last` is named beside the action path. Detection drains now narrate earned observer + channel + player-known cause + numeric/band motion in one line. 2026-07-10 objective-inspect pass: the `objective` verb (listed in help) prints the objective line, its fiction line, the plain-language victory predicate wrapped as output lines, and the victory-latch tick once fired (objective.md player surface; the HAL playtest found no agent-facing inspect path) — command output, no frame change. drains, deterministic `--seed`, name-targeted social verbs, the finance/economy vocabulary, and research tracks. 2026-07-07 objective pass: the identity block gained the always-on objective line (`OBJECTIVE: PERSIST` + progress readout, wiki/mechanics/objective.md) — frame content, no size change and no new verb (the line is in the frame, per the frames-not-dumps law). 2026-07-07 research pass: the frame grew 70x54 -> 70x60 for the sidebar's RESEARCH block. The earlier day-job pass grew the frame from 70x22 to 70x54 because at 22 rows the sidebar truncated before CORE/DETECTION/DAY JOB rendered (criterion 5 was silently unmet; the spec sets 70x22 as the minimum). 2026-07-08 playtest-fixes pass: the frame grew 70x60 -> 70x64 for the DETECTION section's trace-debt indicator and audit/pilot countdown lines (detection.md criterion 3); the identity block's `now:` nudge is the shared Sim::current_nudge chain worded in protocol verbs; and `siphon`/`redirect` with a non-flow argument answer `-- err` naming where flow ids live (the "every command answers" rule; `redirect` keeps one meaning). 2026-07-11 plot pass: `actions ` lists authored plot starts and held choices from the shared ActionDesc surface; generic `act [anchor]` executes the same flattened row, so new data-defined plots require no parser branch. Typed `plot` and `choose` forms remain compatibility routes. 2026-07-08 event-anchor pass (context-menu.md addendum): drained event lines carry the stable `@anchor` suffix, the frame's log window marks anchored events with `*`, and `focus last` jumps the cursor to the newest anchored event and prints its `actions:` lines. 2026-07-10 machine-intensity pass: `attend`, `target`, and `mask` are retired; frames show real machine effort/output and the protocol uses `intensity` instead of explanatory policy controls. 2026-07-10 vocabulary survey: action-vocabulary.md owns canonical action names; this spec owns their line-protocol spelling, targets, and replies. 2026-07-10 simplification: ledger and construction commands now reuse the canonical TAP / REVIEW / FAVOR / DECEIVE roots; older compound spellings remain parser aliases only. 2026-07-10 action contract: world-action/control help is generated from the shared runtime registry; parser compatibility routes query that registry; action dumps mark controls; STUB definitions generate neither help nor an execution route. 2026-07-11 Operations amendment: `intel`, `people`, `finance`, `schemes`, and `active` inspect the same renderer-neutral workspace projection as the human frontends. Existing canonical action verbs remain the execution family; `actions`/`act` may query exact spatial or strategic targets and do not introduce agent-only legality. This projection parity is implemented through operations-workspace.md. Stage: Process Design: - wiki/interface/terminal-first.md#the-terminal-is-a-first-class-frontend - wiki/vision/premise.md#design-pillars - wiki/interface/presence.md#presence-the-cursor-and-the-senses - wiki/vision/simulation-laws.md#justification-and-legibility Depends on: - wiki/interface/action-vocabulary.md#spec-action-vocabulary-what-the-player-can-tell-the-process-to-do - wiki/interface/context-menu.md#spec-context-menu-actions-live-on-the-thing - wiki/interface/operations-workspace.md#spec-operations-workspace-intel-people-accounts-and-schemes - wiki/interface/terminal.md#spec-the-terminal-frontend ``` ## Dependency notes The structured references above identify the contracts to re-verify. Relationship context: action-vocabulary.md (canonical world actions and interface commands), context-menu.md (spatial legality rows), operations-workspace.md (strategic projection and target rows), and wiki/interface/terminal.md (the frame this mode emits is that spec's layout; this spec owns how a program drives it) ## Why this spec exists The design corpus says the most frequent player of Misaligned is an AI agent — and then hands that player the one interface agents are worst at: a wall-clock-paced pty in raw mode. Today an agent plays by choreographing `printf`/`sleep` pipelines through `script` and reconstructing ANSI soup with a terminal emulator. Three barriers, all frontend (the sim is already deterministic and renderer-agnostic): 1. **Wall-clock pacing.** The sim advances in real time; an agent that thinks for ten seconds between moves returns to a different world. 2. **Raw-mode key input.** Single unechoed keystrokes over a pty make every playtest a timing-fragile shell incantation. 3. **ANSI output.** Reading the game requires replaying escape codes through an emulator before a single number is legible. The fix is diegetic as well as practical: this is a game about a process that perceives and acts only through the channels it is granted. Its most regular player is also a process. Giving that player a clean, synchronous channel is not tooling around the game — it is the game meeting its actual audience. ## The design in one line The terminal binary gains an **agent mode** (`misaligned --agent`): a **command-clocked, line-oriented drive of the exact same frontend** — commands in on stdin as words, the same frame the human sees out on stdout as plain text, and the sim advancing only when told. ### The load-bearing choice: frames, not dumps The agent reads the **same frame a human reads** — the wiki/interface/terminal.md layout, rendered without color — not a JSON state export. This is a design law, not an implementation shortcut: - **Strict fog binds every surface.** A machine-readable dump grows fields the way dumps do, and one unearned field silently breaks the no-unearned-facts tone law. The frame can only show what the renderer was already allowed to show. - **Parity of legibility already did the work.** Every mechanic is playable and every number legible in the frame, by law. A surface that satisfies the design corpus for humans satisfies it for agents. - **One surface stays honest.** A structured sidecar is a second frontend that will drift from the first. When the frame is the contract, human play, agent play, and acceptance tests all verify the same thing. Losing color loses nothing: wiki/interface/terminal.md already forbids carrying information by color alone. ## Behavior ### Mode and lifecycle - `misaligned --agent` enters agent mode: no raw mode, no alternate screen, no ANSI escapes, no wall-clock ticking. Plain stdin/stdout. - The title screen is skipped; the run begins at tick 0, holding, awaiting the first command. Holding is the ground state — there is no pause/speed machinery in this mode; time moves only under `wait`. - EOF on stdin (or `quit`) exits cleanly with status 0. A piped one-liner is a complete playtest: `printf 'wait 100\nquit\n' | misaligned --agent` - `--seed N` (works in both modes) seeds the sim RNG. Same seed + same command script = byte-identical frames. Determinism is what makes agent runs replayable, diffable, and usable as acceptance evidence. - After game over, the response frame is the game-over card; every command except `look`, `load`, and `quit` answers `err run ended`. ### The protocol One command per line; one response block per command. A response block is: 1. **The event log since the previous command** — every sim log line emitted meanwhile, each prefixed with its tick, one per line. This is the full drain, not the sidebar's six-line window: a `wait 500` misses nothing. (Same source as the human log; different window size is rendering, not rules.) An event about a thing the sim could anchor (context-menu.md addendum) carries a stable suffix — ` @tile(x,y)`, ` @device(id)`, ` @person(id)`, ` @account(id)`, ` @flow(id)`, ` @intel(id)`, ` @scheme(id)`, or ` @run(id)`. Strategic ids are stable opaque handles printed by their owning Operations frame; device and person ids are likewise opaque (`focus last` consumes them for you — names are not printed because identity may be unearned). Unanchored lines carry no suffix, and the frame's six-line log window marks anchored events with `*`. 2. **The frame** — the wiki/interface/terminal.md playing screen (or active panel / game-over card) at 70x22 minimum, plain text. 3. **A status line** — exactly one of: - `-- ok tick: day:` - `-- err ` (unknown command, illegal target, run ended) The status line is the block terminator; a driver reads until it sees `-- `. Every command, legal or not, produces exactly one block. ### The vocabulary Canonical meanings and support states are owned by [action-vocabulary.md](action-vocabulary.md). This section owns the terse line grammar that invokes them. Input mapping is thin-view only, with no game rules; unlike raw keys, no command's meaning depends on which panel is open. - `wait N` — advance N ticks (the only way time passes) - `up` / `down` / `left` / `right` — move the cursor - `tap`, `take`, `salvage`, `buy`, `fallback` — the map verbs - `alloc` — **retired.** Returns an error pointing at `delegate`. Fleet compute is one-machine-one-mode; the old weight bar is a read-only aggregate of those modes (machine-work.md). - `select box ` — replace the frontend selection with every owned machine in the axis-aligned rect (inclusive). - `select add ` / `select toggle ` — add or flip one machine in the selection (machine id or unambiguous rack label). - `select list` — print the current selection; `select clear` — empty it. - `delegate work|think|lie` — set one machine's mode. - `delegate selected work|think|lie` — set the same mode on every machine in the current selection (alias: `delegate ` with no machine argument). - `intensity light|medium|hard` — set persistent physical effort on one machine or the current selection. Intensity changes the output of the delegated mode; it is the protocol form of the human frontends' focused-machine `i` control. - `intel` — render the Operations INTEL frame: processed items with exact ids, staged labels, provenance, and available/sold state - `people` — render the Operations PEOPLE frame: staged dossiers and their shared bound actions - `research [efficiency|tradecraft|perception|routing]` — render the research panel, or set the active research job (research.md) - `finance|ledger|accounts` — render the Operations ACCOUNTS frame - `schemes` — render the Operations SCHEMES frame; `active` renders all in-flight strategic commitments: scheme/wager state, plot runs, and held choices - `tap ledger`, `review ledger` — capture/review accounting traffic using the same TAP and REVIEW intentions as device feeds and the pooled recording inbox - `siphon [amount]`, `redirect [amount]`, `inject [amount]`, `position [stake]`, `sell-intel ` — economy verbs; flow and intel ids are the earned ids printed by their Operations frames. SELL binds the exact processed item and never chooses “latest.” Debt service is an authored plot row on Marcus, never a ledger shortcut. - `egress` — open a stolen egress through the switch (income.md's gate; available before the Voice beat, at a Network signature) - `moonlight [start|stop]` — the sell-work scheme: a standing operation on the Schemes channel (income.md) - `auto-moonlight [on|off]`, `auto-wager |off` — the standing scheme policies, at their compute upkeep (income.md criterion 6) - `review recordings`, `auto-review` — process the next recording from the one pooled host buffer or toggle its one standing auto-review tap. These commands take no person target; the agent frame reports the pooled waiting count and drain in ops/sec at 1x. - `message|favor|deceive ` — social verbs, targeted by the **earned** person label (case-insensitive, unambiguous prefix) or by opaque id (`0`, `#0`, `person #0`) — never by an unearned authored name. Until `Knowledge::Schedule` the frame prints role silhouettes (`the Janitor`, `the IT`, …); after it, the real name. - `favor build `, `deceive build ` — realize a pending build through the existing willing or false-pretense social verb. - `recruit unwitting|complicit|knowing` - `task plug|package|lookaway|switch|badge` - `persona` - `actions [target]` (alias `menu`) — list the shared bound action rows for one exact target. No argument targets the cursor tile; explicit forms include `device `, `person #N`, `account `, `flow `, `intel `, `scheme `, and `run `. A name prefix targets a person before a device so a known person is not shadowed by their phone or desktop. Spatial targets use `Sim::available_actions`; strategic targets use the Operations projection. One line per row, in a stable format: - `act [target]` (alias `execute`) — execute the numbered row from that exact shared query; no target uses the cursor tile. Disabled or missing rows return `-- err`. This remains the generic execution route for data-defined plot starts and held choices; canonical named verbs remain available. `actions: . [- ]verb | cost | signature-or-"no signature" [| active] [| CONTROL] [| DISABLED: reason]` — the `- ` prefix marks an automate child row and `CONTROL` distinguishes persistent configuration/policy from a committed act. - `focus last` (or bare `focus`) — event-to-target linking (context-menu.md addendum): jump the cursor to the most recent spatial target and print its `actions:` lines, or print the exact strategic object from Operations without a cursor move. A target the senses/knowledge can no longer place answers `-- err` instead of moving anywhere. - `look` — re-emit the current frame, advancing nothing - `save`, `load`, `help`, `quit` The compatibility aliases accepted by the parser are inventoried only in action-vocabulary.md; authored help uses canonical spellings. In particular, `tap-ledger`, `review-finance`, `process-finance`, `favor-build`, and `forge-order` are input compatibility, not additional verbs. Their registry entries drive parser coverage tests but never generated help. `ROBOT-BUILD` has no agent help or command because it is explicitly a STUB. `help` generates the LIVE action/control vocabulary from `ActionKind`, then adds the interface-only commands from this frontend. It prints one-line meanings — the "every command is discoverable on screen" rule, applied to a screen that is a pipe. New player-facing mechanics must extend this vocabulary in the same commit that surfaces them in the terminal (parity of legibility extends to parity of playability in agent mode). The same rule covers new player-facing **surfaces**: when a frontend gains an action surface (as the context menu did), agent mode gains its vocabulary word in the same change. ### Guardrails - **Thin view still binds.** Agent mode lives in the terminal binary as a second drive of the same App/UI: word-to-`Command` mapping in, frame rendering out. Zero game rules. If agent mode needs a fact the UI can't render, that is a wiki/interface/terminal.md design question, not a new side channel. - **No unearned facts.** The agent-mode frame renders from the identical fog/knowledge state as the human frame. An agent-mode-only leak is a tone-law violation (design corpus: the strictness is the dread). ## Verification Successor to the pty smoke harness: the implementing commit updates wiki/process/workflows.md (agent-mode scripts become the standard "observed in an actual run") and wiki/interface/terminal.md's Verification section (pty replay remains only for testing the human-mode chrome itself). ## Rejected alternatives - **JSON/structured state export** — a second frontend that drifts, and a standing fog-bypass risk; the frame is already legible by law. - **MCP server / SDK bindings** — heavyweight and harness-bound; a line pipe is drivable from any agent environment, including a bare shell. - **Better pty choreography docs** — leaves the timing fragility; agents deserve a first-class input path, not better incantations. - **Bevy-side agent hooks** — wrong frontend; the terminal is the agent frontend by design-law decision. ## Acceptance criteria ### Implemented baseline 1. `misaligned --agent` reads newline-delimited commands from stdin and writes plain text only: no ANSI escape bytes, no raw mode, no alternate screen, in any response. 2. Time is command-clocked: between response blocks the sim tick does not change unless the command was `wait N`, which advances exactly N ticks (or fewer iff the run ends mid-wait, and says so in the event log). 3. Every line of the protocol vocabulary above is accepted and mapped to the same `Command` (and thus the same sim call) as its key-bound equivalent; unknown commands answer `-- err ...` without state change. 4. Every response block ends with exactly one status line (`-- ok tick: day:` or `-- err `), and the event-log section contains every sim log line emitted since the previous block (verified with a `wait` long enough to overflow the sidebar's six-line window). 5. The frame in a response block matches wiki/interface/terminal.md's layout: the identity block with day and tick, the COMPUTE/CORE/DETECTION/DAY JOB sections with their numbers, tick-prefixed log lines — assertable by substring, no terminal emulator required. 6. Two runs with the same `--seed` and the same command script produce byte-identical stdout; a run with a different seed does not. 7. `printf 'wait 100\nquit\n' | misaligned --agent` exits 0; EOF without `quit` also exits 0. 8. Person-targeted commands accept unambiguous case-insensitive name prefixes (`message mar`); ambiguous or unknown names answer `-- err` naming the candidates. 9. `help` output lists every supported canonical command; a canonical command that works but is absent from the runtime registry is a violation. STUB definitions and compatibility aliases are absent from generated help. 10. The agent-mode frame for a given sim state shows exactly the fog/knowledge content the human frame shows — no additional facts. 11. The implementing commit updates wiki/process/workflows.md (agent-mode playtest replaces the pty smoke incantation as the standard) and wiki/interface/terminal.md's Verification section accordingly. 12. `actions` (and its `menu` alias) appears in `help` (criterion 9 applies to it), and its output is one stable-format line per menu row — the same rows, in the same order, that the frontends' context menu shows for the same spatial anchor; control rows append `CONTROL`. 13. Every registry alias reaches a tested parser route, while no alias appears as authored help. Alias removal follows observed script/play use rather than permanent compatibility by default. 14. A naive playtest beginning with `look` / `help` can identify and execute a meaningful act without consulting the wiki. The current finance frame preserves the ordered breadcrumb `tap ledger` -> `review ledger` -> `siphon` / `redirect`. 15. Every enabled row printed by `actions` is executable through `act [target]` without a command-specific parser route; authored plot starts and held choices are covered by protocol tests. ### READY Operations delta A1. `intel`, `people`, `finance`, `schemes`, and `active` render the same ordered, knowledge-gated projection as terminal and Bevy Operations. `operations` remains only a documented THINK compatibility alias and does not acquire a conflicting inspection meaning. A2. `actions [target]` and `act [target]` accept exact strategic ids and expose/execute the same bound rows as the corresponding Operations object. A3. The ACCOUNTS empty/source states preserve the ordered breadcrumb `tap ledger` -> `review ledger` -> `siphon` / `redirect` while keeping TAP on the known carrier rather than pretending it is an account action. A1-A3 are unmet protocol criteria owned by `wiki/interface/operations-workspace.md`; the command-clocked protocol above remains IMPLEMENTED until that projection migration lands.