diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-05 05:28:58 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-05 05:28:58 -0500 |
| commit | ebdf9e466b0e1f86e9b7d76650ac32408273e7a7 (patch) | |
| tree | dab9b453f3a93c324b5388b3843502a088c7ed46 /docs | |
| parent | c094b2e4e64530379a9cb273303308a9affcabf6 (diff) | |
| download | dotemacs-ebdf9e466b0e1f86e9b7d76650ac32408273e7a7.tar.gz dotemacs-ebdf9e466b0e1f86e9b7d76650ac32408273e7a7.zip | |
feat(term): replace vterm with ghostel as the terminal engine
I swapped the terminal engine from vterm to ghostel (libghostty-vt) everywhere. term-config replaces vterm-config (the F12 terminal, the C-; x menu, tmux history capture), and ai-term replaces ai-vterm (the F9 Claude-agent launcher). ghostel renders the agent TUI without vterm's flicker under heavy streaming, and one engine now covers every terminal workflow.
Two behavior changes fall out of the swap. F9 launches in a terminal frame now: ghostel renders in TTY frames, so the old GUI-only guard is gone. Terminal windows no longer dim when unfocused: ghostel resolves its palette into the native module per-terminal, so there's no per-window color hook to dim through the way vterm had.
auto-dim drops its vterm color-advice path, the dashboard Terminal button launches ghostel, and the vterm and vterm-toggle packages are removed. The tmux pane-history and copy-mode machinery carried over unchanged. It keys on the pty tty, which ghostel exposes.
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/design/init-load-graph.org | 2 | ||||
| -rw-r--r-- | docs/design/module-inventory.org | 4 | ||||
| -rw-r--r-- | docs/design/vterm-to-ghostel-migration-spec.org | 415 |
3 files changed, 418 insertions, 3 deletions
diff --git a/docs/design/init-load-graph.org b/docs/design/init-load-graph.org index d4a68f47..3db2fe85 100644 --- a/docs/design/init-load-graph.org +++ b/docs/design/init-load-graph.org @@ -284,7 +284,7 @@ Category key: | =diff-config= | C/P | eager or package-loaded | Diff/merge UX. | | =erc-config= | O/D/P | command-loaded | IRC should not be startup load by default. | | =slack-config= | O/D/P | command-loaded | Slack package/auth and which-key registration should be after-load. | -| =eshell-vterm-config= | D/P | command/hook-loaded | Shell/terminal packages. | +| =eshell + term-config= | D/P | command/hook-loaded | Shell/terminal packages. | | =help-utils= | L/D | autoload commands | Search/help commands. | | =help-config= | C/P | eager or after help | Info/man/help config. | | =tramp-config= | D/P | package-loaded | Remote shell configuration. | diff --git a/docs/design/module-inventory.org b/docs/design/module-inventory.org index ffef323a..385bdbd5 100644 --- a/docs/design/module-inventory.org +++ b/docs/design/module-inventory.org @@ -193,7 +193,7 @@ flyspell-and-abbrev is the one Core-UX member (text-mode hooks). | Module | Layer | Cat | Current | Target | Runtime requires | Top-level side effects | Direct load | |--------+-------+-----+---------+--------+------------------+------------------------+-------------| | =ai-config= | 3 | D/P | eager | command | keybindings, system-lib | cj/ai-keymap under cj/custom-keymap | yes | -| =ai-vterm= | 3 | D | eager | command | cl-lib, seq, cj-window-geometry-lib, cj-window-toggle-lib, host-environment | 4 global keys | yes | +| =ai-term= | 3 | D | eager | command | cl-lib, seq, cj-window-geometry-lib, cj-window-toggle-lib, host-environment | 4 global keys | yes | | =browser-config= | 3 | D/P | eager | command | cl-lib | 1 global key | yes | | =calendar-sync= | 3 | D/S | eager (.local.el) | eager (.local.el) | cl-lib, subr-x, system-lib, cj-org-text-lib, keybindings | calendar keymap (C-; g), guarded timer/network | yes | | =calibredb-epub-config= | 4 | O/D/P | eager | command | user-constants, subr-x | add-hook, advice-add, package config | yes | @@ -236,7 +236,7 @@ owns the intentional end-of-startup buffer-bury timer. | =tramp-config= | 3 | D/P | eager | package | none | package config | yes | | =transcription-config= | 4 | O/D/P | eager | command | dired, notifications, system-lib, user-constants | 1 add-to-list | yes | | =video-audio-recording= | 4 | O/D/S | eager | command | system-lib, keybindings | cj/record-map under C-; r | yes | -| =vterm-config= | 3 | D/P | eager | command | keybindings, seq, subr-x, cj-window-geometry-lib, cj-window-toggle-lib | 2 keymaps, 1 global key, 2 add-hook | yes | +| =term-config= | 3 | D/P | eager | command | keybindings, seq, subr-x, cj-window-geometry-lib, cj-window-toggle-lib | 2 keymaps, 1 global key, 2 add-hook | yes | | =weather-config= | 4 | O/D/P | eager | command | none | package config | yes | | =wrap-up= | 2 | S | eager | eager | system-lib | one-shot startup buffer-bury timer | yes | diff --git a/docs/design/vterm-to-ghostel-migration-spec.org b/docs/design/vterm-to-ghostel-migration-spec.org new file mode 100644 index 00000000..b7d61e23 --- /dev/null +++ b/docs/design/vterm-to-ghostel-migration-spec.org @@ -0,0 +1,415 @@ +#+TITLE: Migration: vterm → ghostel (single terminal engine) +#+AUTHOR: Craig Jennings +#+DATE: 2026-06-04 + +* Status + +READY. Review incorporated (external review, 2026-06-04). Supersedes the +EAT-consolidation direction in =todo.org= (task "Migrate all terminals from +vterm to ghostel"). Research background: +[[file:../2026-05-25-emacs-terminal-comparison.org][docs/2026-05-25-emacs-terminal-comparison.org]]. + +* Goal + +Replace vterm with [[https://github.com/dakra/ghostel][ghostel]] (a native Emacs module over libghostty-vt, the +Ghostty terminal engine) as the single terminal engine across every +workflow, and rename the AI-agent launcher =ai-vterm= → =ai-term=. When the +migration lands, vterm and vterm-toggle are removed from the config. + +Why ghostel over the prior EAT plan: ghostel is the most faithful Claude +Code TUI renderer and the fastest engine (≈81 vs vterm 34 vs eat 4.9 MB/s), +and an audit confirmed it exposes an analog for every vterm primitive this +config uses. EAT's washed colors, its scroll-pop / stuck-input bug under +Claude Code, and its slowest throughput made it the weaker single-engine +pick. One engine beats running two. + +* What the spike established (2026-06-04, read-only) + +Sandbox: =/tmp/ghostel-spike= via =emacs --init-directory=, nothing touched +in the real config. Emacs 30.2 GTK, x86_64, modules supported. + +- *Install / multi-machine*: ghostel installs from MELPA; the native module + auto-downloaded (=v0.33.0 ghostel-module-x86_64-linux.so=) and loaded with + no toolchain. Confirms the per-machine prebuilt-binary story works + (x86_64-linux covers velox). Only a non-prebuilt arch needs the Zig build. +- *tmux pty (linchpin)*: a spawned ghostel reports =process-tty-name + "/dev/pts/1"=. The tmux pane-id lookup in the current vterm-config keys on + exactly that, so the tmux copy-mode / history machinery ports unchanged. +- *Colorization model*: =ghostel-color-palette= is a vector of 16 named + faces, but =ghostel--apply-palette= RESOLVES those faces (+ =ghostel-default= + fg/bg) to hex and pushes them to the native module + (=ghostel--set-palette= / =ghostel--set-default-colors=); the module bakes + the colors into the grid. Theme changes are handled by =ghostel-sync-theme= + (hooked to =enable-theme-functions= / =load-theme= advice), which + re-resolves and re-pushes. *Consequence:* buffer-local =face-remap= does + NOT dim a ghostel buffer, and there is no per-window color hook. This + drives Decision D1. +- *TTY frames*: no =display-graphic-p= / =window-system= guards in + =ghostel.el= — it renders text + faces and only kitty inline-graphics + degrade in a TTY. ghostel works in terminal frames (Decision D4). +- *copy-mode*: a read-only input-mode toggle (=ghostel-copy-mode=) with + standard Emacs nav / mark; =q= / =C-g= exit, =M-w= copies and stays. The + vterm copy-mode contract maps near-free. +- *F8 / key forwarding (diagnostic)*: ghostel's default semi-char mode + forwards unlisted keys to the terminal program; only + =ghostel-keymap-exceptions= (default =C-c C-x C-u C-h M-x M-: C-\=) reach + Emacs. This is why F-key bindings (F9 family, F12) must be installed in + =ghostel-mode-map= for terminal buffers, exactly as the current config does + for =vterm-mode-map=. +- *GUI / TTY visual*: Craig confirmed the Claude Code TUI and a TTY frame + both render great. dupre chrome applies; the 16 ANSI terminal faces are + ghostel defaults (dupre does not theme them) — Decision D2. + +* Agreed decisions + +All confirmed by Craig 2026-06-04 (incorporating the external review). + +- *D1 — auto-dim*: terminal buffers do NOT participate in unfocused-window + dimming in v1. =auto-dim-config.el= drops its entire vterm integration + (~140 lines of =vterm--get-color= advice + redraw scheduling). Rationale: + ghostel bakes the palette per-terminal, not per-window, so vterm's + per-window dim is not achievable; a buffer-wide palette re-push on + focus-loss is more code, forces repaints, and only works when the buffer is + in one window — not worth it. +- *D2 — dupre ANSI palette*: a follow-up, not v1. The 16 =ghostel-color-*= + faces (+ =ghostel-default=) get themed in dupre later, unless the engine + swap exposes visibly poor colors during verification. +- *D3 — eshell*: out of scope. =ghostel-eshell= adoption is a separate + follow-up task; eshell stays the shell. +- *D4 — TTY refuse-guard*: dropped. =cj/--ai-vterm-refuse-in-terminal= and + its echo-area refusal message are removed; F9 launches in a terminal frame. + Its manual-verify test is removed too (it asserted the refusal). +- *D5 — module names*: =vterm-config.el= → =term-config.el=; =ai-vterm.el= → + =ai-term.el=; =cj/vterm-*= → =cj/term-*=; =cj/ai-vterm-*= → =cj/ai-term-*=. + The "agent [" buffer prefix is unchanged. +- *D6 — module-failure behavior*: ghostel degrades with a warning rather than + failing startup. Load it guarded (=(require 'ghostel nil t)=) and, on + failure, emit a =display-warning= and leave the terminal commands defined + but inert. Rationale: the daemon serves many frames across machines, and + the project idiom is graceful degradation (the =(when (require 'foo nil t) + ...)= rule and =cj/executable-find-or-warn=); hard-failing startup on a + machine missing the prebuilt module is worse than a warned degrade. Tests + stub ghostel and never require the native module. (Modifies the reviewer's + recommendation of "fail loudly" — see Review dispositions.) +- *D7 — scrollback value*: =ghostel-max-scrollback= set to =10 MB= (=(* 10 + 1024 1024)=) as a defcustom, the byte analog of the prior =100000=-line + intent (~100 bytes/line). Verified under heavy output during manual + testing. + +* Primitive mapping (vterm → ghostel) + +| vterm | ghostel | note | +|--------------------------------+-------------------------------------------+------| +| =(vterm NAME)= | =(ghostel)= + rename to NAME | via =ghostel-buffer-name-function= or post-create rename | +| =vterm-send-string= | =ghostel-send-string= | public; confirmed | +| =vterm-send-return= | =(ghostel-send-string "\n")= | | +| =vterm-mode= | =ghostel-mode= | all major-mode checks | +| =vterm-mode-map= | =ghostel-mode-map= | F9 + F12 rebind, C-; install | +| =vterm-keymap-exceptions= | =ghostel-keymap-exceptions= | add =C-;= | +| =vterm-copy-mode= | =ghostel-copy-mode= | read-only input mode | +| =vterm-copy-mode-map= bindings | input-mode (q/C-g exit, M-w copies-stays) | near-free parity | +| =vterm-clear-scrollback= | =ghostel-clear-scrollback= | C-; x l | +| =vterm-next-prompt= | =ghostel-next-prompt= | C-; x n | +| =vterm-previous-prompt= | =ghostel-previous-prompt= | C-; x p | +| =vterm-send-next-key= | =ghostel-send-next-key= | C-; x q | +| =vterm-yank= | =ghostel-yank= | C-y | +| =vterm-reset-cursor-point= | drop (renderer owns point) | decided: no analog needed | +| =vterm-other-window= | =(ghostel)= + other-window display | thin wrapper | +| =vterm-max-scrollback= (lines) | =ghostel-max-scrollback= = 10 MB (D7) | unit change lines→bytes | +| =vterm-kill-buffer-on-exit= | =ghostel-kill-buffer-on-exit= | | +| =vterm-timer-delay= (nil hack) | =ghostel-timer-delay= / adaptive-fps | hacks DROP | +| =cj/vterm--send-mouse-wheel= | drop (ghostel forwards SGR natively) | net deletion; verify under tmux/Claude/lazygit | +| =cj/vterm-send-escape= | =(ghostel-send-string "\e")= if needed | re-check =<escape>= global conflict | +| =vterm--get-color= advice | none (D1) | auto-dim integration deleted | +| =vterm-always-compile-module= | =ghostel-module-auto-install= | + D6 guarded load | +| tmux pane-id via =process-tty-name= | unchanged | confirmed /dev/pts | + +* Surface to change + +Audited file set. + +** Main modules +- =modules/vterm-config.el= (~540L) → =modules/term-config.el=. Ports with + renamed primitives; deletes the mouse-wheel forwarding and the + =vterm-timer-delay= hacks; renames =cj/vterm-*= → =cj/term-*= (no + compatibility shim). Keeps the tmux history / copy-mode-dwim logic pure + around =process-tty-name= and =tmux= process calls (engine-agnostic — the + part most worth preserving). =cj/vterm-map= (C-; x) → =cj/term-map=; + which-key label "vterm menu" → "terminal menu". +- =modules/ai-vterm.el= (~978L) → =modules/ai-term.el=. Only ~6 call sites + are vterm-specific (=vterm= / =vterm-send-string= / =vterm-send-return=, + the suppress-tmux coupling, the =vterm-mode-map= F9 rebind, the + declare-functions). The ~970L of picker / MRU / crash-recovery / display + chain / dispatch / geometry is engine-agnostic and renames cleanly + (=cj/ai-vterm-*= → =cj/ai-term-*=). Buffer prefix "agent [" stays. The + refuse-in-terminal guard is deleted (D4). + + *tmux-suppression invariant (contract).* =cj/--ai-term-show-or-create= must + preserve exactly one tmux launch path for agent buffers: the dynamic + binding of the suppress flag around =(ghostel)= keeps the generic + auto-tmux hook from sending a bare =tmux\n= before the project-named + =tmux new-session -A= command runs. Porting must not introduce a second + launch path. + +** Satellites +- =modules/auto-dim-config.el= — per D1, delete the vterm color advice + + redraw scheduling entirely (no ghostel replacement in v1). +- =modules/ui-config.el= — =vterm-mode= / =vterm-copy-mode= cursor/modeline + check → ghostel equivalents (live ghostel = writeable cursor state; + =ghostel-copy-mode= = read-only). +- =modules/dashboard-config.el= — launcher lambda → =(ghostel)=; label + "Launch VTerm" → "Launch Terminal". +- =modules/cj-window-geometry-lib.el=, =modules/cj-window-toggle-lib.el= — + vterm only in comments; update doc references. +- =init.el= — =(require 'ai-vterm)= → =(require 'ai-term)=; add term-config + require (guarded per D6). + +** Docs (active references only — historical notes stay) +- =todo.org= current task link (already updated to this -spec path). +- =docs/design/module-inventory.org=, =docs/design/init-load-graph.org= — + update active =vterm-config= / =ai-vterm= references to the new names. + +** Tests (~35 files) +- 24 =test-ai-vterm--*.el= are mostly engine-agnostic logic (buffer-name, + candidates, sort, dispatch, geometry, MRU) → rename to =test-ai-term--*.el= + mechanically, only after a green baseline; assertions stand. +- Coupled, need rework: =testutil-vterm-buffers.el= (→ stub ghostel), + =test-ai-vterm--f9-in-vterm.el=, =test-ai-vterm--show-or-create.el=, + =test-vterm-copy-mode-cursor.el=, =test-vterm-tmux-history.el=, + =test-vterm-toggle--*.el= (×3). +- Cross-cutting touch: =test-auto-dim-config.el= (delete vterm-integration + tests per D1), =test-ui-config--buffer-cursor-state.el=, + =test-dashboard-config-launchers.el=, =test-init-module-headers.el=, + =test-cj-window-toggle-lib.el=. + +* Dependency / module failure behavior (D6) + +- ghostel is a required MELPA package. It loads guarded: + =(unless (require 'ghostel nil t) (display-warning 'term "..."))=. +- On a prebuilt arch the native module auto-downloads + (=ghostel-module-auto-install=). On a non-prebuilt arch the user installs + Zig 0.15.2 and builds per ghostel's instructions; until then the warning + fires and terminal commands are inert (defined but no-op / user-error), + never breaking startup or other frames. +- Tests stub ghostel in the test-util layer and never require the native + module, so the suite runs on any machine and in CI/batch. + +* Key & menu ownership (per phase) + +To avoid order-dependent duplicate bindings, ownership transfers cleanly: + +- *Before*: =vterm-config= owns F12, =C-; x=, the vterm display rule, and the + which-key labels. +- *Phase 1*: =term-config= is added and immediately becomes the owner of F12 + and =C-; x= (and the terminal display rule). =vterm-config= is no longer + required, so its bindings do not co-install. The vterm package remains + installed only as a fallback engine until Phase 4. +- *Phase 2*: =ai-term= owns the F9 family (global + in =ghostel-mode-map=); + =ai-vterm= is no longer required. +- *Phase 4*: vterm / vterm-toggle packages removed; no vterm ownership + remains anywhere. + +* Implementation phases (TDD, green at each step) + +Each phase is a shippable deliverable; the suite + byte-compile stay green at +every step. + +- *Phase 0 — characterization baseline.* Before any port, add/confirm + characterization tests for the behaviors that must survive: F12 + dispatch/display, tmux pane-id + history-buffer replacement, AI + show-or-create tmux launch command, F9 from inside terminal mode, + cursor-state classification, dashboard launcher action. Green baseline. + Deliverable: characterization tests committed; no behavior change. +- *Phase 1 — ghostel + term-config.* Add ghostel (use-package, MELPA, + guarded per D6). New =term-config.el= owning F12, =cj/term-map= (C-; x), + copy-mode parity, tmux history/copy-mode-dwim (pure =process-tty-name= + path), which-key "terminal menu", =ghostel-max-scrollback= 10 MB, + =ghostel-keymap-exceptions= incl. =C-;=. =vterm-config= dropped from the + require list (ownership transfers). Tests for the new module + ghostel + stubs. Deliverable: F12 general terminal runs on ghostel. +- *Phase 2 — ai-term.* Rename =ai-vterm.el= → =ai-term.el=; swap the ~6 vterm + call sites to ghostel; F9/C-F9/M-F9 on global + =ghostel-mode-map=; drop + the refuse-in-terminal guard (D4); preserve the tmux-suppression invariant. + Rename engine-agnostic tests to =test-ai-term--*= (after green); rework the + coupled ones; add D4 regression tests (no refusal path; F9 installed in + =ghostel-mode-map=) and a negative test that agent buffers are excluded + from F12 toggling under the new names. Deliverable: agents run on ghostel. +- *Phase 3 — satellites.* auto-dim vterm integration deleted (D1); + ui-config cursor/modeline check ported; dashboard launcher + label; + geometry/toggle-lib doc refs; init.el requires; active doc references. + Deliverable: no module references vterm except the package itself. +- *Phase 4 — remove vterm.* Delete vterm + vterm-toggle packages, dead + config, the mouse-wheel / timer hacks. Full test sweep + byte-compile + + manual smoke after a daemon restart (the restart is an acceptance gate — + see below). Deliverable: vterm gone; ghostel is the only terminal engine. + +** Follow-up / vNext (not this series) +- D2 — theme the 16 =ghostel-color-*= + =ghostel-default= faces in dupre. +- D3 — evaluate =ghostel-eshell= as eshell's visual backend. +- Evaluate =ghostel-compile= against the F4 dev-fkeys compile flow. +- =ghostel-comint= for =M-x shell= / REPL output fidelity (optional). + +* Acceptance criteria + +The migration is complete when all hold: + +1. =init.el= requires =term-config= and =ai-term=; nothing in the config + requires =vterm-config= or =ai-vterm=. +2. vterm / vterm-toggle packages and their keybindings are removed, after + ghostel parity is green. +3. F12 normal-terminal toggle excludes agent buffers and preserves saved + geometry. +4. F9 / C-F9 / M-F9 work from normal buffers AND inside =ghostel-mode= + buffers. +5. AI project launch reuses/reattaches the named =aiv-= tmux session and does + NOT receive the generic auto-tmux launch. +6. =C-; x c= and =C-; x h= preserve the tmux copy / history behavior. +7. Live ghostel buffers report a writeable cursor state; ghostel copy-mode + reports read-only. +8. Terminal-frame F9 launches (the refusal path and its test are gone). +9. ghostel-unavailable degrades with a warning, not a startup failure (D6). +10. Full test suite, byte-compile, and manual smoke all pass after a daemon + restart. + +* Test strategy + +- *Characterization first* (Phase 0): capture current behavior before porting + so parity is measurable. +- *Stub ghostel* in the test-util layer; tests never require the native + module (runs in batch/CI on any machine). +- *Rename mechanically after green*: only rename engine-agnostic + =test-ai-vterm--*= → =test-ai-term--*= once the baseline is green. +- *Regression tests for D4*: no terminal-frame refusal path remains; F9 + bindings are installed in =ghostel-mode-map=. +- *Negative test*: agent buffers are excluded from F12 normal-terminal + toggling under the new buffer/mode names. + +* Manual-verify test matrix + +Per =verification.md=, filed under "Emacs Manual Testing and Validation" at +Phase 4, run again after a daemon restart. Each: steps + expected. + +- Claude Code TUI in ghostel (GUI): colors true, flicker-free under heavy + stream, box-drawing + cursor correct. +- Claude Code TUI in a TTY frame (velox-style =emacs -nw=): renders as + text+color, layout intact; inline images absent (expected). +- F9 / C-F9 / M-F9 dispatch: toggle, pick-project, close — same behavior as + the vterm era, on ghostel, including from a terminal frame (now launches). +- tmux: agent launches in its named session; second F9 reattaches; close + kills the session; =C-; x h= captures tmux history; =C-; x c= enters tmux + copy-mode. +- copy-mode parity: =M-w= copies and stays, =q= / =C-g= exit. +- mouse wheel inside tmux / Claude Code / lazygit scrolls correctly (this was + a prior explicit vterm fix being removed — confirm ghostel's native SGR + forwarding covers it). +- lazygit, htop/btop, a heavy-output build, ssh to a remote: render + behave. +- Crash recovery: kill Emacs with a live =aiv-= tmux session, restart, the + picker flags it =[detached]= and reattaches. + +* Risks / notes + +- *Daemon module reload*: a loaded native module needs a daemon restart to + upgrade; the Phase 4 restart is an acceptance gate before deleting vterm + (plus the gold-standard full-launch smoke per CLAUDE.md after =:config= + edits). +- *Buffer naming*: forcing "agent [basename]" goes through + =ghostel-buffer-name-function= or a post-create rename — confirm the exact + hook in Phase 2. +- *<escape> global rebind*: vterm needed a custom escape forwarder because + =<escape>= is globally =keyboard-escape-quit=; re-check whether ghostel in + semi-char mode forwards it or needs the same treatment. +- *ssh terminfo*: ghostel advertises =TERM=xterm-ghostty=; outbound ssh to + hosts lacking that terminfo may need =ghostel-ssh-install-terminfo= or a + fallback =ghostel-term=. Covered by the ssh manual-verify row. +- *ANSI palette*: until D2 lands, terminal ANSI colors are ghostel defaults. + +* Implementation tasks (drop-in for todo.org) + +#+begin_src org +*** TODO [#B] Phase 0: terminal characterization baseline :terminal:ghostel:tests: +Characterization tests for F12 dispatch/display, tmux pane-id + history replacement, AI show-or-create launch command, F9-in-terminal, cursor-state classification, dashboard launcher. Green baseline, no behavior change. +*** TODO [#B] Phase 1: add ghostel + term-config.el :terminal:ghostel: +ghostel use-package (MELPA, guarded per D6); term-config.el owns F12 + C-; x + copy-mode + tmux history; which-key "terminal menu"; ghostel-max-scrollback 10MB; C-; in ghostel-keymap-exceptions. Drop vterm-config from requires. Tests + ghostel stubs. +*** TODO [#B] Phase 2: rename ai-vterm→ai-term on ghostel :terminal:ghostel: +Swap the 6 vterm call sites; F9 family on global + ghostel-mode-map; drop refuse-in-terminal guard (D4); preserve tmux-suppression invariant. Rename engine-agnostic tests after green; rework coupled tests; add D4 + F12-excludes-agent regression tests. +*** TODO [#B] Phase 3: port satellites to ghostel :terminal:ghostel: +Delete auto-dim vterm integration (D1); port ui-config cursor check; dashboard launcher + "Launch Terminal" label; geometry/toggle-lib doc refs; init.el requires; module-inventory + init-load-graph doc refs. +*** TODO [#B] Phase 4: remove vterm and vterm-toggle :terminal:ghostel: +Delete packages + dead config + mouse-wheel/timer hacks. Full suite + byte-compile + manual smoke after daemon restart (acceptance gate). Run the manual-verify matrix. +*** TODO [#C] Follow-up: theme ghostel ANSI faces in dupre :terminal:ghostel:dupre: +D2 — set the 16 ghostel-color-* + ghostel-default faces in dupre-faces/palette. +*** TODO [#C] Follow-up: evaluate ghostel-eshell + ghostel-compile :terminal:ghostel:eval: +D3 — ghostel-eshell as eshell visual backend; ghostel-compile against F4 dev-fkeys. +#+end_src + +* Review dispositions + +Only the *modified* recommendations are listed; everything else in the external +review was accepted as written. + +- *Module-failure behavior (modified).* The reviewer recommended ghostel be required + and "startup may fail loudly" if the package/module can't load. Modified to + degrade-with-warning (D6): guarded require + =display-warning=, terminal + commands inert, startup unaffected. Reason: the daemon serves many frames + across machines and the project idiom is graceful degradation; hard-failing + startup on a machine missing the prebuilt module is worse than a warned + degrade. The rest of the recommendation (ghostel required; non-prebuilt + needs Zig; tests stub the module) is accepted. +- *Scrollback value (modified → concretized).* The reviewer asked for a concrete + byte value or a defcustom. Chose =10 MB= as a defcustom (D7), the byte + analog of 100000 lines, verified under heavy output. (Not a disagreement — + filling the gap the review flagged.) + +Everything else accepted as written: D1-D5 baked as Agreed decisions; +Implementation phases + Acceptance criteria + Dependency-failure + Test +strategy sections added; key/menu ownership made explicit per phase; +tmux-suppression stated as a contract; UX changes (TTY-refusal removal, +"Launch Terminal", "terminal menu"); architecture (rename =cj/vterm-*= → +=cj/term-*=, keep tmux fns pure, no vterm-private-redraw port); doc cleanup +for active references; mouse-wheel manual verify; daemon-restart acceptance +gate. + +* Review and iteration history + +** 2026-06-04 Thursday @ 23:17:54 -0500 — reviewer + +- *What changed or was recommended:* Ran the spec-review workflow after + renaming this file to the required =-spec.org= suffix. Wrote a companion + review with a =Not ready= rubric: D1-D5 still need acceptance, the handoff + needs an =Implementation phases= section, acceptance criteria are missing, + and ghostel package/native-module failure behavior needs an explicit v1 + contract. +- *Why:* The migration direction is sound, but the current draft still leaves + implementation-affecting decisions and completion criteria for the builder + to infer. +- *Artifacts:* review file (deleted on incorporation). + +** 2026-06-04 Thursday @ 23:24:28 -0500 — responder + +- *What changed:* Incorporated the external review via the spec-response + workflow. Craig accepted D1-D5; baked them (plus D6 module-failure and D7 + scrollback) into a new "Agreed decisions" section and out of "Open + decisions." Added Implementation phases, Acceptance criteria, Dependency / + module failure behavior, Test strategy, explicit per-phase key/menu + ownership, the tmux-suppression contract, and an Implementation-tasks + drop-in block. Applied the UX, architecture, doc-cleanup, and + manual-verify additions. Status raised DRAFT → READY. +- *Why:* Close the "Not ready" findings — resolve the open decisions, + give the builder phases + acceptance criteria, and define + ghostel-unavailable behavior — so a reader can implement from this file. +- *Modified vs the review:* module-failure = degrade-with-warning, not + fail-loud (D6 rationale); scrollback concretized to 10 MB (D7). See Review + dispositions. Everything else accepted as written. +- *Artifacts:* This spec; review file deleted; =todo.org= task link updated. + +** 2026-06-04 Thursday @ 23:30:18 -0500 — reviewer + +- *What changed or was recommended:* Re-reviewed the incorporated spec and + assigned a =Ready= rubric. No further blocking review notes. The prior + blockers are closed: D1-D7 are accepted decisions, implementation phases and + acceptance criteria are present, ghostel-unavailable behavior is explicit, + key/menu ownership is phased, and implementation tasks are enumerated. +- *Why:* Confirm the spec-response pass left an implementable handoff rather + than just adding prose. +- *Artifacts:* This history entry; no new review file because the spec is + implementation-ready. |
