Skip to content

Spec: agent play — the line-protocol drive

SPEC Binding system contract
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 <machine|selected> 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 <mode>`
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 <person>` lists authored plot starts
and held choices from the shared ActionDesc surface; generic
`act <row> [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.
2026-07-13 persona-authoring amendment: `personas` inspects the PERSONAS
view, `actions archetype <id>` / `act` owns CREATE PERSONA, and the retained
`persona` word is a non-mutating compatibility redirect rather than a direct
identity constructor.
Amended 2026-07-13 with the intel scale pass: the protocol must navigate
aggregate ids, review an exact recording or custody aggregate, sell an exact
actionable item or report-lot generation/revision, and configure bound
review and disposition controls without an agent-only rule path. Implemented
2026-07-14: `intel`, `actions` / `act`, targeted REVIEW, versioned-lot SELL,
and stable policy rows consume the same exception-first Operations projection
and exact bound commands as the human frontends; criteria A4-A6 are met.
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

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)

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 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

Section titled “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.

  • 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.

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:<N> day:<D>
    • -- err <reason> (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.

Canonical meanings and support states are owned by 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 <x0> <y0> <x1> <y1> — replace the frontend selection with every owned machine in the axis-aligned rect (inclusive).
  • select add <machine> / select toggle <machine> — 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 <machine> 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 <mode> with no machine argument).
  • intensity <machine|selected> 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.
  • people — render the Operations PEOPLE frame: staged dossiers and their shared bound actions
  • personas — render the Operations PERSONAS frame: immutable archetype rows and persisted identity instances. CREATE PERSONA executes only through the selected bound archetype row.
  • 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
  • intel [<intel-target>] — render the Operations INTEL hierarchy or one exact actionable/recording, custody aggregate, report-stream aggregate, versioned lot sale target, or related subject summary using the earned ids/tokens printed by that hierarchy. Aggregate ids are stable; a lot token includes <stream-id>:<generation>@<revision> and changes when the open lot changes.
  • 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 <flow> [amount], redirect <flow> [amount], inject [amount], position [stake], sell-intel <intel-id|lot-token> — economy verbs; flow and intel ids are the earned ids printed by their Operations frames. SELL binds one exact actionable item or previewed report-lot generation/revision and never chooses “latest” or later arrivals; a changed revision rejects the stale command. 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 <stake>|off — the standing scheme policies, at their compute upkeep (income.md criterion 6)
  • review recordings [<recording-or-aggregate-id>] — process the next pooled recording when omitted, or the exact eligible set on the selected canonical custody target. It never accepts a person target.
  • auto-review — toggle the implemented root policy. Recursive AUTO-REVIEW and INTEL DISPOSITION rule editing uses generic actions custody <id>|stream <id> / act <row> custody <id>|stream <id> rows for add/edit/reorder/remove/INHERIT, so agent mode does not invent another policy grammar or overload exact-intel, custody, and stream id namespaces. The frame prints stable rule ids, inheritance, matches, order, resolved outcome, and live ops/sec cost.
  • message|favor|deceive <target> — 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 <target> build <intent>, deceive <target> build <intent> — realize a pending build through the existing willing or false-pretense social verb.
  • recruit <target> unwitting|complicit|knowing
  • task <target> plug|package|lookaway|switch|badge
  • persona — retired compatibility word; redirect to PERSONAS and mutate nothing
  • actions [target] (alias menu) — list the shared bound action rows for one exact target. No argument targets the cursor tile; explicit forms include device <name>, person #N, archetype <id>, persona <id>, account <id>, flow <id>, intel <id>, recording <id>, custody <id>, stream <id>, knowledge <person>:sighting|schedule, lot <stream-id>:<generation>@<revision>, scheme <id>, and run <id>. 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 <row> [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. The stable report-stream target exposes policy controls and a related versioned lot ... target, but no enabled sale row. actions lot <token> prints the enabled sale row and act <row> lot <token> executes that same bound generation/revision; a new arrival makes the token stale and rejects execution instead of being silently included. Every enabled row therefore executes against the same target that printed it. actions: <n>. [- ]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

Agent mode has no modal confirmation subprotocol. An explicit consequential command is its confirmation surface: sell-intel must name one exact id or the versioned lot token printed by INTEL, and an external-policy act must name the bound row and stable aggregate target whose actions output prints the full envelope. That one command commits through the same core command as a human CONFIRM; bare/ambiguous targets error, a stale lot token mutates nothing, and there is never one prompt per aggregate member.

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.

  • 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).

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).

  • 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.
  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:<N> day:<D> or -- err <reason>), 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 <row> [target] without a command-specific parser route; authored plot starts and held choices are covered by protocol tests.

A1. intel, people, personas, 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 <row> [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. A4. intel [<intel-target>] renders the same exception-first hierarchy, canonical custody drill-down, related subject facets, exact/aggregate provenance, and policy inheritance as terminal and Bevy. Unknown raw subject/classification never appears in an id or label. A5. review recordings [<recording-or-aggregate-id>] binds the same REVIEW target as Operations; omitted target preserves next-pooled-recording behavior. sell-intel <intel-id|lot-token> binds one exact actionable item or one previewed report-lot generation/revision. Agent tests prove a later arrival rejects that stale sale and no alternate facet can sell the same output. A6. Bare auto-review and bound actions custody <id>|stream <id> / act <row> custody <id>|stream <id> add/edit/reorder/remove/INHERIT rows inspect and change the same registry-backed review/disposition controls as human Operations. Output names inherited/default rule, earned match, standing cost, resolved disposition, external envelope, and suspended reason without a person queue or agent-only legality path.

A1-A6 are implemented through wiki/interface/operations-workspace.md and wiki/mechanics/intel.md.