From f26aa0b38b276dea666e7275077a119860792c6b Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Tue, 21 Jul 2026 13:26:36 -0400 Subject: [PATCH] docs(vterm): fold architecture decisions Record the chosen C-c escape, resize and exit behavior, additive v19 compatibility, complete-frame wire, unchanged Style, and shared-session ownership. Specify the Emacs-style PID/outcome line retained after exit. --- docs/vterm-framing.md | 81 ++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/docs/vterm-framing.md b/docs/vterm-framing.md index 3c99643..599db62 100644 --- a/docs/vterm-framing.md +++ b/docs/vterm-framing.md @@ -1,6 +1,13 @@ # Vterm — framing (Arc 5 stage 2, three-PR delivery) -**Revision 1 — 2026-07-21. Status: framing for discussion; no implementation.** +**Revision 2 — 2026-07-21. Status: decisions folded; awaiting approval, no implementation.** + +Revision 2 records the architecture discussion: `C-c` is the terminal editor +escape (`C-c C-c` sends interrupt); main-screen resize reflows while alternate +screen clips/pads; exited buffers remain with an Emacs-style process message; +protocol v19 is additive with complete frames; shared `Style` stays unchanged; +and one `BufferId` owns one shared process/screen whose most recently active +frontend controls size. This framing follows the compile-mode terminal substrate that landed in PR #113. `src/process.rs` already owns PTY creation, process groups, bounded @@ -125,6 +132,7 @@ pub struct TerminalSelectionSpan { pub start_col: u32, // inclusive pub end_col: u32, // exclusive } + pub enum TerminalProcessState { Running, Exited(i32), @@ -132,7 +140,6 @@ pub enum TerminalProcessState { Crashed(String), } - pub struct TerminalSnapshot { pub buffer_id: BufferId, pub size: CellSize, @@ -143,6 +150,7 @@ pub struct TerminalSnapshot { pub selection: Vec, pub scroll_offset: u32, pub at_bottom: bool, + pub pid: u32, pub process: TerminalProcessState, } ``` @@ -183,8 +191,9 @@ Lua `ansi = true` process option retain the line-oriented profile; terminal session construction is the only initial full-screen caller. On `finish()`, `LineOriented` preserves today's synthetic alternate-screen/style balancing. `FullScreen` flushes pending text but does not invent an alternate-screen exit -or clear cells: a child that crashes in alternate screen leaves its final -visible screen inspectable. Parser internals reset in both profiles. +or clear cells. The session manager, after applying the actual final parser +events, adds the process-exit annotation described in §4.1. Parser internals +reset in both profiles. The shared `Cell::Style` remains unchanged in this arc. Existing support covers indexed/truecolor foreground/background, bold, italic, underline variants, @@ -299,12 +308,19 @@ cannot steal batches from each other. 3. the existing `process.after-tick` hook runs; 4. LSP/MCP retain their existing later ticks. -At process exit, final drained output is applied before the terminal becomes -`exited`. The buffer remains visible and copyable with a terminal-local exit -status; it is not automatically killed. Killing the buffer terminates the -process/session. A periodic prune handles all buffer-removal paths, including -Lua callers that bypass the friendly terminal close API. Editor shutdown uses -the existing supervisor shutdown path and cannot restart a terminal. +At process exit, final drained output is applied first. The manager then writes +one synthetic, default-style hard line into the active terminal screen: +`Process exited normally with code 0` for zero; `Process exited +abnormally with code ` for a non-zero code; or `Process exited +abnormally with signal ` for signal termination. It emits CRLF first +when needed so the annotation never overwrites child text. The annotation is +terminal-owned (not parser output and not rope text), visible and copyable like +the rest of the screen. +The buffer remains until the user kills it. Killing the buffer terminates a +still-live process/session. A periodic prune handles all buffer-removal paths, +including Lua callers that bypass the friendly terminal close API. Editor +shutdown uses the existing supervisor shutdown path and cannot restart a +terminal. The wire-visible process state has only the four variants above: synchronous spawn failure never publishes a session, and `terminate` remains `Running` @@ -441,10 +457,10 @@ The normalized protocol does not distinguish number-row digits from numeric keypad digits, so application-keypad mode is tracked but cannot transform those ambiguous `Key::Char` events. -`C-]` is the fixed stage-2 terminal escape prefix. It is consumed and makes the -next key run through the ordinary editor dispatcher, allowing `C-] C-x ...` -for editor commands. `C-] C-]` sends a literal Ctrl-] byte. This is an -intentional fixed stage-2 policy. +`C-c` is the fixed stage-2 terminal escape prefix. It is consumed and makes the +next key run through the ordinary editor dispatcher, allowing `C-c C-x ...` +for editor commands. `C-c C-c` sends the literal Ctrl-C byte required to +interrupt the child. This is an intentional fixed stage-2 policy. Paste sends exact bytes, wrapped in `ESC[200~` / `ESC[201~` only while the child enabled bracketed paste. It never passes through a command shell or Lua. @@ -506,6 +522,7 @@ InstanceMessage::TerminalFrame { selection: Vec, scroll_offset: u32, at_bottom: bool, + pid: u32, process: TerminalProcessState, } @@ -678,8 +695,8 @@ base-branch deletion/auto-close risk and makes each PR's gate evidence honest. 11. Strict owned `TerminalSpec` values reject before spawn and are mutation-independent after spawn. 12. A real PTY child prints cursor-addressed/alternate-screen content in - adversarial chunks; the headless snapshot matches and final output lands - before exit state. + adversarial chunks; final output lands first, then exact normal/non-zero/ + signal process annotations are visible and copyable before exit state. 13. Spawn failure leaves no buffer/session/process residue. Killing a live terminal buffer terminates it; editor shutdown leaves no child/reader. 14. Every rope mutation path (ordinary, intercept-skipping, undo/redo, local @@ -697,7 +714,7 @@ base-branch deletion/auto-close risk and makes each PR's gate evidence honest. and scrolled-back hiding are exact. 18. Printable, Ctrl, Alt, arrows, Home/End, function keys, application cursor, focus reporting, and unknown keys produce the specified PTY bytes. -19. `C-]` dispatches one editor key; `C-] C-]` sends Ctrl-]; modal minibuffer, +19. `C-c` dispatches one editor key; `C-c C-c` sends Ctrl-C; modal minibuffer, search, menu, and query-replace remain authoritative. 20. Paste is byte-exact with bracketed wrappers only when enabled. 21. Mouse-reporting modes receive translated SGR reports. With reporting off, @@ -766,22 +783,16 @@ Not part of these three PRs: Deferral means graceful ignore or documented absence, never escape leakage, panic, unbounded allocation, or child leak. -## 12. Review questions +## 12. Resolved decisions -1. **Escape prefix:** accept fixed `C-]` for the first TUI stage, with - `C-] C-]` for literal Ctrl-], or choose a different fixed prefix before - implementation? -2. **Resize:** accept logical-line reflow on the main screen and no reflow on - alternate screen? -3. **Exit:** accept keeping an exited terminal buffer visible/copyable until - the user kills it, rather than auto-killing? -4. **Compatibility:** accept v18 grid support but no terminal surface for v18 - semantic/GPU peers, while normal v18 document editing remains compatible? -5. **Wire shape:** accept complete visible `TerminalFrame` replacements for - stage 3, with complete-payload compare suppression, instead of introducing - a terminal-delta cache in the first GPU PR? -6. **Style scope:** accept preserving the current shared `Style` encoding and - explicitly deferring faint/blink/conceal/strikethrough? -7. **Session identity:** accept one screen/process per terminal `BufferId`, - shared across splits/frontends, with the most recently active frontend's - active view controlling PTY size? +The 2026-07-21 architecture discussion resolved every Revision 1 question: + +1. Fixed terminal editor escape: `C-c`; `C-c C-c` sends literal Ctrl-C. +2. Resize: reflow main-screen soft wraps; clip/pad alternate screen. +3. Exit: retain the buffer and append the process PID/outcome line from §4.1. +4. Compatibility: additive v19; v18 grid remains supported, v18 semantic has + no terminal surface. +5. GPU wire: complete visible frames with complete-payload suppression. +6. Style: preserve the shared encoding and defer unsupported attributes. +7. Identity: one process/screen per terminal `BufferId`; the most recently + active frontend's active view controls PTY size.