Working knowledge that doesn't belong in source comments: decisions, follow-ups, open questions, deferred work. Source rots; this file is where the WHY lives.
P3 core complete. P0a + P0b + P0 close + P1 + P2 partial + deferred-work sweep + P3 core all shipped on PR #1. P3 core added the typed-Action interceptor, two evaluators (allow-all + binary allowlist), per-snapshot nonced content-boundary delimiters, and the in-process AuditEvent firehose broadcast channel.
Next major chunks: P3 follow-on (OPA-WASM Rego evaluator, auth vault with mlock + kernel-keyring/Keychain, per-pane mpsc queue) or P4 (MCP server + cargo-dist + distribution channels).
Default engine: alacritty_terminal v0.26.0. Flipped from wezterm-term during P0a because wezterm-term is not published on crates.io — only available as a git dependency, which would block the cargo install agent-tui / npm install -g / brew install distribution promise documented in ADR-TA-012.
Implication: v1 does not have Kitty graphics + Sixel + OSC 8 hyperlinks in the engine. The PNG rasterizer (P1 task) will stub those. The wezterm crate stays as a placeholder; revisit when a published source appears or when libghostty-vt tags stable.
| Topic | Decision | Rationale |
|---|---|---|
| Engine sync | Mutex<Box<dyn MasterPty + Send>> — wrap every portable-pty handle in a Mutex |
Trait bound is Send only; storing the Pane in Arc<> needs Sync |
| Per-pane queue | Deferred from P0 to P1 cycle 13 | Mutex on write_input is functionally equivalent until the wait subsystem needs concurrent observers |
| Doctor | Wired to DaemonStatus, not its own command |
Avoids a wire-protocol change; status payload covers reachability + version + pane count |
| Snapshot generation | GenerationTracker keyed by PaneId lives in handlers/snapshot.rs |
Moves onto Pane itself when the per-pane queue lands |
| Signal delivery | nix::sys::signal::killpg to the process group via MasterPty::process_group_leader |
Reaches forwarded shell children, not just immediate child |
- task 0.4 — Per-pane
mpsc::Sender<Command>queue. Re-deferred to P3 with policy. The only thing a real queue adds today isPANE_BUSYsemantics under--no-wait, and no flow triggers that yet. - PluginAdapter (sub-process JSON-RPC over stdio) — Moved to P4 alongside
mcp serve. Both speak stdio JSON-RPC; build the framework once. - nvim / tmux built-in adapters — Will land as external plug-ins via PluginAdapter once #2 ships. Avoids dragging
nvim --headless/tmux -CCinto CI. - wezterm engine real impl — Blocked on
wezterm-termbeing published to crates.io. Track only. - Windows runtime — cycle W2 — SHIPPED (PR #124), with a mechanism change. Cycle W1 (IPC swap to
interprocess) shipped on all platforms; W2 landed signal handling as ConPTY-input ETX (SIGINT) +taskkill /F /Tdescendant-tree kill (SIGTERM/SIGKILL) instead of the plannedGenerateConsoleCtrlEvent(which cannot reach a child on its own pseudoconsole), plus ConPTY DSR render, headless pipe spawn forrun/ask/edit, therun | consumerhandle-inheritance fix, andwindows-latestin the CI matrix. SIGBREAK/SIGQUIT is rejected honestly (no delivery path). Still open: owner-only DACL on the named pipe, real Ctrl-Break delivery — seedocs/design/windows-strategy.md.
- ✅ Client read-timeout derives from the command —
send_and_recvno longer hardcodes 25s.Waitcarries its own deadline (--max), so the read timeout iswait_timeout + 5smargin; everything else keeps the 25s base. Fixesask … --max 120000/run --max >25sdying at 25s while the daemon was still working (gaps §3.3). Global--timeoutis still unwired — left for a follow-up; deriving from the command is the prescribed fix and is self-contained. - ✅
snapshot --mode text --keep-color— reconstructs per-cell SGR fromfg/bg/attrs(palette / 24-bit / attrs), coalescing same-style runs and resetting per row. Default text stays plain (gaps §3.7). Color→SGR mapping unit-tested;>=256colors (NamedColor defaults) map to terminal default. - ✅
session gc—[--older-than-days N=7] [--all] [--dry-run]. Reaps dead sessions' sidecars + cast dirs; never reaps a session whose socket still answers (non-spawning liveness probe). Age-gated so a just-crashed session isn't pulled from under a retry (gaps §3.9). Pure planning logic + fs enumeration unit-tested; e2e tokio test.
Deferred from this PR (with reason): wait --ready-for-stdin (needs
ptrace/syscall tracing or a recipe startup-delay hint — not a small
primitive, gaps §3.5) and uniform --json (cross-cutting output-format
refactor touching every verb + the integration suite, gaps §3.13).
Pre-existing red left untouched: xtask docs-coverage fails on 13
un-annotated headings in ai-cli/addressing/vim skills (identical
on clean main). Per lesson §7, that's a standalone fix-up, not this PR.
- ✅ Focus tracking —
pane focus <id>command + tri-state focus (Auto/Focused/Held). Focused-pane death promotes to Held; explicit refocus required. - ✅ Marker events — Every command emits an
mrecorder event with {kind, ok, error_code}. - ✅ Checkpoint events — Per-pane background task pushes
sevent every 1000 mutations. - ✅ First-bytes Detect re-attach — PtyChild captures first 512 bytes; spawn fires a deferred re-detect that swaps the attached adapter if a better one wins on populated PaneInfo.
- ✅ OSC 133 raw-byte parser — Pre-engine scanner upgrades
PaneState::Unknown→Shell/Runningon FinalTerm shell-integration markers. - ✅ Stale comment sweep — Removed every
lands in P0b/v0.1.0 wires only outlinelie. - ✅ P3 core: typed Action governance —
Governance::checkwrapsSpawnandInputactions;AllowlistEvaluatorenforces--allowed-binaries;AuditEventbroadcast channel fires on every Decision (Allow/Deny/RequireConfirm). NewPOLICY_DENIED/POLICY_PENDINGerror paths surface to callers. - ✅ Per-snapshot nonced content-boundary delimiters — Every snapshot response carries an
<<<AGENT_TUI_OUTPUT_xxxxxxxx>>>/<<<END_xxxxxxxx>>>pair with a fresh 8-hex-char nonce so a malicious TUI can't inject a colliding marker. - Snapshot
--mode cells/hybrid/adapter— Only--mode outlinewired in P0a. Cycle 15. - State classifier — Returns
Unknownunless alt-screen is on. Cycle 16. The 9-state heuristic stack ships with the recorder. - Asciicast recorder — Event types exist (
agent-tui-recorder); no writer yet. Cycle 17–18. - Stale doc comments — CLI flag claims "alacritty lands in P5" (it's the default now);
server.rscalls dispatch a "stub matrix" (it isn't anymore). Sweep in cycle 12. - CLI engine default — Still
Weztermdespite the substrate flip; spawn handler ignores it and instantiatesAlacrittyEngineregardless. Cycle 12. - wezterm engine crate — Returns the placeholder. Keep as a stub until either wezterm-term is published or libghostty-vt tags. Don't delete — the abstraction validates the trait works for multiple substrates.
- OSC 133 detection precedence. Once the classifier lands, does an OSC 133 marker in scrollback override a current alt-screen flag? Probably yes (shell-in-screen is still shell), but worth a test.
- Wait under per-pane queue. Does
wait --idleblock the queue from accepting concurrent reads? Current design:waitshould NOT be on the input queue; it subscribes to engine events directly and the snapshot path is already lock-free. - Hash window size. RFC pins 256. We can revisit if
WAIT_HASH_UNKNOWNshows up in practice — the snapshot-then-wait-on-hash pattern depends on the window covering a typical agent action latency (~2-5 seconds of mutations). - Recorder backpressure. RFC says "drop oldest non-g/s on full". Current sketch: bounded mpsc with
try_send; on full, drop and bumpagent_tui_recorder_dropped_total. Need to confirm tokio's broadcast channel doesn't already do this — it's the closer-fit primitive.
| Commit | Phase | Notes |
|---|---|---|
| (next) | P3 core | Typed Action interceptor, AllowlistEvaluator, AuditEvent firehose, per-snapshot nonced delimiters |
| 5ca9990 | Deferred-work sweep | Focus tracking, marker/checkpoint events, first-bytes redetect, OSC 133 parser, stale-comment sweep |
| fcf460c | P2 partial | AdapterRegistry, sectioned generic outline, claude-code + shell built-ins |
| e212d9c | P1 | wait subsystem, cells/hybrid snapshots, classifier, recorder + rotation/retention |
| d7057f7 | P0 close | doctor wired to DaemonStatus |
| 3b2239f | P0b | keymap, press/type quiesce barrier, send_ansi, resize, signal |
| 2140ae9 | P0a | real engine + PTY + registry + spawn/die/list/snapshot |
| 04a664a | scaffolding | workspace + RFC + skeleton |
Live record of the addressing-model rollout. Spec: docs/design/addressing-rfc.md.
| # | Phase | Status | Notes |
|---|---|---|---|
| 1 | Selector grammar + parser (pure lib) | in-progress | foundation, pure logic |
| 2 | Protocol types (state, RoutedStep) | todo | wire-format additions |
| 3 | Daemon: snapshot --select, wait --ref |
todo | |
| 4 | Adapter trait: route() |
todo | default impl = identity |
| 5 | CLI: press --to, wait --gone |
todo | |
| 6 | Built-in adapters emit durable refs | todo | vim, shell, claude-code |
| 7 | Migrate existing tests to new idioms | todo | no compat constraint |
| 8 | Update skills (core, vim, addressing new) |
todo | |
| 9 | New integration scenarios | todo | |
| 10 | Pre-push lint + CI green | todo |
- tmux adapter (v1 limitation, separate phase)
- Adapter composition (v2, separate RFC)
- Manifest-driven
[routing]blocks (v2) - Selector caching API
-
wait --refstable_ms variant - htop adapter (nice to have)
- fzf adapter (nice to have)
- claude-code
response-streamingvsresponse-finalrole split (claude-code adapter now emits@ai-cli.responseas a single node; splitting requires per-CLI streaming-end detection) - Cell-mode crop for
snapshot --select --mode cells
- Selector parser lives in
agent-tui-protocol— pure logic over outline types; no new crate, no new deps. RoutedSteplives inagent-tui-adapter.- Bool predicates not in v1 grammar. Use role variants
(
role=response-streaming) instead. - No backward compat constraint per user. Rewrite tests freely.
(populated as we go)