aboutsummaryrefslogtreecommitdiff
path: root/todo.org
diff options
context:
space:
mode:
Diffstat (limited to 'todo.org')
-rw-r--r--todo.org348
1 files changed, 256 insertions, 92 deletions
diff --git a/todo.org b/todo.org
index 5401fa8d4..588450bca 100644
--- a/todo.org
+++ b/todo.org
@@ -63,23 +63,6 @@ ghostel is held at 0.33.0 (=ghostel-20260604.2049=, commit 5779a2adceb2) in =mod
archsetup automated the zig 0.15.2 pin (managed =install_zig_pin= step, sha-verified, unit-tested). If the un-pinned ghostel bumps its ghostty dependency to a newer zig, send archsetup the new version + sha256 so it bumps its =ZIG_VERSION= / =ZIG_SHA256= constants (=inbox-send archsetup=).
-** VERIFY [#A] calendar-sync drops final occurrences, resurrects cancelled meetings :bug:solo:next:
-:PROPERTIES:
-:LAST_REVIEWED: 2026-06-13
-:END:
-Needs from Craig: a real .ics fixture (or two) that reproduces both symptoms — a recurring event missing its final occurrence, and a cancelled meeting that reappears. This is RFC-5545 recurrence handling (RRULE/UNTIL/EXDATE/STATUS:CANCELLED); I won't guess-patch the parser without a failing case to test against. Drop a sanitized .ics and I'll write the characterization test + fix.
-RFC 5545 conformance holes in =modules/calendar-sync.el=, all agenda-visible (from the 2026-06 config audit):
-- =:973,1015,1024= — UNTIL treated as exclusive (strict =calendar-sync--before-date-p=); RFC and Google make it inclusive, so the LAST instance of every UNTIL-bounded series vanishes. Tests assert loose count ranges, so it's unpinned. Allow equality.
-- =:578= — comma-separated EXDATE lists (Google emits them) never parse; the exclusion drops silently and cancelled occurrences reappear on the agenda. Split on "," before parsing; no comma-case test exists.
-- =:902= — timed events without DTEND render as all-day (time lost); multi-day all-day spans collapse to one day (end date unused, exclusive-DTEND unhandled). Emit start-time-only stamps and org date ranges.
-
-** VERIFY [#A] Native compilation disabled config-wide; GC at stock 800KB :bug:next:
-:PROPERTIES:
-:LAST_REVIEWED: 2026-06-13
-:END:
-Needs from Craig: re-enabling native-comp config-wide is a stability/perf judgment, not a mechanical fix. Was it disabled deliberately (a crash, a build without native-comp, async-warning noise)? If you want it back on, confirm and I'll re-enable + raise the GC threshold and verify a clean full launch; otherwise this stays parked. I won't flip it blind.
-From the 2026-06 config audit (verified against the live daemon). =early-init.el:69= =(setq native-comp-deferred-compilation nil)= — the obsolete alias of =native-comp-jit-compilation= — turns JIT native compilation OFF entirely, not "synchronous" as the comment claims: 19 .eln files exist for 184 packages, ~100 of 121 modules run interpreted for the daemon's lifetime, and system-defaults.el:42-44's speed-3/8-jobs/always-compile settings are dead. Plus =early-init.el:113-116= restores =gc-cons-threshold= to the captured STOCK default (800000, verified) post-startup — frequent small GC pauses forever. Together these plausibly feed the filed org-capture 15-20s task more than anything in the capture path itself. Actions: retest the old "Selecting deleted buffer" race on 30.2 and re-enable JIT (or AOT sweep); set a deliberate 16-64MB threshold (or gcmh). Check both before burning time on the capture-perf debug task.
-
** VERIFY [#B] calendar-sync robustness: atomic writes, curl --fail, zero-event false errors :bug:solo:next:
:PROPERTIES:
:LAST_REVIEWED: 2026-06-20
@@ -106,18 +89,12 @@ From the 2026-06 config audit, =modules/transcription-config.el=:
- =:210= — =make-process :stderr= with a file PATH creates a BUFFER named like the path (verified by probe); the "Errored. Logs in <file>" notification points at a log without the error text, and the hidden stderr buffer leaks per transcription. Route stderr into the process buffer or write it out in the sentinel.
- =:370-374= — video path derives txt/log from the temp mp3's /tmp path; the transcript lands in /tmp and dies on reboot, contradicting the "alongside the source" docstring. Pass the video's path as the output base.
-** 2026-06-20 Sat @ 10:29:42 -0400 Dirvish: d duplicates, D force-deletes (guarded)
-Decided with Craig 2026-06-20: remove delete-to-trash entirely, bind =d= = =cj/dirvish-duplicate-file= and =D= = =cj/dirvish-hard-delete= (sudo rm -rf after a =yes-or-no-p= naming the exact targets). Built in =modules/dirvish-config.el= (=cj/--dirvish-hard-delete-command= pure builder + =cj/dirvish-hard-delete= command; keymap =d=/=D= swap). 4 ERT tests for the command builder; full suite green; live-reloaded into the daemon (=dirvish-mode-map= =d=/=D= rebinding confirmed). Manual keypress + sudo-flow check filed under Manual testing and validation.
-
** VERIFY [#C] page-signal pager account deregistered — re-registration needs your hands
:PROPERTIES:
:LAST_REVIEWED: 2026-06-12
:END:
Reported by .emacs.d 2026-06-12 01:01: the dedicated pager number (+15045173983, the Claude Pager Google Voice number on signal-cli) returns "User ... is not registered" on every send — Signal appears to have deregistered it (GV numbers get periodically re-verified). Re-registration requires captcha/SMS, which only you can do. Until then every page-signal call fails; .emacs.d's config-audit page fell back to email. Wrapper lives at claude-templates/bin/page-signal.
-** 2026-06-20 Sat @ 10:29:42 -0400 C-; b + arrow pulls a window away from a sole window
-Decided with Craig 2026-06-20: when the selected window is the sole window, =C-; b= + arrow keeps that window on the arrow's edge and slivers =other-buffer= in on the opposite side (=minimize-window=, so the current window keeps almost the whole frame), focus staying put; each further arrow then shrinks it step by step via =windsize=, reading the same as resizing an existing split. Generalizes to any sole window, not just terminals — resize was a no-op there before. Built in =modules/ui-navigation.el= (=cj/window-pull-side= pure mapping + =cj/window--pull-away= + a =one-window-p= branch in =cj/window-resize-sticky=). ERT tests for the mapping and both sticky paths; geometry verified in a headless frame (down -> terminal 37/40 at the bottom, reveal 2 lines slivered on top via window-min-height=1, windsize-down then steps it down); full suite green; live-reloaded into the daemon. Refined from a first cut that split toward the arrow and jumped to 50%, per Craig's feedback. Manual gesture check filed under Manual testing and validation.
-
** VERIFY [#C] Remove unused system-power keybindings :refactor:quick:next:
:PROPERTIES:
:LAST_REVIEWED: 2026-06-20
@@ -140,6 +117,29 @@ Fixed 2026-06-13: cmail gets =mu4e-trash-folder= "/cmail/Trash"; refile is a per
Fixed 2026-06-13: lockscreen-cmd resolves to =loginctl lock-session= on Wayland (logind Lock → hypridle → hyprlock, the path idle/sleep locking already uses), =slock= on X11; also added the missing =(require 'host-environment)=. Live in the daemon; manual lock test under the Manual testing parent.
** PROJECT [#A] Manual testing and validation
Exercised once the phases above land.
+*** VERIFY deferred game commands still work after a restart (load-graph Phase 4)
+What we're verifying: with games-config no longer eagerly required, malyon and 2048-game still launch from a fresh Emacs, and games-config loads on first use rather than at startup. Batch tests cover the autoload chain; this is the interactive confirmation the spec asks for after each deferral batch.
+- Restart Emacs (daemon or standalone) so games-config is no longer pre-loaded from this session.
+- Confirm it's not loaded at startup:
+#+begin_src emacs-lisp
+(featurep 'games-config)
+#+end_src
+- M-x malyon — it should load games-config and the malyon package, then start interactive fiction (stories under ~/sync/org/text.games/).
+- M-x 2048-game — should start the 2048 puzzle.
+- Re-check (featurep 'games-config) — now non-nil.
+Expected: at startup (featurep 'games-config) is nil; both commands launch normally; after invoking one, games-config is loaded. If a command errors instead of launching, capture it and reopen the deferral as a TODO.
+*** VERIFY native-comp + gcmh survive a daemon restart cleanly
+What we're verifying: re-enabling JIT native compilation and switching GC to gcmh holds up across a full daemon restart and a real work session. The fix is live in the current daemon and a throwaway daemon launched clean, but the already-loaded modules only get natively compiled on a fresh start (a background async burst), and the old "Selecting deleted buffer" race needs a real GUI session to rule out on 30.2.
+- Restart the Emacs daemon (clean state): kill it and start fresh, or reboot.
+- Use Emacs normally for a while — the first session after restart triggers background native compilation of ~100 modules. Watch for any "Selecting deleted buffer" errors or compilation crashes (check the *Async-native-compile-log* buffer and comp-warnings.log).
+- After things settle, confirm the settings are live:
+#+begin_src emacs-lisp
+(list :jit native-comp-jit-compilation
+ :gcmh gcmh-mode
+ :gcmh-high gcmh-high-cons-threshold)
+#+end_src
+- Edit normally (completion, agenda, AI buffers) and notice whether the periodic GC jank is gone.
+Expected: restart is clean (no backtrace); the background native-comp burst finishes without "Selecting deleted buffer" errors; the form returns (:jit t :gcmh t :gcmh-high 1073741824); editing feels smoother with no frequent GC pauses. If the async race recurs on 30.2, capture the error and reopen as a TODO — the fallback is an AOT sweep or going back to JIT-off.
*** VERIFY mu4e buffers are themed (headers, main, message view)
What we're verifying: with the mu4e modes excluded from global font-lock, mu4e's manual face properties survive, so the buffers pick up the theme. The headers + main + view-headers are the ones global font-lock was stripping.
- Restart Emacs (cleanest), or kill and reopen the mu4e buffers
@@ -347,6 +347,10 @@ What we're verifying: in dirvish, d now duplicates the file at point (delete-to-
- Answer no first (confirm nothing happens), then press D again and answer yes
- Note whether sudo prompts for a password and whether the file actually disappears
Expected: d duplicates; D names the exact targets and only deletes on yes; the files are gone with no trash copy. If sudo needs a password that shell-command can't supply, flag it — the delete may need to route through a tty instead.
+*** 2026-06-20 Sat @ 22:11:00 -0400 F9 agent toggle no longer shrinks after a C-; b pull-away
+Craig confirmed in his live GUI frame: the agent window keeps its height across repeated F9 toggles after a C-; b pull-away, even under the WIP theme's near-zero mode-line-inactive. The total-height capture/replay fix holds (dbee95ae).
+*** 2026-06-20 Sat @ 22:11:00 -0400 F9 toggle preserves all windows in a 3-window layout
+Craig confirmed in his live GUI frame: toggling the agent off then on in a 3-window layout returns the same three windows — both working windows survive and the agent re-splits its own bottom strip. The reversible-toggle fix holds (64916462).
** PROJECT [#A] Theme-Studio Open Work
Parent grouping the open theme-studio / theming issues; close each child independently.
@@ -808,7 +812,7 @@ No init.el load-order change — keybindings and the foundation modules already
Verified each fix with a fresh =emacs --batch (require 'X)=, then swept all ~100 modules standalone: every one loads or fails only with a clear missing-package message (the spec's Phase 2 exit bar). Full =make test=, =make validate-modules=, and an init smoke all pass. Module headers and the inventory's hidden-dependency section updated to mark the seven resolved.
-**** TODO [#A] Defer feature modules behind autoloads, hooks, and commands :refactor:
+**** DOING [#A] Defer feature modules behind autoloads, hooks, and commands :refactor:
Once dependencies are explicit, reduce the number of modules required at
startup. Start with lower-risk feature modules:
@@ -823,6 +827,9 @@ Do this incrementally. After each batch:
- Run =make test= or at least targeted tests.
- Check that keybindings still resolve and which-key labels still appear.
+***** 2026-06-21 Sun @ 01:53:55 -0400 Deferred games-config (batch 1, module 1)
+Replaced =(require 'games-config)= in init.el with explicit autoloads for =malyon= and =2048-game= → games-config; the module now loads on first game-command use instead of at startup. games-config.el: =:defer 1= → =:defer t :commands=, header Load shape eager→command. package.el already autoloads both commands, so routing through games-config only preserves the one setting it owns (=malyon-stories-directory=), applied via use-package =:config= when malyon loads. Verified the autoload→module→package→config chain in batch. Test: =tests/test-init-defer-games.el= (commands resolve with the module unloaded; config applies on load). Inventory row eager→command; header-contract 4/4 (still allowlisted), full =make test= green. Shipped as 03d8b587. Daemon keeps it loaded until restart — interactive restart smoke pending (see Manual testing).
+
**** 2026-05-24 Sun @ 19:59:01 -0500 Centralized custom keymap registration
Added cj/register-prefix-map and cj/register-command to keybindings.el (commit 47f222f6) with test-init-keymap-registration.el, then migrated all 31 cj/custom-keymap registration sites across 24 modules onto the API. Consumers no longer reference cj/custom-keymap directly — keybindings.el is the sole owner of the prefix, and modules require keybindings to reach the API.
@@ -894,66 +901,6 @@ Add the buffer-local var, set it on each "Run a test..." selection, use it as th
*** TODO [#B] TS/JS coverage status sync
Update the =dev-fkeys.el= header comment (L33) — TS/JS is no longer punted; the cmd-builder at L384 emits vitest/jest. Document the prefer-vitest fallback.
-** PROJECT [#B] Migrate All Terminals From Vterm to Ghostel
-:PROPERTIES:
-:LAST_REVIEWED: 2026-06-04
-:END:
-Replace vterm with ghostel (libghostty-vt) as the single terminal engine across every workflow, and rename ai-vterm → ai-term. References: [[file:docs/2026-05-25-emacs-terminal-comparison.org][docs/2026-05-25-emacs-terminal-comparison.org]] (vterm vs eat vs ghostel research); migration spec [[id:b54c94a0-d762-4b41-afd7-cf5593ce6675][docs/specs/vterm-to-ghostel-migration-spec-implemented.org]] (READY; external review incorporated 2026-06-04, D1-D7 agreed). Build in 5 phases (0-4); see the spec's Implementation tasks block.
-
-Decisions D1-D7 are settled in the spec's Agreed-decisions section. Build order below; each phase stays green (suite + byte-compile) at every step.
-
-*** TODO [#B] Follow-up: theme ghostel ANSI faces in dupre
-D2 — set the 16 ghostel-color-* + ghostel-default faces in dupre-faces/palette.
-Roam-inbox note (2026-06-14): theme-studio assignments don't reach ghostel — it paints from its own ANSI palette, not the theme. Also investigate ghostel's property-file color mechanism as an alternative and surface the options for working with that limitation.
-
-*** TODO [#B] Follow-up: evaluate ghostel-eshell + ghostel-compile
-D3 — ghostel-eshell as eshell visual backend; ghostel-compile against F4 dev-fkeys.
-
-*** TODO [#B] Investigate ghostel selection/highlight color
-Look at how selected text is highlighted in a ghostel buffer — the region face in =ghostel-copy-mode= and any live selection — surfaced during the copy-mode debugging. Check whether the highlight is legible against the dupre background and consistent with the rest of the config; if it needs theming, fold it in with D2 (theming the ghostel faces in dupre).
-
-*** 2026-06-04 Thu @ 23:57:09 -0500 Phase 0 done: characterization baseline green
-=make test= green except the 5 documented pre-existing failures (4 test-dupre-theme, 1 test-init-module-headers), none terminal-related. Characterization coverage already present + green for all six must-survive behaviors: vterm-toggle--dispatch/display/buffer-filter, vterm-tmux-history, ai-vterm--show-or-create/launch-command/f9-in-vterm, ui-config--buffer-cursor-state + vterm-copy-mode-cursor, dashboard-config-launchers. Add a characterization test before any behavior change in later phases if a gap appears.
-
-*** 2026-06-05 Fri @ 00:38:34 -0500 Phase 1 done: ghostel + term-config.el
-=modules/term-config.el= written (full port of vterm-config: tmux history/copy-mode-dwim preserved via process-tty-name + ghostel-send-string; F12 toggle + display rule + geometry; cj/term-map C-; x menu → ghostel commands; which-key "terminal menu"; ghostel-max-scrollback 10MB; C-; added to ghostel-keymap-exceptions; F12 + C-; in ghostel-mode-map; use-package ghostel guarded per D6). Dropped: mouse-wheel SGR forwarding, vterm-timer-delay hacks, copy-mode cursor hook, goto-address hook. ghostel installed into elpa (MELPA + auto-downloaded native module). Tests: test-term-toggle--{dispatch,display,buffer-filter} + test-term-tmux-history (16) ported with a ghostel stub in testutil-ghostel-buffers; all green.
-
-*** 2026-06-05 Fri @ 00:38:34 -0500 Phase 2 done: ai-vterm→ai-term on ghostel
-=modules/ai-vterm.el= → =modules/ai-term.el=: 6 vterm call sites swapped to ghostel (buffer named via let-bound ghostel-buffer-name + pinned ghostel-buffer-name-function so OSC titles don't rename agent buffers); F9/C-F9/M-F9 on global + ghostel-mode-map; refuse-in-terminal guard removed (D4 — F9 launches in TTY frames); tmux-suppression invariant preserved (cj/--ai-term-suppress-tmux). 23 ai-vterm tests renamed → test-ai-term--* (terminal-guard test deleted, obsolete); show-or-create + f9-in-term rewritten for ghostel; all green. ui-config cursor-state ported (ghostel-mode + ghostel--input-mode; copy/emacs = read-only, else writeable) + its test. init.el now requires term-config + ai-term; vterm-config.el + ai-vterm.el deleted. Full suite green except the 5 documented pre-existing failures (4 dupre-theme, 1 init-module-headers/popper-config-missing — both unrelated). validate-modules ✓; full early-init+init smoke clean (no ghostel/term/ai-term errors). vterm package still installed (Phase 4) — dashboard "Launch VTerm" + dormant auto-dim still reference it until Phase 3/4. Restart Emacs to pick up ghostel (load-order + use-package :config change).
-
-*** 2026-06-05 Fri @ 00:50:58 -0500 Phase 3 done: satellites ported to ghostel
-Deleted auto-dim's vterm color-advice + redraw integration (~165 lines; D1 — terminals don't dim, ghostel bakes its palette per-terminal so there's no per-window color hook); dashboard launcher → =(ghostel)= + "Launch Terminal" label; cj-window-geometry/toggle-lib doc comments; module-inventory + init-load-graph doc refs. (ui-config cursor-state + init.el requires landed in Phase 2.) Trimmed test-auto-dim-config (dropped the 6 vterm tests) + updated the dashboard-launcher test stub. Incidental: removed the stale =popper-config= entry from the test-init-module-headers allowlist (the file doesn't exist + isn't required) — fixes the long-standing pre-existing test failure.
-
-*** 2026-06-05 Fri @ 00:50:58 -0500 Phase 4 done: vterm + vterm-toggle removed
-=package-delete='d vterm + vterm-toggle from elpa. No vterm refs remain in modules/init except intentional historical comments. Suite green except the 4 pre-existing dupre-theme failures (the popper-config one is now fixed). validate-modules ✓; full early-init+init batch smoke = INIT-SMOKE-OK. The migration parent stays DOING until Craig restarts Emacs and walks the ghostel manual-verify matrix under "Emacs Manual Testing and Validation".
-
-*** 2026-06-05 Fri @ 14:24:02 -0500 Auto-dim revisit cancelled — current no-dim behavior is fine
-Craig confirmed the shipped auto-dim setup works fine as-is: terminal buffers don't participate in unfocused-window dimming (D1), and the rest of auto-dim behaves. That is the measured decision the original task asked for — option (a), keep no-dim — so no rework (the focus-loss palette-blend in option (b) or an upstream per-window hook in option (c)) is needed. Closing without further investigation. Context: [[id:b54c94a0-d762-4b41-afd7-cf5593ce6675][migration spec]] D1.
-
-*** 2026-05-26 Tue @ 15:15:43 -0500 Direction confirmed; Claude Code in eat needs a caveat
-Craig confirmed the consolidation: one terminal engine everywhere — eat for standalone terminal buffers (replacing vterm) plus =eat-eshell-mode= as eshell's visual backend, keeping eshell as the shell. Not dropping eshell for eat + zsh.
-
-Researched whether Claude Code runs cleanly in eat (Craig runs it in his Emacs terminal). Verdict: mostly, with caveats. eat is the default backend for claude-code.el and renders the TUI with color and full key handling, but there is an eat-specific bug where Claude Code's input handling makes the buffer scroll-pop to the top on window-buffer changes and the input box can get stuck mid-buffer (recoverable, but it does not happen in vterm or ghostel), and eat runs about 1.5x slower than vterm on heavy streaming output. claude-code.el's own docs name ghostel as the most faithful Claude TUI renderer.
-
-Recommendation: consolidate everyday terminals onto eat, but keep ghostel (or vterm) for the Claude Code workflow specifically — the scroll-pop / stuck-input bug and the slower heavy-stream handling are exactly what bites a long Claude session. Sources: [[https://github.com/cpoile/claudemacs][claudemacs]], [[https://github.com/stevemolitor/claude-code.el][claude-code.el]], [[https://codeberg.org/akib/emacs-eat][emacs-eat]].
-
-Eval plan (from the research doc): install EAT alongside vterm, run the same workloads through both, decide. Test matrix: Claude Code TUI, lazygit, htop/btop, yazi, a heavy-output build, ssh to a remote, and eshell with =eat-eshell-mode=. Assess rendering fidelity, stability under heavy output, and Emacs-native line editing. Switch only if it covers every workflow without regression.
-
-*** 2026-06-02 Tue @ 14:12:48 -0500 Audit: eval plan not yet run; back to TODO
-Task audit found no eval work recorded since the 2026-05-26 direction-confirmed note. The test matrix above is unrun, so the task isn't actively in progress — moved DOING back to TODO until the eval starts.
-
-*** 2026-06-04 Thu @ 22:40:27 -0500 Pivot: ghostel as the single engine (not eat)
-Direction changed from eat-everyday + ghostel-for-Claude to ghostel-for-everything, and the task is now a migration rather than an eval. Rationale: ghostel is claude-code.el's most-faithful Claude 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 (=ghostel-send-string=, =ghostel-keymap-exceptions=, =ghostel-copy-mode=, =ghostel-clear-scrollback=, =ghostel-send-next-key=, =ghostel-next-prompt= / =ghostel-previous-prompt=, =ghostel-max-scrollback=, =ghostel-kill-buffer-on-exit=). eat's washed colors, the scroll-pop / stuck-input bug under Claude Code, and slowest throughput made it the weaker single-engine pick; one engine beats running two. Surface audited: 2 main modules (=vterm-config.el=, =ai-vterm.el=) + 4 satellites (=auto-dim-config.el= is the heavy one) + ~35 test files + init.el. Next: spike ghostel read-only to answer the open migration questions (auto-dim rework — ARCHITECTURE.md forbids the around-redraw color advice vterm uses; tmux pane-id via =process-tty-name= on a ghostel process; buffer naming; TTY-frame behavior; copy-mode keybinding parity), then write the migration spec under =docs/design/= and review it.
-
-*** 2026-06-04 Thu @ 23:17:54 -0500 Spec review: not ready until decisions and handoff shape are closed
-Ran the spec-review workflow against [[id:b54c94a0-d762-4b41-afd7-cf5593ce6675][docs/specs/vterm-to-ghostel-migration-spec-implemented.org]] and wrote a companion review file (incorporated and deleted 2026-06-04). Verdict: =Not ready=. Direction is sound, but the draft still has open D1-D5 decisions, lacks the workflow-required =Implementation phases= section and acceptance criteria, and needs explicit ghostel package/native-module failure behavior before implementation tasks can be emitted.
-
-*** 2026-06-04 Thu @ 23:24:28 -0500 Spec-response: review incorporated, raised to READY
-Folded the external review via spec-response. Craig accepted D1-D5; baked them plus D6 (module-failure = degrade-with-warning, modifying the reviewer's fail-loud) and D7 (=ghostel-max-scrollback= 10 MB) into a new Agreed-decisions section. Added Implementation phases (0-4), Acceptance criteria, Dependency/module-failure behavior, Test strategy, per-phase key/menu ownership, the tmux-suppression contract, and an Implementation-tasks drop-in block. Status DRAFT → READY; review file deleted. Build is now unblocked.
-
-*** 2026-06-04 Thu @ 23:30:18 -0500 External re-review: ready
-Re-reviewed [[id:b54c94a0-d762-4b41-afd7-cf5593ce6675][docs/specs/vterm-to-ghostel-migration-spec-implemented.org]] after incorporation. Verdict: =Ready=. No further blocking review notes; implementation can start from the phase plan and acceptance criteria in the spec.
-
** PROJECT [#B] Module-by-module hardening
:PROPERTIES:
:LAST_REVIEWED: 2026-06-05
@@ -3453,23 +3400,42 @@ Restart the daemon, open a GUI frame, trigger an encrypted decrypt, confirm =pin
*** TODO [#C] Archive the original L3813 task
After this work lands, mark the original "Finish terminal GPG pinentry configuration" task DONE with a =CLOSED:= stamp and a one-line note pointing at this parent task.
-** TODO [#A] Unified popup placement and dismissal rules :feature:
-All transient popups should follow one set of principles. Placement: when the Emacs frame is wider than tall, the popup rises from the right; when square or taller, from the bottom — settle the aspect-ratio threshold and the pop-out percentage. Dismissal: C-c C-c when there's an accept action, C-c C-k when there's a cancel, otherwise =q= closes the window. This generalizes two existing tasks — ai-term adaptive placement (the aspect-ratio docking) and the messenger window/key unification spec (the C-c C-c / C-c C-k dismissal) — into one config-wide policy. From the roam inbox.
-
-** TODO [#A] Unify Signel and All Messengers into one UX :feature:
+** TODO [#A] Unified popup and messenger UX — placement, dismissal, one library :feature:
:PROPERTIES:
-:LAST_REVIEWED: 2026-06-16
+:LAST_REVIEWED: 2026-06-20
:END:
+Merged 2026-06-20 from the config-wide popup-policy task and the messenger-unification
+task — they're the same policy at two scopes (the messenger windows are the first
+concrete application of the general popup rules). Two parts:
+
+(A) Config-wide popup policy. All transient popups follow one set of principles.
+Placement: when the Emacs frame is wider than tall, the popup rises from the right;
+when square or taller, from the bottom — settle the aspect-ratio threshold and the
+pop-out percentage. Dismissal: C-c C-c when there's an accept action, C-c C-k when
+there's a cancel, otherwise =q= closes the window. Generalizes ai-term adaptive
+placement (the aspect-ratio docking) and the messenger window/key rules below into
+one config-wide policy. From the roam inbox.
+
+(B) Messenger unification (first application of the policy above).
Spec: [[file:docs/specs/messenger-unification-spec.org][messenger-unification-spec.org]] ([[id:4bfc2011-8ffc-4765-8886-91df12141171][by id]], Draft, 2026-06-11; keybinding-alphabet section + smoke-first parity added 2026-06-16). One library (=cj-messenger-lib.el=) gives every messenger the same shape: chat windows rise from the bottom (the signel rule, generalized), C-c C-c confirms, C-c C-k cancels, C-c C-a attaches — dispatched per backend through a registry + minor mode. Signel already conforms (reference backend); telega and slack join in phases 2-3; ERC later. All eight decisions settled 2026-06-11 (cancel closes an idle window; telega's filter-cancel shadow accepted; slack rooms join the bottom rule). Spec held open — Craig has more ideas to fold in before it's marked Ready.
** TODO [#B] agenda sources: roam Projects missing, no existence filtering :bug:solo:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-06-20
+:END:
From the 2026-06 config audit, =modules/org-agenda-config.el=:
- =:182-191= — commentary and docstrings promise org-roam nodes tagged "Project" as agenda sources, but =cj/--org-agenda-scan-files= never scans them, and files added by the roam finalize-hook are wiped on the next =cj/build-org-agenda-list= cache rebuild (≤1h). Add a roam Project pass (mirror =org-refile-config.el:101-109=) or correct the docs.
- =:186,456= — agenda file list built unconditionally (inbox/calendars may not exist on a fresh machine) and =org-agenda-skip-unavailable-files= is unset — the exact interactive-prompt class that once hung the chime daemon. Filter with =file-exists-p= + set the var as backstop.
** TODO [#B] Auto-dim: org headings, links, and tags do not dim in unfocused windows :bug:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-06-20
+:END:
auto-dim-other-buffers-affected-faces (auto-dim-config.el) remaps font-lock and a few org faces to the flat dim face, but not org-level-1..8, org-link, or org-tag, so headings, links (seen in daily-prep.org), and tags like :solo: stay lit when the window loses focus. Decide the dim approach: a flat-dim remap like font-lock (quick) versus dedicated -dim variants surfaced through org-faces / theme-studio (richer, matches the keyword work; Craig flagged org-tags may want the org-faces treatment). Consolidates three roam-inbox captures.
** TODO [#B] "? = curated help menu" convention across modes :feature:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-06-20
+:END:
From the calibredb keybindings work 2026-06-06. The pattern that worked: in a modal/major-mode buffer (calibredb), bind =?= to a curated transient of the frequent workflows, and move the package's own full dispatch to =H=. It fixes the "I can't discover the keys" problem that which-key can't help with (which-key only pops up after a prefix, not for top-level single keys in a mode-map).
Task: survey the modes/modules Craig works in and identify where a =?= -> curated-help-menu (transient) makes sense. Candidates: any major-mode buffer with single-key bindings and no good discovery affordance -- calibredb (done), nov, dirvish, mu4e, ghostel/term, signel, pearl/linear, ELFeed, etc. For each, note whether =?= is free or already a help dispatch, and whether a curated menu (vs the package's own) adds value. Establish it as a convention (and maybe a small helper/macro to define a curated =?= menu consistently).
@@ -3487,12 +3453,24 @@ Ask:
Reference values -- modus-vivendi: refine-changed bg #4a4a00 fg #efef80, changed bg #363300 fg #efef80. modus-operandi: refine-changed bg #fac090 fg #553d00, changed bg #ffdfa9 fg #553d00.
Side-by-side legibility render: [[file:assets/2026-06-07-dupre-diff-face-legibility-compare.png][assets/2026-06-07-dupre-diff-face-legibility-compare.png]].
-** TODO [#B] erc-yank silently publishes >5-line pastes as public gists :bug:
-=modules/erc-config.el:345= — C-y in any ERC buffer auto-creates a public gist for anything over 5 lines: clipboard content goes to a public URL with no confirmation, and no executable-find guard for =gist= (errors mid-send if absent). Privacy trap. Add a =yes-or-no-p= gate or drop the package for plain C-y. From the 2026-06 config audit.
-
** TODO [#B] F7 diff-aware coverage classifies every changed file "not tracked" :bug:solo:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-06-20
+:END:
=modules/coverage-core.el:252= — =cj/--coverage-intersect= joins covered×changed by exact string key, but simplecov.json keys are ABSOLUTE paths while the git-diff parser returns repo-RELATIVE ones — zero matches ever, so working-tree/staged/branch scopes report ":tracked nil" for everything and F7's main feature is inert (whole-project scope works, same-source keys). Unit tests hand-build matching keys so they pass; add one integration test feeding a real undercover report + real diff. Normalize both sides to repo-relative. From the 2026-06 config audit.
+** TODO [#C] Migrate tests off mocking primitives (native-comp robustness) :test:refactor:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-06-21
+:END:
+Long-term test-quality work surfaced by re-enabling native-comp (2026-06-20). When a test redefines a C primitive or a native-compiled function (=cl-letf=/=fset=/=advice-add=), native-comp routes native callers through a trampoline, which interacts badly with mocks in three ways: trampoline-build failure under =--batch=, silent mock-bypass (native callers ignore the redefinition), and arity mismatch (the trampoline calls the mock with the primitive's max arity).
+
+Done 2026-06-21 (the immediate fix): swept every arity-narrow subr mock to =(lambda (&rest _) ...)= (188 sites) and added =tests/test-meta-subr-mock-arity.el=, which fails =make test= on any arity-narrow subr mock. That kills the arity mode (the only one we've hit) and enforces it going forward.
+
+This task is the durable fix the ecosystem and =elisp-testing.md= point to: restructure tests so they don't redefine primitives at all — inject dependencies, drive real state (temp-file fixtures, real buffers), or test pure helpers. That closes the two latent modes (build-failure, silent-bypass) the variadic sweep leaves open. Big, incremental, low-urgency.
+
+Full mechanism, the three failure modes, the research (Emacs bug#51140, bug#61880, buttercup #230, Debian #1021842, the emacs-29 redefine-primitive warning, the manual on =native-comp-enable-subr-trampolines=), and the decision: [[file:docs/native-comp-subr-mocking.org][docs/native-comp-subr-mocking.org]].
+
** TODO [#B] Fix up test runner :bug:
:PROPERTIES:
:LAST_REVIEWED: 2026-06-06
@@ -8605,3 +8583,189 @@ Dump from the live daemon by default (reflects the packages actually run); the b
** DONE [#C] todo.org org-lint follow-ups :refactor:
CLOSED: [2026-06-20 Sat]
From the lint-org sweeps (2026-06-15, refreshed 2026-06-20). Resolved 2026-06-20: the misplaced-heading false positive was reworded (the bug-capture task's prose quoted heading-like "* TODO" strings), and the broken link was repointed from the missing =~/code/signel/todo.org= to =~/code/smoke/todo.org= (smoke is the evolved Signal package). The obsolete-properties-drawer entries no longer reproduce under a full org-lint pass. Both lint-org --check and the built-in org-lint now report zero.
+** DONE [#B] F9 toggle collapses a 3-window layout to 2 :bug:
+CLOSED: [2026-06-20 Sat]
+Fixed 2026-06-20 (option 1 — reversible toggle, Craig's call). In a 3+ window layout where
+the agent had its own split, toggle-on reused the working window at the bottom edge,
+displacing its buffer and collapsing three windows to two. Added a flag
+(=cj/--ai-term-last-toggle-deleted-split=) set when toggle-off delete-windows the agent's own
+window; =cj/--ai-term-reuse-edge-window= consumes it and falls through to a fresh re-split, so
+the agent returns to its own window and the others are untouched. The flag only changes the 3+
+window case (2-window slot-reuse unchanged). TDD regression
+=test-ai-term--reuse-edge-window-3win-toggle-restores-own-window=; full =make test= green;
+live-reloaded. Commit 64916462. GUI sign-off is a VERIFY under Manual testing and validation.
+** DONE [#B] Codebase refactoring program — remaining batch :refactor:solo:
+CLOSED: [2026-06-20 Sat]
+Complete 2026-06-20: all 13 scan findings addressed across the day's sessions (see
+=.ai/sessions/= for the logs). 5 medium extractions + 2 big single-file refactors +
+6 theme-studio items including the browser-gates harness rewrite. The only item not
+done is the item-8 plan() factory, consciously skipped as premature abstraction
+(heterogeneous call sites — see "Remaining — item-8 plan() factory" below).
+The original scan: full-codebase 8-agent fan-out over modules/ + scripts/theme-studio/,
+one focused refactor per commit, won't-do items excluded.
+
+*** Working protocol (apply to every item)
+- TDD: write/keep a failing-then-green test; harvest new test seams the refactor opens.
+- Behavior-preserving only. If a "dedup" would delete a real test seam or couple
+ dissimilar code, SKIP it and record why (see skips below).
+- Per refactor, verify in this order, then commit + push (no-approvals mode):
+ 1. =make test-file FILE=<basename.el>= for touched + new tests.
+ 2. =make validate-modules= (loads all 123 modules; catches load/paren errors).
+ 3. Init-launch smoke on a throwaway daemon: =emacs --daemon=cj-sNN=, then
+ =emacsclient -s cj-sNN -e '(emacs-pid)'= to capture the PID, check
+ =(length features)= = 807 and no init errors in the log, then kill by that
+ PID (the emacsclient kill-emacs is flaky; pkill -f 'daemon=cj-sNN'
+ self-matches its own shell — kill the captured PID).
+ 4. Live-reload the edited module into Craig's running daemon
+ (=emacsclient -e '(load "/home/cjennings/.emacs.d/modules/<m>.el")'=); skip
+ the live reload for big use-package modules whose :config restacks (verify via
+ the fresh smoke daemon instead, as with mail-config).
+- Tab-heavy files: =sed -n 'A,Bp' FILE | cat -A= to get exact bytes before an Edit;
+ write NEW code in the documented 2-space style.
+- Shared asset already created: =cj/format-region-with-program= in system-lib.el
+ (the run-a-formatter-over-the-buffer helper). Reuse it for any further
+ format-region duplicates.
+
+*** DONE — medium extractions (2026-06-20 afternoon)
+All five shipped: calibredb-epub nov re-render/centering helpers (fccf29b0);
+ai-term toggle-off teardown + working-buffer swap (62fee96b); calendar-sync
+per-event exception parser (23f405b4); dirvish playlist-target resolution
+(a1ca2fb0); custom-case per-word title-case decision (4cc9ca0b).
+
+*** DONE — big single-file + theme-studio (2026-06-20 afternoon, no-approvals run)
+Both big single-file items shipped: dwim-shell branching command builders
+(f93b4615); custom-comments divider/box generator dedup (42f0c88a). Five of the
+six theme-studio items shipped: face_coverage path_kind (9a52370b),
+capture-default-faces condition_matches unify (28b4d1cf), dropdownRowTextColor
+delete (10a56789), test-file inline-integrity dedup — subTest loop + shared
+inline-strip.mjs (13969c70), generate.py lazy _build()/__getattr__ (6df4ebdc),
+browser-gates assertPreviewFaces for the 3 preview gates (5627f137).
+
+*** DONE — browser-gates harness rewrite (with Craig's go-ahead, 2026-06-20)
+- =gate(id, body)= helper (05697e83): the 38 standard gates' ok/notes/A + title +
+ result-div boilerplate, note format standardized to " fails=". Each call site keeps
+ its literal =location.hash==='#NAMEtest'=. 6 custom gates stay inline. First automated
+ attempt deleted gates (a closing-finder spanned boundaries) — caught by a gate-count
+ guard, reverted, redone anchored on each gate's unique =d.id=. Verified all 44 green +
+ a forced A(false) in a converted gate still FAILs.
+- =withSavedState(keys, body)= (a473aa7c): wraps the 7 restore-nothing gates, scoped to
+ the globals each mutates; JSON-clone snapshot + finally-restore (structuredClone threw
+ on the studio objects — caught by the gate run as "no verdict", switched to JSON like
+ the gates' own local saves). The 14 self-restoring gates left as-is. Verified 44 green,
+ restore round-trip holds, broken assertion in a wrapped gate still FAILs.
+
+*** Remaining — item-8 plan() factory (deferred, low value)
+The =plan(overrides)= factory for the ~30 planPaletteGenerator calls (test-app-core.mjs
++ test-palette-generator-core.mjs) was deferred. The calls pass heterogeneous options
+(scheme/accentCount/sourceMode/vibe/intent vary per call); a factory only dedups the
+constant spanCount:0/rng and would hide which options each test actually exercises —
+premature abstraction over varying calls. The other two item-8 parts (subTest loop +
+shared stripExports) shipped in 13969c70.
+
+*** WON'T-DO (do not re-attempt — assessed and rejected)
+- theme-studio buildTable/buildUITable/buildPkgTable merge: genuine per-tier divergence
+ (column order, syntax dual fg/bg dropdowns, ui preview cell, pkg nd markers) + the
+ =.cells[N]= positional sort coupling make a unified builder MORE complex than the
+ three explicit ones. Close as won't-do.
+- Cross-language test overlap (browser-gates preview gate vs test_generate.py
+ PackageFaceCoverage): don't merge — would couple a fast Python test to a headless
+ browser run. A one-line comment in each noting the split is the most that's worth it.
+
+*** Skipped this run (with reasons — don't redo)
+- eshell-config ssh-alias "merge the two helpers": =cj/--eshell-ssh-alias-commands= is
+ a deliberate pure/effectful split with 3 dedicated tests; merging deletes the seam.
+- prog-*-setup boilerplate: only python+webdev share the full pattern; shell/c/elisp/
+ common-lisp differ materially. A keyword-arg helper would be less readable. No
+ premature abstraction.
+- erc join-command =cj/erc--ensure-active-connection= extraction: nesting-only on
+ untestable UI (call-interactively/switch-to-buffer), no test seam, risky tab-rewrite.
+- coverage-core =simplecov-executable-lines= vs =parse-simplecov= clone: borderline
+ MEDIUM, differs only by a =(> hits 0)= predicate; parameterize with a keep-line-p
+ only if revisiting. Low priority.
+** CANCELLED [#A] calendar-sync drops final occurrences, resurrects cancelled meetings :bug:solo:next:
+CLOSED: [2026-06-20 Sat 22:51]
+:PROPERTIES:
+:LAST_REVIEWED: 2026-06-13
+:END:
+Needs from Craig: a real .ics fixture (or two) that reproduces both symptoms — a recurring event missing its final occurrence, and a cancelled meeting that reappears. This is RFC-5545 recurrence handling (RRULE/UNTIL/EXDATE/STATUS:CANCELLED); I won't guess-patch the parser without a failing case to test against. Drop a sanitized .ics and I'll write the characterization test + fix.
+RFC 5545 conformance holes in =modules/calendar-sync.el=, all agenda-visible (from the 2026-06 config audit):
+- =:973,1015,1024= — UNTIL treated as exclusive (strict =calendar-sync--before-date-p=); RFC and Google make it inclusive, so the LAST instance of every UNTIL-bounded series vanishes. Tests assert loose count ranges, so it's unpinned. Allow equality.
+- =:578= — comma-separated EXDATE lists (Google emits them) never parse; the exclusion drops silently and cancelled occurrences reappear on the agenda. Split on "," before parsing; no comma-case test exists.
+- =:902= — timed events without DTEND render as all-day (time lost); multi-day all-day spans collapse to one day (end date unused, exclusive-DTEND unhandled). Emit start-time-only stamps and org date ranges.
+-----
+
+2026-06-20 Sat @ 22:52:51 -0400 Can't reproduce. closing
+** DONE [#A] Native compilation disabled config-wide; GC at stock 800KB :bug:next:
+CLOSED: [2026-06-20 Sat]
+Both fixed 2026-06-20. =early-init.el:69= was =(setq native-comp-deferred-compilation nil)= — the obsolete alias of =native-comp-jit-compilation= — which turned JIT native-comp OFF entirely (not "synchronous"); replaced with =(setq native-comp-jit-compilation t)= + =native-comp-async-report-warnings-errors 'silent=. The old "Selecting deleted buffer" async race was an Emacs 28/29 issue; this is 30.2. GC: dropped the early-init post-startup restore to stock 800KB and the system-defaults minibuffer setup/exit hooks, replaced with gcmh (idle-delay 'auto, 1GB high threshold) — keeps the threshold high during activity, collects on idle. Verified via a clean throwaway-daemon launch (native-comp-jit t, gcmh-mode t, no backtrace) and a batch proof of gcmh's threshold cycle; applied live to the running daemon. Restart confirmation filed under Manual testing and validation.
+** DONE [#C] Dirvish: free D for hard-delete, move duplicate :feature:quick:next:
+CLOSED: [2026-06-20 Sat]
+Decided with Craig 2026-06-20: remove delete-to-trash entirely, bind =d= = =cj/dirvish-duplicate-file= and =D= = =cj/dirvish-hard-delete= (sudo rm -rf after a =yes-or-no-p= naming the exact targets). Built in =modules/dirvish-config.el= (=cj/--dirvish-hard-delete-command= pure builder + =cj/dirvish-hard-delete= command; keymap =d=/=D= swap). 4 ERT tests for the command builder; full suite green; live-reloaded into the daemon (=dirvish-mode-map= =d=/=D= rebinding confirmed). Manual keypress + sudo-flow check filed under Manual testing and validation.
+** DONE [#C] Pull a fullscreen terminal window away with C-; b + arrow :feature:next:
+CLOSED: [2026-06-20 Sat]
+Decided with Craig 2026-06-20: when the selected window is the sole window, =C-; b= + arrow keeps that window on the arrow's edge and slivers =other-buffer= in on the opposite side (=minimize-window=, so the current window keeps almost the whole frame), focus staying put; each further arrow then shrinks it step by step via =windsize=, reading the same as resizing an existing split. Generalizes to any sole window, not just terminals — resize was a no-op there before. Built in =modules/ui-navigation.el= (=cj/window-pull-side= pure mapping + =cj/window--pull-away= + a =one-window-p= branch in =cj/window-resize-sticky=). ERT tests for the mapping and both sticky paths; geometry verified in a headless frame (down -> terminal 37/40 at the bottom, reveal 2 lines slivered on top via window-min-height=1, windsize-down then steps it down); full suite green; live-reloaded into the daemon. Refined from a first cut that split toward the arrow and jumped to 50%, per Craig's feedback. Manual gesture check filed under Manual testing and validation.
+** DONE [#B] Migrate All Terminals From Vterm to Ghostel
+CLOSED: [2026-06-20 Sat 22:50]
+:PROPERTIES:
+:LAST_REVIEWED: 2026-06-04
+:END:
+Replace vterm with ghostel (libghostty-vt) as the single terminal engine across every workflow, and rename ai-vterm → ai-term. References: [[file:docs/2026-05-25-emacs-terminal-comparison.org][docs/2026-05-25-emacs-terminal-comparison.org]] (vterm vs eat vs ghostel research); migration spec [[id:b54c94a0-d762-4b41-afd7-cf5593ce6675][docs/specs/vterm-to-ghostel-migration-spec-implemented.org]] (READY; external review incorporated 2026-06-04, D1-D7 agreed). Build in 5 phases (0-4); see the spec's Implementation tasks block.
+
+Decisions D1-D7 are settled in the spec's Agreed-decisions section. Build order below; each phase stays green (suite + byte-compile) at every step.
+
+*** 2026-06-20 Sat @ 22:49:41 -0400 Follow-up: theme ghostel ANSI faces in dupre
+D2 — set the 16 ghostel-color-* + ghostel-default faces in dupre-faces/palette.
+Roam-inbox note (2026-06-14): theme-studio assignments don't reach ghostel — it paints from its own ANSI palette, not the theme. Also investigate ghostel's property-file color mechanism as an alternative and surface the options for working with that limitation.
+
+*** 2026-06-20 Sat @ 22:50:28 -0400 CANCELLED [#B] Follow-up: evaluate ghostel-eshell + ghostel-compile
+CLOSED: [2026-06-20 Sat 22:49]
+D3 — ghostel-eshell as eshell visual backend; ghostel-compile against F4 dev-fkeys.
+
+*** 2026-06-20 Sat @ 22:50:32 -0400 DONE [#B] Investigate ghostel selection/highlight color
+CLOSED: [2026-06-20 Sat 22:50]
+Look at how selected text is highlighted in a ghostel buffer — the region face in =ghostel-copy-mode= and any live selection — surfaced during the copy-mode debugging. Check whether the highlight is legible against the dupre background and consistent with the rest of the config; if it needs theming, fold it in with D2 (theming the ghostel faces in dupre).
+
+*** 2026-06-04 Thu @ 23:57:09 -0500 Phase 0 done: characterization baseline green
+=make test= green except the 5 documented pre-existing failures (4 test-dupre-theme, 1 test-init-module-headers), none terminal-related. Characterization coverage already present + green for all six must-survive behaviors: vterm-toggle--dispatch/display/buffer-filter, vterm-tmux-history, ai-vterm--show-or-create/launch-command/f9-in-vterm, ui-config--buffer-cursor-state + vterm-copy-mode-cursor, dashboard-config-launchers. Add a characterization test before any behavior change in later phases if a gap appears.
+
+*** 2026-06-05 Fri @ 00:38:34 -0500 Phase 1 done: ghostel + term-config.el
+=modules/term-config.el= written (full port of vterm-config: tmux history/copy-mode-dwim preserved via process-tty-name + ghostel-send-string; F12 toggle + display rule + geometry; cj/term-map C-; x menu → ghostel commands; which-key "terminal menu"; ghostel-max-scrollback 10MB; C-; added to ghostel-keymap-exceptions; F12 + C-; in ghostel-mode-map; use-package ghostel guarded per D6). Dropped: mouse-wheel SGR forwarding, vterm-timer-delay hacks, copy-mode cursor hook, goto-address hook. ghostel installed into elpa (MELPA + auto-downloaded native module). Tests: test-term-toggle--{dispatch,display,buffer-filter} + test-term-tmux-history (16) ported with a ghostel stub in testutil-ghostel-buffers; all green.
+
+*** 2026-06-05 Fri @ 00:38:34 -0500 Phase 2 done: ai-vterm→ai-term on ghostel
+=modules/ai-vterm.el= → =modules/ai-term.el=: 6 vterm call sites swapped to ghostel (buffer named via let-bound ghostel-buffer-name + pinned ghostel-buffer-name-function so OSC titles don't rename agent buffers); F9/C-F9/M-F9 on global + ghostel-mode-map; refuse-in-terminal guard removed (D4 — F9 launches in TTY frames); tmux-suppression invariant preserved (cj/--ai-term-suppress-tmux). 23 ai-vterm tests renamed → test-ai-term--* (terminal-guard test deleted, obsolete); show-or-create + f9-in-term rewritten for ghostel; all green. ui-config cursor-state ported (ghostel-mode + ghostel--input-mode; copy/emacs = read-only, else writeable) + its test. init.el now requires term-config + ai-term; vterm-config.el + ai-vterm.el deleted. Full suite green except the 5 documented pre-existing failures (4 dupre-theme, 1 init-module-headers/popper-config-missing — both unrelated). validate-modules ✓; full early-init+init smoke clean (no ghostel/term/ai-term errors). vterm package still installed (Phase 4) — dashboard "Launch VTerm" + dormant auto-dim still reference it until Phase 3/4. Restart Emacs to pick up ghostel (load-order + use-package :config change).
+
+*** 2026-06-05 Fri @ 00:50:58 -0500 Phase 3 done: satellites ported to ghostel
+Deleted auto-dim's vterm color-advice + redraw integration (~165 lines; D1 — terminals don't dim, ghostel bakes its palette per-terminal so there's no per-window color hook); dashboard launcher → =(ghostel)= + "Launch Terminal" label; cj-window-geometry/toggle-lib doc comments; module-inventory + init-load-graph doc refs. (ui-config cursor-state + init.el requires landed in Phase 2.) Trimmed test-auto-dim-config (dropped the 6 vterm tests) + updated the dashboard-launcher test stub. Incidental: removed the stale =popper-config= entry from the test-init-module-headers allowlist (the file doesn't exist + isn't required) — fixes the long-standing pre-existing test failure.
+
+*** 2026-06-05 Fri @ 00:50:58 -0500 Phase 4 done: vterm + vterm-toggle removed
+=package-delete='d vterm + vterm-toggle from elpa. No vterm refs remain in modules/init except intentional historical comments. Suite green except the 4 pre-existing dupre-theme failures (the popper-config one is now fixed). validate-modules ✓; full early-init+init batch smoke = INIT-SMOKE-OK. The migration parent stays DOING until Craig restarts Emacs and walks the ghostel manual-verify matrix under "Emacs Manual Testing and Validation".
+
+*** 2026-06-05 Fri @ 14:24:02 -0500 Auto-dim revisit cancelled — current no-dim behavior is fine
+Craig confirmed the shipped auto-dim setup works fine as-is: terminal buffers don't participate in unfocused-window dimming (D1), and the rest of auto-dim behaves. That is the measured decision the original task asked for — option (a), keep no-dim — so no rework (the focus-loss palette-blend in option (b) or an upstream per-window hook in option (c)) is needed. Closing without further investigation. Context: [[id:b54c94a0-d762-4b41-afd7-cf5593ce6675][migration spec]] D1.
+
+*** 2026-05-26 Tue @ 15:15:43 -0500 Direction confirmed; Claude Code in eat needs a caveat
+Craig confirmed the consolidation: one terminal engine everywhere — eat for standalone terminal buffers (replacing vterm) plus =eat-eshell-mode= as eshell's visual backend, keeping eshell as the shell. Not dropping eshell for eat + zsh.
+
+Researched whether Claude Code runs cleanly in eat (Craig runs it in his Emacs terminal). Verdict: mostly, with caveats. eat is the default backend for claude-code.el and renders the TUI with color and full key handling, but there is an eat-specific bug where Claude Code's input handling makes the buffer scroll-pop to the top on window-buffer changes and the input box can get stuck mid-buffer (recoverable, but it does not happen in vterm or ghostel), and eat runs about 1.5x slower than vterm on heavy streaming output. claude-code.el's own docs name ghostel as the most faithful Claude TUI renderer.
+
+Recommendation: consolidate everyday terminals onto eat, but keep ghostel (or vterm) for the Claude Code workflow specifically — the scroll-pop / stuck-input bug and the slower heavy-stream handling are exactly what bites a long Claude session. Sources: [[https://github.com/cpoile/claudemacs][claudemacs]], [[https://github.com/stevemolitor/claude-code.el][claude-code.el]], [[https://codeberg.org/akib/emacs-eat][emacs-eat]].
+
+Eval plan (from the research doc): install EAT alongside vterm, run the same workloads through both, decide. Test matrix: Claude Code TUI, lazygit, htop/btop, yazi, a heavy-output build, ssh to a remote, and eshell with =eat-eshell-mode=. Assess rendering fidelity, stability under heavy output, and Emacs-native line editing. Switch only if it covers every workflow without regression.
+
+*** 2026-06-02 Tue @ 14:12:48 -0500 Audit: eval plan not yet run; back to TODO
+Task audit found no eval work recorded since the 2026-05-26 direction-confirmed note. The test matrix above is unrun, so the task isn't actively in progress — moved DOING back to TODO until the eval starts.
+
+*** 2026-06-04 Thu @ 22:40:27 -0500 Pivot: ghostel as the single engine (not eat)
+Direction changed from eat-everyday + ghostel-for-Claude to ghostel-for-everything, and the task is now a migration rather than an eval. Rationale: ghostel is claude-code.el's most-faithful Claude 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 (=ghostel-send-string=, =ghostel-keymap-exceptions=, =ghostel-copy-mode=, =ghostel-clear-scrollback=, =ghostel-send-next-key=, =ghostel-next-prompt= / =ghostel-previous-prompt=, =ghostel-max-scrollback=, =ghostel-kill-buffer-on-exit=). eat's washed colors, the scroll-pop / stuck-input bug under Claude Code, and slowest throughput made it the weaker single-engine pick; one engine beats running two. Surface audited: 2 main modules (=vterm-config.el=, =ai-vterm.el=) + 4 satellites (=auto-dim-config.el= is the heavy one) + ~35 test files + init.el. Next: spike ghostel read-only to answer the open migration questions (auto-dim rework — ARCHITECTURE.md forbids the around-redraw color advice vterm uses; tmux pane-id via =process-tty-name= on a ghostel process; buffer naming; TTY-frame behavior; copy-mode keybinding parity), then write the migration spec under =docs/design/= and review it.
+
+*** 2026-06-04 Thu @ 23:17:54 -0500 Spec review: not ready until decisions and handoff shape are closed
+Ran the spec-review workflow against [[id:b54c94a0-d762-4b41-afd7-cf5593ce6675][docs/specs/vterm-to-ghostel-migration-spec-implemented.org]] and wrote a companion review file (incorporated and deleted 2026-06-04). Verdict: =Not ready=. Direction is sound, but the draft still has open D1-D5 decisions, lacks the workflow-required =Implementation phases= section and acceptance criteria, and needs explicit ghostel package/native-module failure behavior before implementation tasks can be emitted.
+
+*** 2026-06-04 Thu @ 23:24:28 -0500 Spec-response: review incorporated, raised to READY
+Folded the external review via spec-response. Craig accepted D1-D5; baked them plus D6 (module-failure = degrade-with-warning, modifying the reviewer's fail-loud) and D7 (=ghostel-max-scrollback= 10 MB) into a new Agreed-decisions section. Added Implementation phases (0-4), Acceptance criteria, Dependency/module-failure behavior, Test strategy, per-phase key/menu ownership, the tmux-suppression contract, and an Implementation-tasks drop-in block. Status DRAFT → READY; review file deleted. Build is now unblocked.
+
+*** 2026-06-04 Thu @ 23:30:18 -0500 External re-review: ready
+Re-reviewed [[id:b54c94a0-d762-4b41-afd7-cf5593ce6675][docs/specs/vterm-to-ghostel-migration-spec-implemented.org]] after incorporation. Verdict: =Ready=. No further blocking review notes; implementation can start from the phase plan and acceptance criteria in the spec.
+** DONE [#A] erc-yank silently publishes >5-line pastes as public gists :bug:quick:solo:
+CLOSED: [2026-06-20 Sat]
+Dropped erc-yank 2026-06-20 (Craig's call: drop, not harden). The package turned a >5-line paste into a PUBLIC gist (=gist -P=, the clipboard-paste flag, no =--private=) behind a single y-or-n-p, with no executable-find guard for =gist=. It also gisted the system clipboard rather than the kill-ring text being yanked. No replacement binding needed: =erc-mode-map= defines no C-y of its own, so removing the package lets C-y fall through to the ordinary global =yank=. Verified live: effective C-y in an ERC buffer = =yank=. (Audit's "no confirmation" was slightly off — the package did prompt — but public-by-default + one-keystroke confirm + no guard made dropping it the clean fix.)