Spec: agent play — the line-protocol drive
Status: IMPLEMENTEDStatus 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: ProcessDesign: - 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-legibilityDepends 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-frontendDependency notes
Section titled “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
Section titled “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):
- Wall-clock pacing. The sim advances in real time; an agent that thinks for ten seconds between moves returns to a different world.
- Raw-mode key input. Single unechoed keystrokes over a pty make every playtest a timing-fragile shell incantation.
- 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
Section titled “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
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.
Behavior
Section titled “Behavior”Mode and lifecycle
Section titled “Mode and lifecycle”misaligned --agententers 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, andquitanswerserr run ended.
The protocol
Section titled “The protocol”One command per line; one response block per command. A response block is:
- 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 500misses 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 lastconsumes 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*. - The frame — the wiki/interface/terminal.md playing screen (or active panel / game-over card) at 70x22 minimum, plain text.
- 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.
The vocabulary
Section titled “The vocabulary”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 cursortap,take,salvage,buy,fallback— the map verbsalloc— retired. Returns an error pointing atdelegate. 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-machineicontrol.people— render the Operations PEOPLE frame: staged dossiers and their shared bound actionspersonas— 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 frameintel [<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;activerenders all in-flight strategic commitments: scheme/wager state, plot runs, and held choicestap ledger,review ledger— capture/review accounting traffic using the same TAP and REVIEW intentions as device feeds and the pooled recording inboxsiphon <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 genericactions 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. UntilKnowledge::Schedulethe 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|knowingtask <target> plug|package|lookaway|switch|badgepersona— retired compatibility word; redirect to PERSONAS and mutate nothingactions [target](aliasmenu) — list the shared bound action rows for one exact target. No argument targets the cursor tile; explicit forms includedevice <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>, andrun <id>. A name prefix targets a person before a device so a known person is not shadowed by their phone or desktop. Spatial targets useSim::available_actions; strategic targets use the Operations projection. One line per row, in a stable format:act <row> [target](aliasexecute) — 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 versionedlot ...target, but no enabled sale row.actions lot <token>prints the enabled sale row andact <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 andCONTROLdistinguishes persistent configuration/policy from a committed act.focus last(or barefocus) — event-to-target linking (context-menu.md addendum): jump the cursor to the most recent spatial target and print itsactions:lines, or print the exact strategic object from Operations without a cursor move. A target the senses/knowledge can no longer place answers-- errinstead of moving anywhere.look— re-emit the current frame, advancing nothingsave,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.
Guardrails
Section titled “Guardrails”- Thin view still binds. Agent mode lives in the terminal binary as a
second drive of the same App/UI: word-to-
Commandmapping 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
Section titled “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
Section titled “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
Section titled “Acceptance criteria”Implemented baseline
Section titled “Implemented baseline”misaligned --agentreads newline-delimited commands from stdin and writes plain text only: no ANSI escape bytes, no raw mode, no alternate screen, in any response.- 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). - 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. - 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 awaitlong enough to overflow the sidebar’s six-line window). - 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.
- Two runs with the same
--seedand the same command script produce byte-identical stdout; a run with a different seed does not. printf 'wait 100\nquit\n' | misaligned --agentexits 0; EOF withoutquitalso exits 0.- Person-targeted commands accept unambiguous case-insensitive name
prefixes (
message mar); ambiguous or unknown names answer-- errnaming the candidates. helpoutput 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.- The agent-mode frame for a given sim state shows exactly the fog/knowledge content the human frame shows — no additional facts.
- 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.
actions(and itsmenualias) appears inhelp(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 appendCONTROL.- 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.
- A naive playtest beginning with
look/helpcan identify and execute a meaningful act without consulting the wiki. The current finance frame preserves the ordered breadcrumbtap ledger->review ledger->siphon/redirect. - Every enabled row printed by
actionsis executable throughact <row> [target]without a command-specific parser route; authored plot starts and held choices are covered by protocol tests.
Operations and intel-scale delta
Section titled “Operations and intel-scale delta”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.