Imported from raymond-w-ko/maproom (
AGENTS.md). Install upstream withnpx skills add raymond-w-ko/maproom. Copyright stays with the author.
AGENTS.md
Read README.md for product thesis and architecture, docs/plan.md for staged delivery, docs/terminology.md for canonical vocabulary, docs/runtime.md for runtime contracts, and docs/security/threat-model.md for trust boundaries. Maproom aims to become a long-lived personal development environment: a central Director owns persistent world and terminal state, thin Runners execute processes on local or remote hosts, and replaceable Consoles present the same work through orthographic, spatial, or future interfaces. Build a useful daily-driver terminal first; keep durable semantics independent from renderers, machines, AI harnesses, and input devices. Predecessor implementation lives at ~/src/dullahan; use it to understand existing behavior and design history, not as an automatic compatibility contract. A Ghostty checkout is available under ~/src/ghostty/** when implementation research is needed.
Principles
- This software is pre-alpha. Every change is a hard cutover until further notice. Do not spend effort on migrations, backward compatibility, legacy formats, data preservation, deprecation paths, aliases, shims, or fallbacks. Replace contracts and schemas directly; delete superseded code and tests.
- Choose simplest implementation that fully meets current requirements. Avoid speculative abstraction, optional complexity, and legacy architecture.
Testing
Design each new component or subproject with its test strategy in mind before implementation: identify important behavior, external boundaries, failure modes, and the cheapest proof that gives confidence. Prefer writing the test first when it clarifies a contract or reproduces a bug, but do not force TDD where an exploratory spike or another proof is clearer. Maintain a useful mix based on risk, not quotas:
- Use classic/state-based TDD for domain logic, protocol codecs, state machines, and deterministic transformations. Prefer real values and lightweight real collaborators.
- Use mockist/outside-in TDD for orchestration and costly or dangerous boundaries such as PTYs, processes, SSH, clocks, filesystems, networks, and
libghostty-vt. Mock owned interfaces at architectural seams; verify meaningful contracts, not private call sequences. - Use focused end-to-end tests for cross-component promises that unit tests cannot prove, using real processes and transports where practical. Cover critical paths such as Runner lifecycle, PTY I/O, Director state ownership, Console synchronization, reconnect/resync, and Control Leases.
Keep the suite deterministic, bounded, and useful during development. Avoid sleep-based timing, oversized fixtures, mock-driven production abstractions, and duplicate assertions across layers. Every fitting bug fix gets a regression test at the lowest level that catches it; add a higher-level test only when the failure crossed a boundary.
Implementation decisions
Implement Runner in Go. Its thin OS-facing role benefits from pure-Go static binaries, straightforward cross-compilation, and goroutines for duplex PTY streaming and lifecycle handling. Keep it free of World logic, persistence, rendering, and terminal emulation; Director owns those concerns. Require CGO_ENABLED=0, avoid dependencies that require cgo, keep the wire protocol language-neutral, isolate PTY/process/signal/ConPTY differences behind a small OS-specific boundary, and produce a separate binary for each supported OS and architecture.
Implement Director in Go for now. Use Mitchell Hashimoto's go-libghostty bindings for libghostty-vt, with CGO_ENABLED=1, pinned binding and Ghostty commits, and all cgo details contained inside Director's terminal package. Serialize access with one owning goroutine per Terminal; keep synchronous Ghostty callbacks short. Reconsider language only if measured cgo overhead or missing C APIs fail current requirements. Director owns authoritative semantic and Terminal state, not rendering or game-engine state; future spatial Consoles may use different languages or engines.
Use Protobuf as language-neutral wire schema, with separate Runner and Console protocols sharing only common types. Runner communicates with Director through length-framed Protobuf over subprocess pipes locally or SSH stdin/stdout remotely; reserve Runner stdout for frames and stderr for bounded, redacted diagnostics. First Console communicates with Director through binary Protobuf over secure WebSocket (WSS); use HTTPS separately for assets and bulk transfer. Keep messages transport-independent, require exact protocol revision matches during pre-alpha, and do not add gRPC, custom crypto, WebTransport, Runner daemons, reconnection, or multiplexing until current requirements demand them.
Use a root justfile to aggregate workflows. Each major component owns <component>/justfile, exposed from root with mod <component> and invoked as just <component> <recipe> or just <component>::<recipe>. Use module dependencies for root-wide recipes; avoid flattened imports and shell-recursive just calls. Require just >= 1.31.0.