aboutsummaryrefslogtreecommitdiff
path: root/todo.org
diff options
context:
space:
mode:
Diffstat (limited to 'todo.org')
-rw-r--r--todo.org357
1 files changed, 175 insertions, 182 deletions
diff --git a/todo.org b/todo.org
index 3f9b820a1..588450bca 100644
--- a/todo.org
+++ b/todo.org
@@ -55,95 +55,6 @@ Tags are additive. For example, a small wrong-behavior fix can be
=:bug:quick:=, and a feature that requires internal restructuring can be
=:feature:refactor:=.
* Emacs Open Work
-** 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.
-
** TODO [#B] Un-pin ghostel from 0.33.0 once upstream fixes #422/#423 :bug:
:PROPERTIES:
:LAST_REVIEWED: 2026-06-20
@@ -152,24 +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=).
-** 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.
-
** VERIFY [#B] calendar-sync robustness: atomic writes, curl --fail, zero-event false errors :bug:solo:next:
:PROPERTIES:
:LAST_REVIEWED: 2026-06-20
@@ -196,20 +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.
-** 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.
-
** 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.
-** 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.
-
** VERIFY [#C] Remove unused system-power keybindings :refactor:quick:next:
:PROPERTIES:
:LAST_REVIEWED: 2026-06-20
@@ -1016,69 +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.
-** 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.
-
** PROJECT [#B] Module-by-module hardening
:PROPERTIES:
:LAST_REVIEWED: 2026-06-05
@@ -3631,10 +3453,6 @@ 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]].
-** 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.)
-
** TODO [#B] F7 diff-aware coverage classifies every changed file "not tracked" :bug:solo:
:PROPERTIES:
:LAST_REVIEWED: 2026-06-20
@@ -8776,3 +8594,178 @@ the agent returns to its own window and the others are untouched. The flag only
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.)