aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/design/init-load-graph.org2
-rw-r--r--docs/design/module-inventory.org4
-rw-r--r--docs/design/vterm-to-ghostel-migration-spec.org415
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.