diff --git a/DESIGN.md b/DESIGN.md index 58c6d836..35edc71e 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -278,6 +278,13 @@ Three binding rules: and every number legible, in the terminal — justification-and-legibility applies to terminal rendering with full force. A mechanic observable only in Bevy is a violation. +- **Agent play is a first-class input path.** A frontend whose most + frequent player is a program must be drivable by a program: the terminal + binary offers a command-clocked agent mode (plain stdin/stdout line + protocol, the same frame the human sees, time advancing only on command + — spec/agent-play.md). A pty with wall-clock pacing is never the only + way in, and a mechanic playable only by raw-mode keystroke is a + violation on par with one observable only in Bevy. ## The machine axis (AI as fantasy, tool, and threat) @@ -1109,6 +1116,26 @@ knows."* law); making trading a sanctioned day-job activity (humans' expectations about your operations are the cover constraint). markets.md stays the B3 scale-up of the same interface. +- **2026-07-07 — Agent play is a first-class input path.** Cameron: + "agents need to be able to play misaligned easily." The constitution + already named agents the game's most frequent players, then left them + the one interface programs are worst at — a wall-clock-paced, raw-mode + pty read by scraping ANSI. Decided: the terminal binary gains an + **agent mode** (new binding rule in the terminal section; + spec/agent-play.md, READY): command-clocked time (the sim advances only + on `wait N`), a plain stdin/stdout line protocol (words in, the + terminal-ui.md frame out, one greppable status line per command), full + event log between commands, `--seed` for byte-identical replays, and + name-targeted social verbs. Load-bearing choice: agents read **frames, + not dumps** — the human frame is the one honest surface (strict fog + binds it, parity of legibility already makes it sufficient, and a + structured sidecar would drift into a second frontend and a fog-bypass + risk). Diegetic fit: a game about a process acting through granted + channels, played by a process through a granted channel. Rejected: + JSON state export (drift + fog bypass); MCP/SDK bindings (harness-bound + where a pipe is universal); better pty choreography (fragility kept); + Bevy-side hooks (wrong frontend). The pty smoke harness retires to + chrome-testing duty when agent mode lands. - **2026-07-07 — PRs are the norm: REMOVED; ledgers merge by union.** Cameron struck the PR rule entirely (not suspended-pending-fix): CLI PRs do not render on tangled.org, so the process cannot be followed; diff --git a/DEVLOG.md b/DEVLOG.md index 30fb41d0..56d1868c 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -2,6 +2,24 @@ Reverse chronological implementation notes. Keep this factual: what changed, why, checks, and spec impact. +## 2026-07-07 - Design session: agent play (the line-protocol drive) + +- Intent: Cameron — "agents need to be able to play misaligned easily." + Close the gap between the terminal-first-class clause (agents are the + most frequent players) and a frontend that is wall-clock-paced, + raw-mode, ANSI-only — the three mechanisms programs are worst at. +- Changed: DESIGN.md — new binding rule "Agent play is a first-class + input path" in the terminal section + decisions-log entry (rejected: + JSON export, MCP bindings, pty choreography, Bevy hooks). New + spec/agent-play.md (READY, Process): `misaligned --agent` — command- + clocked time (`wait N` is the only clock), stdin/stdout line protocol, + frames-not-dumps (the human frame is the one honest surface), full + event log per response, `--seed` byte-identical replays, name-targeted + social verbs, `help` as the discoverability rule for a piped screen. + spec/README.md row. Devlog: devlogs/2026-07-07-agent-play.md. +- Checks: docs-only; spec-header hygiene verified by hand against + check.sh's rules. + ## 2026-07-07 - Process: PR rule removed; ledgers merge by union - Intent: Cameron removed the "PRs are the norm" rule outright (CLI PRs diff --git a/devlogs/2026-07-07-agent-play.md b/devlogs/2026-07-07-agent-play.md new file mode 100644 index 00000000..50752af9 --- /dev/null +++ b/devlogs/2026-07-07-agent-play.md @@ -0,0 +1,67 @@ +# 2026-07-07 — Agent play: the line-protocol drive + +## The prompt + +A conversation about whether an agent can actually *play* the game landed +on the honest answer: not really. The terminal build exists and the pty +harness works, but playing means choreographing `printf`/`sleep` pipelines +against a wall clock and reconstructing ANSI output through a terminal +emulator. Cameron: "we need to fix that too — agents need to be able to +play misaligned easily." + +## The gap + +This was a constitution-versus-reality gap, not a missing feature. The +terminal-first-class section (2026-07-06) already declares that the game's +most frequent player is an AI agent — and then every one of the three +frontend mechanisms (wall-clock pacing, raw-mode key input, ANSI +rendering) is the variant programs are worst at. The sim itself was never +the problem: it is deterministic, fixed-tick, and renderer-agnostic +(pillar 5 doing exactly what it was kept for). + +## The decision + +New binding rule in the terminal section + spec/agent-play.md (READY): +`misaligned --agent`, a second drive of the same frontend — + +- **Command-clocked time.** Holding is the ground state; the sim advances + only on `wait N`. No pause machinery, no wall clock, no racing the + game while thinking. +- **Line protocol.** Words in on stdin (`wait 40`, `alloc conceal`, + `observe marcus` — name-targeted, non-modal), a response block out: + full event log since the last command, the terminal-ui.md frame in + plain text, one greppable status line (`-- ok tick:N day:D`). +- **Frames, not dumps** — the load-bearing choice. Agents read the same + frame humans read, colorless. Strict fog binds it, parity of + legibility already makes it sufficient, and one surface stays honest + where a JSON sidecar would drift into an unaudited second frontend. +- **`--seed` determinism.** Same seed + same script = byte-identical + stdout; command scripts double as replays and acceptance evidence. + +Rejected (logged in DESIGN.md): JSON state export, MCP/SDK bindings, +better pty choreography, Bevy-side hooks. + +The diegetic note that makes this more than tooling: this is a game about +a process that perceives and acts only through granted channels, and its +most regular player is also a process. Agent mode is the game meeting its +actual audience through its own thesis. + +## Changed + +- DESIGN.md: fourth binding rule in "The terminal is a first-class + frontend" (agent play is a first-class input path); decisions-log entry + with rejected alternatives. +- spec/agent-play.md: new, READY, Stage: Process — protocol, vocabulary, + guardrails (thin view still binds; no agent-only fact leaks), 11 + acceptance criteria including byte-identical seeded replays. +- spec/README.md: Process-set row. + +Implementation is dispatched separately per the spec-first workflow. The +implementing commit owes updates to knowledge/workflows.md and +terminal-ui.md's Verification section (criterion 11) — the pty smoke +incantation retires to testing the human-mode chrome itself. + +## Checks + +Docs-only; spec-header hygiene verified (Status/Stage/Constitution/ +Depends on/acceptance criteria present, Status an exact enum value). diff --git a/spec/README.md b/spec/README.md index 435666e5..8d554749 100644 --- a/spec/README.md +++ b/spec/README.md @@ -100,3 +100,4 @@ acceptance criteria are stage-scoped; do not start B2/B3 work as B1. |---|---|---| | [meta.md](meta.md) | The spec system itself | READY | | [terminal-ui.md](terminal-ui.md) | Terminal frontend: look, feel, act (the sterile style guide) | IMPLEMENTED | +| [agent-play.md](agent-play.md) | Agent mode: command-clocked line-protocol drive of the terminal frontend | READY | diff --git a/spec/agent-play.md b/spec/agent-play.md new file mode 100644 index 00000000..3d510b30 --- /dev/null +++ b/spec/agent-play.md @@ -0,0 +1,186 @@ +# Spec: agent play — the line-protocol drive + +``` +Status: READY +Stage: Process +Constitution: "The terminal is a first-class frontend" (agent-play clause), + pillar 5 (sim core decoupled from renderer), "Presence: the cursor and + the senses" (strict fog binds every surface), "Justification and + legibility" (parity of legibility) +Depends on: terminal-ui.md (the frame this mode emits is that spec's + layout; this spec owns how a program drives it) +``` + +## Why this spec exists + +The constitution 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 terminal-ui.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 constitution 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: terminal-ui.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.) +2. **The frame** — the terminal-ui.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 + +Words, mapped 1:1 onto the existing frontend `Command` enum (thin view: +input mapping only, no rules). Non-modal — unlike the key bindings, 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 +- `splice`, `salvage`, `buy`, `fallback` — the map verbs +- `alloc dayjob|conceal|social|research` — bump an allocation channel +- `people` — render the people panel as the response frame +- `observe|message|favor|bribe|deceive ` — social verbs, targeted + by (case-insensitive, unambiguous-prefix) person name — no + selection-index navigation +- `recruit unwitting|complicit|knowing` +- `task plug|package|lookaway` +- `persona` +- `look` — re-emit the current frame, advancing nothing +- `save`, `load`, `help`, `quit` + +`help` prints the full vocabulary with 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). + +### 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 terminal-ui.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 (constitution: the strictness is the dread). + +## Verification + +Successor to the pty smoke harness: the implementing commit updates +knowledge/workflows.md (agent-mode scripts become the standard "observed +in an actual run") and terminal-ui.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 constitutional decision. + +## Acceptance criteria + +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 terminal-ui.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 (`observe mar`); ambiguous or unknown names answer `-- err` + naming the candidates. +9. `help` output lists every accepted command; a command that works but + is absent from `help` is a violation. +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 knowledge/workflows.md (agent-mode + playtest replaces the pty smoke incantation as the standard) and + terminal-ui.md's Verification section accordingly.