aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-20 13:09:18 -0400
committerCraig Jennings <c@cjennings.net>2026-06-20 13:09:18 -0400
commitba85d2450e201060c65f6e6ee0859bdf65e01e64 (patch)
treed874987c7323a30a33c6066d5bddfee628ff85b9
parent8cccc2cd64105062ae4558090f11cc5b11e968a4 (diff)
downloaddotemacs-ba85d2450e201060c65f6e6ee0859bdf65e01e64.tar.gz
dotemacs-ba85d2450e201060c65f6e6ee0859bdf65e01e64.zip
chore(todo): file parent task for the remaining refactoring batch
-rw-r--r--todo.org124
1 files changed, 124 insertions, 0 deletions
diff --git a/todo.org b/todo.org
index 5401fa8d4..63435af00 100644
--- a/todo.org
+++ b/todo.org
@@ -55,6 +55,130 @@ 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
+** TODO [#B] Codebase refactoring program — remaining batch :refactor:solo:
+Resumes the full-codebase refactoring scan run of 2026-06-20 (8-agent fan-out over
+modules/ + scripts/theme-studio/). The goal: apply every scan finding except the
+won't-do items, one focused refactor per commit. 20 done and pushed this session
+(see =.ai/sessions/= for the 2026-06-20 log); 13 remain, listed below.
+
+*** 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.
+
+*** Remaining — medium extractions
+- calibredb-epub-config.el: =cj/nov-update-layout= (~29 lines, nesting 5) — extract
+ a render-preserving-position helper + a margin/fringe helper; also the =#E8DCC0=
+ sepia literal repeated 3x in =cj/nov-apply-preferences= → a local binding. Visual;
+ verify with the reload-and-verify loop.
+- ai-term.el: =cj/ai-term= toggle-off pcase arm (~50 of its ~76 lines) — extract the
+ teardown into =cj/--ai-term-toggle-off (win)=; and the 3x "switch window to
+ most-recent non-agent buffer" idiom (lines ~813-817, 839-843, 884-887) →
+ =cj/--ai-term-swap-to-working-buffer=. Behavior-sensitive window logic; existing
+ tests test-ai-term--single-window-toggle / --collapse-split are the net.
+- calendar-sync.el: =calendar-sync--collect-recurrence-exceptions= (~457-504, nesting
+ 5, a 14-binding let*) — extract =calendar-sync--parse-exception-event (event-str)=
+ returning the exception plist; the dolist body becomes a thin puthash. Has a
+ dedicated test (characterization net).
+- dirvish-config.el: =cj/dired-create-playlist-from-marked= (nesting 5) — extract a
+ pure =cj/--playlist-resolve-target= (the name-validate + overwrite-prompt loop),
+ leaving filter → resolve → write.
+- custom-case.el: =cj/title-case-region= (~71 lines, cyclomatic ~16) — write a
+ characterization test FIRST, then extract =cj/--title-case-word= (the per-word
+ decision); main loops boundaries and delegates.
+
+*** Remaining — big single-file (characterization tests first)
+- custom-comments.el: the divider/box render-skeleton duplication — simple-divider vs
+ padded-divider (~40 lines each, differ only in the padding loop) and box vs
+ heavy-box (~45 each, heavy-box just adds two blank inserts); plus the comment-syntax
+ prologue repeated 7x and the doubled-semicolon / length-option constants. Extract a
+ shared emit-prefix helper + a border/text/border emitter parameterized by padding
+ lambda + extra-blank-lines. Insertion-order-sensitive: characterize each generator's
+ output before refactoring. The heaviest item.
+- dwim-shell-config.el: ~46 =cj/dwim-shell-commands-*= defuns are trapped in the
+ =use-package dwim-shell-command :config=, so untestable under =make test=. For the
+ branching ones (=-video-trim= pcase x3, =-text-to-speech= darwin/linux,
+ =-extract-archive=/zip/tar single-vs-multi), extract each command's command-string
+ construction into a top-level pure =cj/dwim-shell--<name>-command= (takes prompted
+ values, returns the template string), leaving a thin interactive wrapper in :config.
+ Mirrors the existing =cj/dwim-shell--dated-backup-command= pattern. Do the
+ high-value branching commands; the trivial ones can stay.
+
+*** Remaining — theme-studio (scripts/theme-studio/)
+Suite: =make check= (Python/Node/ERT) + =./run-tests.sh= (browser gates) +
+=make check-generated= (byte-identical html) + =make coverage=. After ANY
+generate.py-output change, stage theme-studio.html in the SAME commit
+(check-generated compares to the working tree, not HEAD).
+- app-core.js: =dropdownRowTextColor= is exported + has 4 tests but no runtime caller
+ (live path computes inline at app.js:82). Decide: wire it into the dropdown popup
+ row painting, or delete it + its 4 tests. Needs Craig's intent — default to delete.
+- generate.py: ~230 lines of module-level build run at import; =face_coverage.py= does
+ =import generate= just for two constants and pays the whole cost. Wrap the assembly
+ in =build()= gated behind =__main__=; keep UI_FACES/CATS/COLS cheap module
+ constants. (The CRITICAL item.)
+- capture-default-faces.py: =condition_matches= (166-206) has parallel dict-branch and
+ list-branch clause checkers encoding the same four rules + constants twice. Normalize
+ both shapes to one mapping, run one set of checks. NOT in =make check= — verify by
+ running it.
+- face_coverage.py: =bucket_from_source= (118) and =bucket_of_source= (157) duplicate
+ the elpa/user/builtin path-kind detection. Extract =path_kind(path)= and have both
+ map its result to their own vocabulary.
+- browser-gates.js (HIGHEST RISK — rewrites the harness that verifies everything):
+ ~39/44 gates copy-paste the =let ok=true;const notes=[];const A=(c,n)=>{...}= setup +
+ the =document.title=...; result-div= postamble (note format already drifted: 17 use
+ " | " vs 24 " fails="). Extract one =gate(name, body)= helper. CRITICAL CONSTRAINT:
+ each gate's =if(...)= MUST keep the literal substring =location.hash==='#NAMEtest'=
+ because run-tests.sh:76 discovers gates by grepping exactly that — a registry/loop
+ that hides the hash check breaks discovery (silent false-green). Pair with a
+ =withSavedState(keys, body)= helper for the ~13 mutating gates' inconsistent
+ PALETTE/MAP/UIMAP/SYNTAX snapshot-restore (7 mutating gates currently restore
+ nothing). Verify: all gates green AND a deliberately-broken assertion still FAILS
+ (prove the harness can't manufacture greens). Also =assertPreviewFaces= for the 3
+ copy-pasted preview-face validators (mdtest/mupreviewtest/gnustest).
+- theme-studio test files: =plan(overrides)= factory for the ~30 planPaletteGenerator
+ full-option-literal calls (test-app-core.mjs + test-palette-generator-core.mjs); one
+ shared =stripExports= (reimplemented 3x in app-core/colormath/app-util tests, must
+ stay aligned with generate.py's strip per test_generate.py:48); and an
+ =assertInlinedVerbatim(name)= loop for the 5 inline-integrity cases.
+
+*** 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