aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-14 21:45:32 -0500
committerCraig Jennings <c@cjennings.net>2026-05-14 21:45:32 -0500
commita5519bc151700d1a76eba9baebe541c06958e9ce (patch)
tree4aa1325acb417626be7b0c54d5636160e875847a
parent7807e5318b2ca352d65651ab4ea25914a38299d7 (diff)
downloaddotemacs-a5519bc151700d1a76eba9baebe541c06958e9ce.tar.gz
dotemacs-a5519bc151700d1a76eba9baebe541c06958e9ce.zip
chore(todo): archive today's DONE work + sync child priorities
Five level-2 subtrees moved into the Resolved section: - Surface org narrowing + sparse-tree under C-; O - F9 toggle restores single-window layout (older) - AI-vterm scrollback history replaces agent buffer (older) - Add ERT coverage for modules below 70% - Fix Python tree-sitter font-lock query syntax error Five child priorities bumped to match their parents.
-rw-r--r--todo.org1081
1 files changed, 536 insertions, 545 deletions
diff --git a/todo.org b/todo.org
index f8d4b393..3fd31c7c 100644
--- a/todo.org
+++ b/todo.org
@@ -39,294 +39,6 @@ Tags are additive. For example, a small wrong-behavior fix can be
* Emacs Open Work
-** DONE [#A] Add Telegram Messaging
-https://github.com/zevlg/telega.el
-Make sure there is a setup script to run, so that the docker container can be installed post emacs dotfiles repository clone on a fresh install
-also, let's add an icon to the dashboard for this. perhaps this is the beginning of the third row? If so, add a child task to review and balance the dashboard icons
-
-Shipped this session:
-- =modules/telega-config.el= -- =use-package telega= with
- =telega-use-docker t=; launcher on =C-; G= (=C-; t= and =C-; m t=
- were both taken).
-- Dashboard Row 3 added with the Telegram icon; dashboard-mode-map =g=
- key launches =telega=.
-- =scripts/setup-telega.sh= -- verifies docker presence + daemon
- reachability; pulls =$TELEGA_DOCKER_IMAGE= when set, otherwise
- announces =M-x telega-server-build= for the in-Emacs build path.
- 7 bats tests in =tests/test-setup-telega.bats= (docker stubbed).
-- Auth (phone + verification code) is interactive on first =M-x telega=
- -- not scripted.
-
-*** DONE [#B] Add =scripts/setup-telega.sh= for TDLib docker container :feature:
-Pull or build the telega TDLib container so a fresh-clone install can
-run telega without a system-wide TDLib build. Mirror the
-=scripts/setup-email.sh= pattern: =main()= wrapped in a
-=BASH_SOURCE == 0= guard so the script is sourceable for bats tests;
-bats test file =tests/test-setup-telega.bats= with =docker= stubbed.
-
-*** DONE [#C] Review and balance dashboard icon layout :refactor:
-CLOSED: [2026-05-14 Thu]
-Adding the Telegram icon started a third row that has only one entry.
-Decide whether to (a) leave it asymmetric and let the row fill in as
-new launchers arrive, (b) move an existing icon down to balance 5/5/2
-or similar, or (c) reorganize by category (work / read / chat / play).
-
-Picked (c) -- the categories actually exist and a 4/4/4 grid
-balances cleanly:
-- Row 1 Work: Code / Files / Terminal / Agenda
-- Row 2 Read & Learn: Feeds / Books / Flashcards / Music
-- Row 3 Communication: Email / IRC / Slack / Telegram
-
-Drive-by fix in the same commit: Music had an icon but no
-`dashboard-mode-map' keybinding, so the visual launcher couldn't be
-fired without the mouse. Added =m=. Reordered the existing
-`define-key' calls to mirror the row layout so reading the keymap
-top-to-bottom matches the icons left-to-right.
-Surfaced when Telegram landed in Row 3 alone.
-** DONE [#B] Add VERIFY and DOING blocks to the main agenda view :feature:
-
-The main agenda "d" command (=cj/main-agenda-display=, F8) currently
-renders four blocks: OVERDUE -> HIGH PRIORITY UNRESOLVED -> SCHEDULE
--> PRIORITY B. Insert two new blocks around SCHEDULE so a glance at
-the daily view also surfaces what's in flight and what's waiting on a
-manual check:
-
-- Above SCHEDULE: all tasks with TODO state VERIFY (header:
- =VERIFICATION=).
-- Below SCHEDULE: all tasks with TODO state DOING (header:
- =IN-PROGRESS=).
-
-Resulting block order:
-
- OVERDUE -> HIGH PRIORITY -> *VERIFICATION* -> SCHEDULE -> *IN-PROGRESS* -> PRIORITY B
-
-Decisions:
-- *Scope*: same as the other blocks -- every entry in
- =org-agenda-files=, no per-project filter.
-- *Scheduled / deadlined entries*: included. A VERIFY task with a
- scheduled date for today appears in both the VERIFICATION block and
- the SCHEDULE block. Mirrors the HIGH PRIORITY block's behavior.
-- *Habit / PROJECT skips*: skip habits via
- =cj/org-skip-subtree-if-habit=. Don't skip PROJECT-keyword entries
- (the =(todo "VERIFY")= and =(todo "DOING")= match is keyword-exact
- so PROJECT parents wouldn't appear anyway, and a PROJECT in VERIFY
- state would be deliberate).
-
-Implementation locations:
-- =modules/org-agenda-config.el= -- two new entries inside
- =org-agenda-custom-commands= "d" block, each a =(todo "STATE" ...)=
- with =org-agenda-overriding-header=, the shared
- =cj/--main-agenda-prefix-format=, and the habit skip-function.
-- =modules/org-agenda-config.el= -- two header defvars
- (=cj/main-agenda-verify-title= / =cj/main-agenda-doing-title=) for
- symmetry with =cj/main-agenda-overdue-title= etc.
-
-Regression coverage:
-- Extend =tests/test-org-agenda-config-skip-functions.el= with
- structural assertions: the "d" command has six blocks in the
- expected order, the new VERIFICATION / IN-PROGRESS blocks reference
- the shared prefix-format symbol, carry the right
- =org-agenda-overriding-header=, and run the habit skip.
-
-** DONE [#A] Org Agenda fixes :bug:
-*** 2026-05-13 Wed @ 13:05:21 -0500 Skip CANCELLED entries from main agenda SCHEDULE
-see the following screenshot
-/home/cjennings/pictures/screenshots/2026-05-13_071428.png
-
-Fix shipped on main: commit =8e57950=. Added an org-agenda-skip-function
-to the SCHEDULE block of the "d" command in =org-agenda-custom-commands=
-that filters entries with TODO state CANCELLED. Scope is deliberately
-narrow -- DONE and FAILED scheduled tasks still render.
-
-Tests in =tests/test-org-agenda-config-skip-functions.el= (Normal +
-Boundary) lock in the configuration form on the agenda block and
-verify the other blocks aren't accidentally carrying the same skip.
-*** DONE [#C] Refactor: extract org-agenda-prefix-format literal :refactor:
-=modules/org-agenda-config.el= currently inlines =" %i %-15:c%?-15t% s"=
-across four blocks of =org-agenda-custom-commands= (overdue, hi-pri,
-schedule, priority-B). Extract into a defvar (e.g.
-=cj/--main-agenda-prefix-format=) and reference it from each block.
-Surfaced during the audit for the CANCELLED-schedule fix.
-
-Fix: new =cj/--main-agenda-prefix-format= defvar in
-=modules/org-agenda-config.el=; all four blocks of the "d" command now
-reference the symbol instead of inlining the literal. Regression test
-in =tests/test-org-agenda-config-skip-functions.el= walks the blocks
-and asserts each =org-agenda-prefix-format= entry resolves to the
-shared symbol -- so a future tweak to one block can't silently diverge
-from the others.
-*** 2026-05-13 Wed @ 13:27:39 -0500 Clear dedicated before toggling window split
-Reproduction steps
-- open an org file (I was using this projects's todo.org file
-- hit the f8 button to open the agenda view. it opens fine.
-- toggle-window-split
->>> The agenda displays on both panes after the toggle.
-see snapshot below
-/home/cjennings/pictures/screenshots/2026-05-13_071603.png
-
-Fix shipped on main: commit =97f0f8e=. Root cause was the dedicated
-=*Org Agenda*= window (set via =display-buffer-alist= rule) rejecting
-the internal =set-window-buffer= swap. The non-dedicated buffer never
-crossed and both panes ended up showing the agenda.
-
-=modules/ui-navigation.el= now clears dedicated on both windows at the
-top of =toggle-window-split= before the swap. The toggle is an
-explicit layout change, so preserving per-window dedicated through it
-would just re-trigger the same wedge on the next invocation.
-
-Tests in =tests/test-ui-navigation--toggle-window-split.el= (5 tests,
-Normal + Boundary) cover the no-dedicated baseline, the bug-trigger,
-post-toggle cleared state, and the 1-window / 3-window no-op cases.
-Verified red against the unfixed code (the bug-trigger test errored
-=Window is dedicated to '*test-toggle-b*'=) before applying the fix.
-
-Live verification pending: =M-x load-file modules/ui-navigation.el=,
-then walk through F8 + M-S-t in a fresh session.
-*** DONE [#A] Enhancement: replace todo indicators with project name
-In the overdue section, the high priority section, and the priority B section, each of the entries has a todo: indicator, which is the name of the file.
-This is not useful. Based on how I'm using emacs, every entry is likely to come from a file named todo.org.
-A much preferrable option would be to have the project's name there instead.
-so, for instance this todo.org file would show "emacs.d" as the project name in place of todo.
-
-see snapshot below for an example of the current state.
-/home/cjennings/pictures/screenshots/2026-05-13_071840.png
-
-Fix: =cj/--org-todo-category-from-file= +
-=cj/--org-set-todo-category= in =modules/org-agenda-config.el=
-hook =org-category= buffer-locally to the parent directory's
-basename (with a single leading dot stripped, so =.emacs.d= reads
-as =emacs.d=) whenever a todo.org file opens. Explicit
-=#+CATEGORY:= still wins. 14 tests in
-=tests/test-org-agenda-config-category.el= cover normal /
-boundary / error paths; full =make test-unit= green.
-
-** DONE [#A] Save journal buffer after marking a task DONE :bug:
-CLOSED: [2026-05-14 Thu]
-
-When a task transitions to a done state, =cj/org-roam-copy-todo-to-today=
-in =modules/org-roam-config.el= refiles a copy of the heading into the
-day's roam journal (creating the file if missing) -- example:
-=/home/cjennings/sync/org/roam/journal/2026-05-14.org=. The journal
-buffer is left modified-but-unsaved, so closing Emacs always prompts
-about open unsaved buffers with no obvious source.
-
-Function intends to save via two routes:
-- =org-after-refile-insert-hook= let-bound to =#'save-buffer= -- a
- single-function value rather than a list. =run-hooks= calls a bare
- function value, so this should fire, but verify it does in the
- capture+refile context (and that it runs in the target buffer, not
- the source).
-- The =:config= block of =use-package org-refile= advises =org-refile=
- =:after= with =org-save-all-org-buffers=. That only runs once
- =org-refile= is loaded; if the DONE transition fires before
- org-refile's =:defer .5= elapses, the advice isn't attached yet.
-
-Fix candidates:
-- Drop the let-binding and call =(save-buffer)= explicitly in the
- target buffer after the =org-refile= call, before
- =save-window-excursion= unwinds. Deterministic, doesn't depend on
- hook timing.
-- Or eager-load =org-refile= so the =:after= advice attaches before any
- DONE transition can fire.
-
-Test: mark a TODO done from a non-journal buffer, then check
-=buffer-modified-p= on the dated journal buffer. Should be nil.
-
-** DONE [#B] Extend dired/dirvish =T= to transcribe videos, not just audio :feature:
-CLOSED: [2026-05-14 Thu]
-
-Today =T= on an audio file in dired/dirvish triggers
-=cj/transcribe-audio-at-point= and only accepts files matching
-=cj/audio-file-extensions= (=cj/--audio-file-p= rejects anything
-else with a =user-error=). Want the same one-key flow on video
-files -- so a =.mp4= or =.mkv= recording can be transcribed without
-hand-extracting the audio track first.
-
-Likely shape:
-- New =cj/video-file-extensions= in user-constants.el (mp4, mkv,
- mov, webm, avi, m4v, ...).
-- =cj/--video-file-p= sibling of =cj/--audio-file-p=.
-- =cj/--start-transcription-process= (or a wrapper) detects video,
- shells out to ffmpeg to extract the audio track to a temp file
- (=ffmpeg -i in.mp4 -vn -acodec copy out.m4a= or similar; pick a
- codec the backend accepts), then transcribes the temp file and
- cleans up.
-- =cj/transcribe-audio-at-point= accepts both audio and video via
- =(or (cj/--audio-file-p f) (cj/--video-file-p f))=; the
- surrounding pipeline knows when to insert the ffmpeg step.
-
-Open design questions:
-- Keep the function named =transcribe-audio-at-point= (treats video
- as "audio-bearing") or rename to =transcribe-media-at-point= and
- add an alias? Rename probably cleaner.
-- ffmpeg availability check + =cj/executable-find-or-warn= pattern
- on first use.
-- Where the temp audio file lives -- alongside the video (visible),
- or =temporary-file-directory= (clean). Probably the latter for
- videos the user doesn't want to clutter.
-- Do we keep the temp audio after transcription, or always delete?
- The log file already retains diagnostic info; extracted audio is
- derivable. Default to delete; offer a custom to keep.
-
-Test surface: =cj/--video-file-p= happy/edge cases, the ffmpeg
-extract step (stub =call-process=), and the dispatch in
-=cj/transcribe-audio-at-point= against a video path.
-
-** DONE [#C] Surface org narrowing + sparse-tree under =C-; O= :refactor:
-CLOSED: [2026-05-14 Thu]
-Final layout flatter than the original proposal: no =n= or =s=
-sub-prefixes. Lowercase letters create / narrow / sparse-tree;
-the same letter capitalized cancels. `n' / `N' = narrow / widen.
-`s' / `S' = match-sparse-tree / show-all. `t' / `T' =
-show-todo-tree / show-all (both capitals point at the same
-`org-show-all' so the mental model is "capital cancels the
-lowercase I just ran"). `R' = `org-reveal' (no lowercase pair --
-`r' is the table-row sub-prefix); F2 (the old reveal binding) is
-freed up. Sibling-stepping is on `>' / `<' at the top level.
-
-Four new ERT assertions in
-=tests/test-org-config-keymap-ownership.el= lock the shape.
-
-The narrowing and sparse-tree commands already exist in
-=modules/org-config.el=, but they're bound only inside the
-=:bind (:map org-mode-map ...)= block and scattered across `C-c'
-shortcuts -- nothing in `cj/org-map' (`C-; O') surfaces them, so
-which-key never shows them and discoverability is poor.
-
-Existing bindings worth promoting (org-config.el ~line 141-150):
-- =C-\\= =org-match-sparse-tree=
-- =C-c N= =org-narrow-to-subtree=
-- =C-c >= =cj/org-narrow-forward=
-- =C-c <= =cj/org-narrow-backwards=
-- =C-c <ESC>= =widen=
-- =<f2>= =org-reveal= (the reveal-narrowed-context command,
- not org-reveal-config.el)
-
-Proposal:
-
-Add a sub-menu under `C-; O':
-
-- `C-; O n' -- narrow operations (sub-prefix)
- - `n s' narrow to subtree
- - `n e' narrow to element
- - `n >' narrow forward sibling
- - `n <' narrow backward sibling
- - `n w' widen
-
-- `C-; O s' -- sparse-tree operations (sub-prefix)
- - `s s' org-match-sparse-tree (by tag/property/todo match)
- - `s t' show all TODOs
- - `s p' show entries with a priority
- - `s r' org-reveal (open the surrounding context of point)
-
-Add the which-key labels alongside. Keep the existing `C-c'
-bindings as-is for muscle memory.
-
-Open question: should `cj/org-narrow-forward' /
-`cj/org-narrow-backwards' have their own sub-letters under `n', or
-just be under `n >' / `n <' as written above? The arrow-symbol
-keys read naturally as "next/previous" so probably keep them.
-
** TODO [#B] Investigate gptel-magit not working properly :bug:
Wired up in =modules/ai-config.el= as three lazy entry points:
@@ -342,181 +54,6 @@ attachment via =transient-append-suffix=, or gptel-side backend/model config.
Migration to current lazy form: commit =3eb1a0c refactor(gptel): lazy-load
gptel-magit, rebind rewrite/context keys=.
-** DONE [#B] F9 toggle should restore single-window layout for AI-vterm :bug:
-
-When the AI-vterm buffer is the only window in the frame (e.g. after =C-x 1=) and
-F9 is pressed, =cj/ai-vterm= buries the buffer (correct), but the next F9
-redisplays the agent in a split rather than restoring the single-window full-frame
-layout. F9 should toggle off/on while preserving the lone-window state.
-
-Fix: new =cj/--ai-vterm-last-was-bury= flag in =modules/ai-vterm.el=.
-The toggle-off branch sets it to t when =one-window-p= is true (bury
-path) or nil when =delete-window= runs. =cj/--ai-vterm-display-saved=
-checks the flag at toggle-on: if t and the frame is still single-window,
-it replaces the selected window's buffer in place via =set-window-buffer=
-rather than splitting via =display-buffer-in-direction=. Flag is
-consumed (cleared) by either branch so it never stays stale.
-
-5 tests in =tests/test-ai-vterm--single-window-toggle.el= cover:
-flag set on bury, flag cleared on delete-window, flag respected only
-when still one-window, flag not set when bury didn't run, and the
-end-to-end roundtrip. Full =make test-unit= green.
-
-** DONE [#B] AI-vterm scrollback history should replace agent buffer in place :feature:
-
-When viewing the scrollback history of an AI-vterm buffer, the history view should
-replace the live agent buffer in the same window rather than splitting or popping
-a separate window. Goal: read past output without losing the agent's frame slot,
-then snap back to the live buffer when done.
-
-Decisions on the open questions:
-- *Trigger*: reused the existing =cj/vterm-tmux-history= command (=C-; x h=).
- No new command -- it already captures the tmux pane and runs from any
- vterm buffer including agents.
-- *Round-trip*: =q= / =<escape>= / =C-g= already restore the origin in
- the same window via =cj/vterm-tmux-history-quit=. Same key as the
- scrollback mode's other exits.
-- *F9 integration*: deferred. Pressing F9 in history mode now treats the
- history buffer as non-agent (its name is =*vterm tmux history: ...*=,
- not =agent [...]=) so dispatch falls through to redisplay-recent + a
- saved-direction split. A user who wants to toggle agent off should
- press =q= first, then F9. Filed as a follow-up if it bites.
-
-Fix: =modules/vterm-config.el= -- one line. =pop-to-buffer buffer=
-became =switch-to-buffer buffer= so the history view replaces the origin
-in the selected window instead of going through display-buffer's split
-logic. Quit was already in-place via =set-window-buffer=.
-
-New test in =tests/test-vterm-tmux-history.el= asserts the selected
-window's buffer becomes the history buffer with no extra window
-created (=one-window-p= still t). Existing tests dropped their
-=pop-to-buffer= stub since =switch-to-buffer= works directly in batch.
-Full =make test-unit= green.
-
-** DONE [#B] Add ERT coverage for modules below 70% :tests:
-CLOSED: [2026-05-14 Thu]
-
-Coverage snapshot from =make coverage= on 2026-05-14 (post-push):
-=5958/6861= executable lines covered (=86.8%=) across 73 tracked module files.
-
-All tracked modules now sit at 70% or above. The two stragglers
-(=system-defaults.el= and =system-commands.el=) were resolved by
-attacking the instrumentation pattern rather than adding more tests:
-
-- =system-defaults.el= 8.3% -> 100%: switched the helper-functions
- test from per-test =(load ...)= reloads inside =cl-letf= to a
- single top-level =(require 'system-defaults)= wrapped in stubs,
- so undercover sees one load instead of N reloads.
-
-- =system-commands.el= 69.4% -> 100%: switched =cj/system-cmd='s
- =(interactive (list (read-shell-command ...)))= to the equivalent
- string spec =(interactive "sSystem command: ")=, which lets
- undercover instrument the function body that was previously
- showing as 0 hits, plus added one test that captures and invokes
- the =run-at-time= lambda body directly.
-
-Lesson for future coverage work: when ERT tests exercise a function
-but the body shows 0 hits, suspect undercover/edebug instrumentation
-failing on a specific Lisp form (=interactive= with a destructured
-spec, backquote-destructured =pcase-let=, etc.), not the tests.
-
-*** DONE [#B] Add ERT tests for =modules/prog-python.el= (21/21, 100.0%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/selection-framework.el= (3/3, 100.0%) :tests:
-*** DONE [#B] Add ERT tests for =modules/keyboard-compat.el= (29/29, 100.0%) :tests:
-*** DONE [#B] Add ERT tests for =modules/prog-webdev.el= (21/21, 100.0%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/calibredb-epub-config.el= (95/133, 71.4%) :tests:
-*** DONE [#B] Add ERT tests for =modules/system-defaults.el= (12/12, 100.0%) :tests:
-CLOSED: [2026-05-14 Thu]
-Rewrote =tests/test-system-defaults-functions.el= to call
-=(require 'system-defaults)= once at top level (with the
-side-effecting primitives stubbed via =cl-letf= wrapping the
-require) instead of looping per-test =(load ...)= reloads inside
-=cl-letf=. Undercover only saw the first load, so the function
-bodies showed as uncovered even though every test ran them. Loading
-once -- with the same stubs -- fixed the gauge and pulled coverage
-to 12/12. Added two more tests to exercise the previously-unhit
-list-without-comp guard and the non-string-message format branch.
-*** DONE [#B] Add ERT tests for =modules/ui-navigation.el= (46/48, 95.8%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/prog-go.el= (24/27, 88.9%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/system-commands.el= (51/51, 100.0%) :tests:
-CLOSED: [2026-05-14 Thu]
-The =interactive= form on =cj/system-cmd= was =(interactive (list
-(read-shell-command "...")))= -- a destructured list form. Edebug-
-based undercover instrumentation didn't see past it, so the function
-body registered 0 hits even though the tests called it directly.
-Switched to the equivalent string spec =(interactive "sSystem
-command: ")= and the body instrumented as expected. Added one more
-test that captures the =run-at-time= lambda inside
-=cj/system-cmd-restart-emacs= and invokes it directly so the inner
-=call-process-shell-command= branch registers as covered.
-*** DONE [#B] Add ERT tests for =modules/external-open.el= (31/33, 93.9%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/org-webclipper.el= (59/59, 100.0%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/system-utils.el= (26/26, 100.0%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/org-reveal-config.el= (68/81, 84.0%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/coverage-elisp.el= (19/19, 100.0%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/org-noter-config.el= (72/99, 72.7%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/ai-config.el= (160/191, 83.8%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/slack-config.el= (70/74, 94.6%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/org-roam-config.el= (80/80, 100.0%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/custom-text-enclose.el= (145/145, 100.0%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/dirvish-config.el= (174/185, 94.1%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/hugo-config.el= (88/96, 91.7%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/org-refile-config.el= (50/51, 98.0%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/org-contacts-config.el= (64/79, 81.0%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/transcription-config.el= (150/162, 92.6%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/music-config.el= (213/278, 76.6%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/mail-config.el= (14/19, 73.7%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/custom-buffer-file.el= (167/212, 78.8%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/org-agenda-config.el= (103/104, 99.0%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/host-environment.el= (53/57, 93.0%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/custom-ordering.el= (101/101, 100.0%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/ui-theme.el= (39/40, 97.5%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/custom-comments.el= (317/358, 88.5%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/ui-config.el= (28/31, 90.3%) :tests:
-*** DONE [#B] Add ERT tests for =modules/custom-whitespace.el= (80/82, 97.6%) :tests:
-*** DONE [#B] Add ERT tests for =modules/jumper.el= (97/99, 98.0%) :tests:
-*** DONE [#B] Add ERT tests for =modules/test-runner.el= (160/222, 72.1%) :tests:
-CLOSED: [2026-05-14 Thu]
-*** DONE [#B] Add ERT tests for =modules/browser-config.el= (62/76, 81.6%) :tests:
-
-** DONE [#A] Fix Python tree-sitter font-lock query syntax error :bug:
-CLOSED: [2026-05-14 Thu]
-
-Diagnosed 2026-04-26 — paused at /start-work Gate 2. Root cause was system-level, not in =.emacs.d=: Emacs 30.2 + tree-sitter library 0.26.x predicate-syntax mismatch. Emacs sent =#match= (no =?= suffix), tree-sitter 0.26 rejected anything but =#match?=. Affected every =:match=, =:equal=, =:pred= predicate in every treesit-aware mode, not just Python.
-
-Full investigation, reproduction, and fix-option analysis in:
-
-[[file:docs/python-treesit-predicate-mismatch.txt][docs/python-treesit-predicate-mismatch.txt]]
-
-Resolved 2026-05-14 by an upstream emacs Arch-package revision bump (=30.2-2= → =30.2-3=, shipped 2026-05-03) — most likely carrying a downstream patch to =treesit.c='s predicate translation. Bug no longer reproduces: the exact failing query runs cleanly via =treesit-query-capture=, and =font-lock-ensure= on a real Python file under =python-ts-mode= completes with no =treesit-query-error=. No local override applied to =modules/prog-python.el=. Matches option A from the investigation's fix-options ("WAIT FOR UPSTREAM EMACS FIX").
-
** STALLED [#C] EPUB text is slightly left-of-center (shr word-wrap shortfall) :bug:
[2026-05-12] Visual review of the reading-width rework is done -- it's good. Not sure I actually need this nit fixed; the left-of-center bias is minor and the `+'/`-' keys let me nudge it. Parking here until I decide it bothers me enough.
@@ -1216,7 +753,7 @@ Expected outcome:
- Optional path failures should be logged but not block startup.
- Add tests around success, optional failure, and required failure behavior.
-**** TODO [#C] Clean up host environment predicates and timezone detection :cleanup:refactor:
+**** TODO [#B] Clean up host environment predicates and timezone detection :cleanup:refactor:
Small module-specific cleanup in =host-environment.el=:
- =env-desktop-p= has a docstring that says "host is a laptop"; it should say
@@ -1234,7 +771,7 @@ Acceptance criteria:
- Normalize or document =env-x-p= vs =env-x11-p= semantics.
- Add or adjust tests only if behavior changes.
-**** TODO [#C] Add minimal =system-defaults.el= setting smoke tests :tests:
+**** TODO [#B] Add minimal =system-defaults.el= setting smoke tests :tests:
=system-defaults.el= has no direct test file, despite holding high-impact
defaults: server startup, backup behavior, custom-file behavior, symlink
@@ -1275,7 +812,7 @@ Pitfalls:
- Keep local repositories higher priority than online archives.
- Avoid prompting or refreshing archives during batch tests.
-**** TODO [#C] Decide and test package signature policy :security:startup:
+**** TODO [#B] Decide and test package signature policy :security:startup:
=early-init.el= sets =package-check-signature= to =nil= after package setup, with
an earlier commented emergency toggle for expired signatures. That may be
@@ -1340,7 +877,7 @@ Pitfalls:
- Some media players need different URL handling; preserve the existing
=:needs-stream-url= behavior.
-**** TODO [#C] Add coverage for =external-open.el= and =media-utils.el= :tests:
+**** TODO [#B] Add coverage for =external-open.el= and =media-utils.el= :tests:
The core custom editing modules are covered, but these integration helpers have
little or no direct test coverage despite owning shell/process boundaries.
@@ -1354,7 +891,7 @@ Useful test seams:
This pairs naturally with the process-launch hardening task above.
-**** TODO [#C] Audit destructive buffer/file keybindings for confirmation policy :ux:
+**** TODO [#B] Audit destructive buffer/file keybindings for confirmation policy :ux:
=cj/buffer-and-file-map= includes destructive operations under =C-; b=,
including delete file, erase buffer, clear top, clear bottom, and revert. Some
@@ -1367,7 +904,7 @@ Expected outcome:
- Consider safer wrappers for =erase-buffer= and =revert-buffer= under the
personal keymap.
-**** TODO [#C] Add explicit autoloads/requires for cross-module command keybindings :cleanup:refactor:
+**** TODO [#B] Add explicit autoloads/requires for cross-module command keybindings :cleanup:refactor:
Several custom utility keymaps bind symbols owned by other modules without
declaring the relationship:
@@ -1406,7 +943,7 @@ Review progress:
- =font-config.el=, =ui-theme.el=, =selection-framework.el=, =ui-navigation.el=,
and =popper-config.el= have little direct test coverage.
-**** TODO [#C] Decide whether =popper-config.el= should exist while disabled :cleanup:
+**** TODO [#B] Decide whether =popper-config.el= should exist while disabled :cleanup:
=popper-config.el= is required by =init.el=, but the only =use-package popper=
form is =:disabled t=. That makes the module a no-op while still participating
@@ -1550,7 +1087,7 @@ Expected outcome:
- Add tests around the pure slug/demotion/format helpers are already present;
add one integration-style test around failure ordering if feasible.
-**** TODO [#C] Make =org-webclipper.el= initialization less global-state-heavy :cleanup:refactor:
+**** TODO [#B] Make =org-webclipper.el= initialization less global-state-heavy :cleanup:refactor:
=org-webclipper.el= stores protocol URL/title in global variables, registers
capture templates lazily, and clears those globals during template expansion.
@@ -1563,7 +1100,7 @@ Expected outcome:
- Ensure aborted captures clear temp state.
- Keep the existing browser bookmarklet workflow unchanged.
-**** TODO [#C] Review external executable assumptions in Org export/publishing modules :cleanup:
+**** TODO [#B] Review external executable assumptions in Org export/publishing modules :cleanup:
=org-export-config.el= assumes =zathura= for one Pandoc PDF path, =hugo-config.el=
assumes =hugo= and a browser/file-manager opener, =org-reveal-config.el= assumes
@@ -1611,7 +1148,7 @@ Expected outcome:
- Preserve the current completing-read workflow when files exist.
- Add tests for missing directory, empty directory, and normal selection list.
-**** TODO [#C] Clarify contradictory Org export task defaults :cleanup:tests:
+**** TODO [#B] Clarify contradictory Org export task defaults :cleanup:tests:
=org-export-config.el= sets =org-export-with-tasks= twice in a row: first to
=("TODO")= and then to =nil=. The final behavior is "export no tasks", but the
@@ -1624,7 +1161,7 @@ Expected outcome:
- If task export should vary by workflow, expose an explicit command or local
export option instead of relying on the global default.
-**** TODO [#C] Fix and cover Org Babel structure templates :bug:tests:
+**** TODO [#B] Fix and cover Org Babel structure templates :bug:tests:
=org-babel-config.el= adds a Java structure template as =("java" . "src javas")=,
which appears to expand to the wrong language name. The template list is useful
@@ -1636,7 +1173,7 @@ Expected outcome:
expand to the intended language names for common aliases: =bash=, =zsh=,
=el=, =py=, =json=, =yaml=, and =java=.
-**** TODO [#C] Make org-contacts/Mu4e boundaries explicit :cleanup:refactor:
+**** TODO [#B] Make org-contacts/Mu4e boundaries explicit :cleanup:refactor:
=org-contacts-config.el= defines helpers that call Mu4e functions when the
current major mode is a Mu4e mode, and the =use-package org-contacts= block is
@@ -1653,7 +1190,7 @@ Expected outcome:
- Add a smoke test for loading =org-contacts-config.el= without Mu4e stubs if
practical.
-**** TODO [#C] Add an Org workflow health check command :feature:ux:
+**** TODO [#B] Add an Org workflow health check command :feature:ux:
Several Org workflow modules depend on personal paths, optional external tools,
and local package checkouts. Failures currently show up at command time in
@@ -1671,7 +1208,7 @@ Recommended improvement:
- Make the checker return structured data so it can be unit-tested and displayed
either in Messages or a buffer.
-**** TODO [#C] Add capture-template key collision and target smoke tests :tests:
+**** TODO [#B] Add capture-template key collision and target smoke tests :tests:
Org capture templates are assembled across =org-capture-config.el=,
=org-contacts-config.el=, =org-webclipper.el=, and other feature modules. The
@@ -1686,7 +1223,7 @@ Recommended improvement:
- Cover lazy additions for contact and webclipper templates without requiring a
browser/org-protocol round trip.
-**** TODO [#C] Document Org workflow module ownership and load boundaries :docs:refactor:
+**** TODO [#B] Document Org workflow module ownership and load boundaries :docs:refactor:
The Org workflow is spread across many modules with overlapping responsibilities:
capture templates, keymaps, org-protocol handlers, refile/agenda target
@@ -1733,7 +1270,7 @@ Review progress:
YAML.
- =test-runner.el= has direct tests, but project-scoping is still a design gap.
-**** TODO [#C] Revisit F4 project classification vs actual project capabilities :ux:
+**** TODO [#B] Revisit F4 project classification vs actual project capabilities :ux:
=dev-fkeys.el= classifies a project as =interpreted= if it has
=pyproject.toml=, =requirements.txt=, =Pipfile=, or =package.json=, even when it
@@ -1802,7 +1339,7 @@ syntax issue. That one resolved upstream on 2026-05-14 (see =docs/python-
treesit-predicate-mismatch.txt= RESOLVED footer), so this task no longer
depends on it.
-**** TODO [#C] Harden git clone from clipboard in =vc-config.el= :robustness:refactor:
+**** TODO [#B] Harden git clone from clipboard in =vc-config.el= :robustness:refactor:
=cj/git-clone-clipboard-url= shells out to =git clone= from clipboard text and
derives the clone directory with =file-name-nondirectory=. The URL is quoted, so
@@ -1817,7 +1354,7 @@ Expected outcome:
- Report clone failures from the process exit status instead of assuming the
directory appears.
-**** TODO [#C] Decide whether auto-executable shell scripts should be opt-in :ux:
+**** TODO [#B] Decide whether auto-executable shell scripts should be opt-in :ux:
=prog-shell.el= adds a global =after-save-hook= that sets executable bits on any
saved file with a shebang. This is convenient, but it silently changes file
@@ -1830,7 +1367,7 @@ Expected outcome:
- Keep the existing =cj/make-script-executable= tests updated for the chosen
policy.
-**** TODO [#C] Review language formatter process boundaries :cleanup:
+**** TODO [#B] Review language formatter process boundaries :cleanup:
JSON, YAML, and webdev formatters use =shell-command-on-region= with command
strings. Most inputs are fixed or shell-quoted, but formatter code is a good
@@ -1911,7 +1448,7 @@ Expected outcome:
- Add a short manual checklist or mocked test for the variables that control
remote image display.
-**** TODO [#C] Clean up mail compose buffer lifecycle conflicts :cleanup:quick:
+**** TODO [#B] Clean up mail compose buffer lifecycle conflicts :cleanup:quick:
=mail-config.el= first sets =message-kill-buffer-on-exit= to =t= in the mu4e
configuration, then =org-msg= later sets it to nil. That may be intentional for
@@ -2044,7 +1581,7 @@ Expected outcome:
- Stop only that process or process group.
- Preserve existing toggle behavior and tests for already-running recordings.
-***** TODO [#C] Ensure chosen recording directories are created directly :bug:
+***** TODO [#B] Ensure chosen recording directories are created directly :bug:
The recording toggles accept a directory via prefix argument, then derive parent
directories in a way that can create the parent but not necessarily the selected
@@ -2056,7 +1593,7 @@ Expected outcome:
- Ensure the actual target directory exists before launching ffmpeg/wf-recorder.
- Add tests for new directories and paths containing spaces.
-**** TODO [#C] Make AI conversation persistence path-safe and project-aware :cleanup:refactor:
+**** TODO [#B] Make AI conversation persistence path-safe and project-aware :cleanup:refactor:
=ai-conversations.el= has good pure helper seams but is currently untested in
this repo. The path slugging is simple and the save/load/delete commands operate
@@ -2100,7 +1637,7 @@ Expected outcome:
- Test autosave path setup and delete confirmation with stubbed prompts.
- Keep GPTel itself mocked or avoided unless a later integration test needs it.
-**** TODO [#C] Add first coverage for Dirvish utility helpers :tests:
+**** TODO [#B] Add first coverage for Dirvish utility helpers :tests:
=dirvish-config.el= is not currently represented in =.coverage/simplecov.json=.
The pure-ish Dired helpers have a few sharp edges that are easy to characterize
@@ -2141,7 +1678,7 @@ Expected outcome:
=music-dir=.
- Add regression tests for no-file-at-point and traversal-like playlist names.
-**** TODO [#C] Add first smoke coverage for mail and system command modules :tests:
+**** TODO [#B] Add first smoke coverage for mail and system command modules :tests:
=mail-config.el= and =system-commands.el= are not currently represented in the
coverage report. Both can get useful coverage without sending mail or invoking
@@ -2154,7 +1691,7 @@ Expected outcome:
and command-string construction with =shell-command= stubbed.
- Keep all tests batch-safe and non-destructive.
-**** TODO [#C] Harden EWW/Elfeed synchronous network helpers :cleanup:refactor:
+**** TODO [#B] Harden EWW/Elfeed synchronous network helpers :cleanup:refactor:
=elfeed-config.el= includes synchronous URL retrieval helpers for converting
YouTube channel/playlist URLs into feed entries, and =eww-config.el= advises URL
@@ -2166,7 +1703,7 @@ Expected outcome:
- Add a small test or manual checklist for the EWW user-agent advice so it does
not affect package.el or non-EWW URL callers.
-**** TODO [#C] Move Slack which-key registration behind =with-eval-after-load= :cleanup:quick:
+**** TODO [#B] Move Slack which-key registration behind =with-eval-after-load= :cleanup:quick:
=slack-config.el= calls =which-key-add-keymap-based-replacements= at top level,
while most modules defer which-key registration. If which-key is not loaded or
@@ -3333,7 +2870,7 @@ Expected outcome:
behavior instead of encoding =git stash= / =git pull= / =git stash pop= as the
desired path.
-*** DONE [#B] Replace shell git calls with process helpers and parse statuses :refactor:
+*** DONE [#A] Replace shell git calls with process helpers and parse statuses :refactor:
Expected outcome:
- Use =process-file= / =call-process= with argv lists.
@@ -3341,7 +2878,7 @@ Expected outcome:
- Distinguish clean, dirty, skipped, pull-failed, and needs-review states.
- Add tests with stubbed git command results.
-*** DONE [#B] Prune expensive directories while discovering repos :refactor:
+*** DONE [#A] Prune expensive directories while discovering repos :refactor:
=cj/find-git-repos= recursively walks the configured project/code roots and
checks every directory for a nested =.git=. That can wander through
@@ -3353,7 +2890,7 @@ Expected outcome:
- Stop descending once a repo root has been found unless explicitly requested.
- Add tests for nested repos and ignored heavy directories.
-*** DONE [#C] Make remote skip policy explicit and configurable
+*** DONE [#A] Make remote skip policy explicit and configurable
=cj/reconcile--reference-clone-p= treats HTTP/HTTPS remotes as reference clones
and skips them. That may be right for this machine, but the behavior is encoded
@@ -3545,7 +3082,7 @@ as intentionally low-value instead of forcing brittle tests.
Focus on formatter/setup helpers and any Python command-building logic. Avoid
testing Emacs package glue directly; split pure helpers first if needed.
-*** TODO [#C] Add coverage for =selection-framework.el= (0.0%, 0/3) :tests:
+*** TODO [#B] Add coverage for =selection-framework.el= (0.0%, 0/3) :tests:
Add a smoke test for the final binding/setup behavior or document why the three
instrumented lines are configuration-only.
@@ -3560,7 +3097,7 @@ tests table-driven so future terminal quirks are easy to add.
Cover formatter wiring and web-mode helper behavior without invoking external
formatters.
-*** TODO [#C] Add coverage for =system-defaults.el= (8.3%, 1/12) :tests:
+*** TODO [#B] Add coverage for =system-defaults.el= (8.3%, 1/12) :tests:
Characterize the platform/default-setting helpers with stubs around system
calls. Do not assert machine-specific defaults directly.
@@ -3590,22 +3127,22 @@ for user-facing open commands and error paths while stubbing launcher calls.
Cover URL/content parsing, manual URL prompt behavior, protocol URL handling,
and aborted capture cleanup.
-*** TODO [#C] Add coverage for =system-utils.el= (19.2%, 5/26) :tests:
+*** TODO [#B] Add coverage for =system-utils.el= (19.2%, 5/26) :tests:
Review remaining utilities not covered by =eval-buffer= tests. Add focused
Normal/Boundary/Error tests for pure helpers; stub process/system calls.
-*** TODO [#C] Add coverage for =org-reveal-config.el= (20.0%, 9/45) :tests:
+*** TODO [#B] Add coverage for =org-reveal-config.el= (20.0%, 9/45) :tests:
Extend existing header-template/title tests to cover export command setup and
option-building helpers.
-*** TODO [#C] Add coverage for =coverage-elisp.el= (26.3%, 5/19) :tests:
+*** TODO [#B] Add coverage for =coverage-elisp.el= (26.3%, 5/19) :tests:
The detector is covered; add tests for report-path/project-root behavior and a
stubbed run callback path if it can be tested without launching compilation.
-*** TODO [#C] Add coverage for =org-noter-config.el= (27.3%, 27/99) :tests:
+*** TODO [#B] Add coverage for =org-noter-config.el= (27.3%, 27/99) :tests:
Build on the existing predicate/template tests. Target note-file placement,
preferred split behavior, and interactive wrapper smoke tests.
@@ -3615,7 +3152,7 @@ preferred split behavior, and interactive wrapper smoke tests.
Prioritize model/backend selection edge cases, gptel local-tool registration,
and command helpers. Stub network/model calls.
-*** TODO [#C] Add coverage for =dirvish-config.el= (30.4%, 48/158) :tests:
+*** TODO [#B] Add coverage for =dirvish-config.el= (30.4%, 48/158) :tests:
Existing utility tests cover several helpers. Add focused coverage for remaining
playlist/quick-access/display-path branches and document config-only areas.
@@ -3630,32 +3167,32 @@ configuration, and command error handling with Slack APIs stubbed.
Extend existing slug/demote/link tests to cover TODO copy behavior, capture
helpers, and file/path boundary cases.
-*** TODO [#C] Add coverage for =custom-text-enclose.el= (35.2%, 51/145) :tests:
+*** TODO [#B] Add coverage for =custom-text-enclose.el= (35.2%, 51/145) :tests:
Many wrappers are tested, but coverage is still low. Identify untested commands
and add table-driven region/buffer boundary cases.
-*** TODO [#C] Add coverage for =hugo-config.el= (39.6%, 38/96) :tests:
+*** TODO [#B] Add coverage for =hugo-config.el= (39.6%, 38/96) :tests:
Extend metadata/template tests to draft collection, path derivation, and command
wrapper behavior with file/process calls stubbed.
-*** TODO [#C] Add coverage for =org-refile-config.el= (41.2%, 21/51) :tests:
+*** TODO [#B] Add coverage for =org-refile-config.el= (41.2%, 21/51) :tests:
Build on target-building tests. Cover org-mode enforcement, missing files, and
refile target edge cases.
-*** TODO [#C] Add coverage for =org-contacts-config.el= (45.6%, 36/79) :tests:
+*** TODO [#B] Add coverage for =org-contacts-config.el= (45.6%, 36/79) :tests:
Extend email parsing/finalize tests to contact lookup, capture field handling,
and malformed contact data.
-*** TODO [#C] Add coverage for =transcription-config.el= (46.3%, 75/162) :tests:
+*** TODO [#B] Add coverage for =transcription-config.el= (46.3%, 75/162) :tests:
Existing helper tests are strong; add start/stop/process lifecycle tests with
process creation and sentinels stubbed.
-*** TODO [#C] Add coverage for =music-config.el= (46.8%, 130/278) :tests:
+*** TODO [#B] Add coverage for =music-config.el= (46.8%, 130/278) :tests:
Coverage is broad but below 50%. Target remaining playlist mutation,
navigation, and MPD side-effect paths with process calls stubbed.
@@ -3670,17 +3207,17 @@ maildir shortcuts, bookmarks, and safe command setup without sending mail.
High-value missing module. Cover gptel persistence, autosave path selection, and
load/save error behavior with filesystem calls isolated.
-*** TODO [#C] Add or triage first coverage for =auth-config.el= (missing from SimpleCov) :tests:
+*** TODO [#B] Add or triage first coverage for =auth-config.el= (missing from SimpleCov) :tests:
Review for testable cache/debug helpers versus package glue. Add tests for
helpers; document any config-only surface as intentionally untested.
-*** TODO [#C] Add or triage first coverage for =calibredb-epub-config.el= (missing from SimpleCov) :tests:
+*** TODO [#B] Add or triage first coverage for =calibredb-epub-config.el= (missing from SimpleCov) :tests:
Review EPUB preference helpers and calibredb/nov hooks. Add tests around pure
helpers; avoid brittle package-load assertions.
-*** TODO [#C] Add or triage first coverage for =chrono-tools.el= (missing from SimpleCov) :tests:
+*** TODO [#B] Add or triage first coverage for =chrono-tools.el= (missing from SimpleCov) :tests:
Identify date/time helpers and command formatting logic. Add deterministic tests
with current time stubbed.
@@ -3690,57 +3227,57 @@ with current time stubbed.
Cover custom dashboard commands such as single-window/dashboard-only behavior
with buffer/window operations isolated.
-*** TODO [#D] Triage =diff-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =diff-config.el= as missing from SimpleCov :tests:
Mostly likely package/keybinding glue. Add tests only for local helper logic; if
none exists, document as no unit tests appropriate.
-*** TODO [#C] Add or triage first coverage for =dwim-shell-config.el= (missing from SimpleCov) :tests:
+*** TODO [#B] Add or triage first coverage for =dwim-shell-config.el= (missing from SimpleCov) :tests:
Review DWIM shell command selection and buffer/process helpers. Stub shell
launches and cover fallback/error cases.
-*** TODO [#D] Triage =elfeed-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =elfeed-config.el= as missing from SimpleCov :tests:
Check for project-owned feed/search helpers. If it is only elfeed setup,
document as low-value for unit coverage.
-*** TODO [#D] Triage =erc-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =erc-config.el= as missing from SimpleCov :tests:
Check for local IRC command helpers. If the file is package setup only, document
as intentionally untested.
-*** TODO [#C] Add or triage first coverage for =eshell-config.el= (missing from SimpleCov) :tests:
+*** TODO [#B] Add or triage first coverage for =eshell-config.el= (missing from SimpleCov) :tests:
Cover local eshell helper functions and command aliases where possible. Avoid
tests that depend on a live shell session.
-*** TODO [#D] Triage =eww-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =eww-config.el= as missing from SimpleCov :tests:
Review for local URL/browser helpers. If configuration-only, document as
low-value for unit coverage.
-*** TODO [#D] Triage =flycheck-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =flycheck-config.el= as missing from SimpleCov :tests:
Prefer testing only project-owned predicate/setup helpers. Do not test flycheck
package internals.
-*** TODO [#D] Triage =flyspell-and-abbrev.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =flyspell-and-abbrev.el= as missing from SimpleCov :tests:
Look for local dictionary/abbrev helpers. If the file only wires modes/hooks,
document that no unit tests are appropriate.
-*** TODO [#D] Triage =font-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =font-config.el= as missing from SimpleCov :tests:
Check for font-selection helpers; otherwise classify as environment-specific UI
configuration.
-*** TODO [#D] Triage =games-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =games-config.el= as missing from SimpleCov :tests:
Check for project-owned game command wrappers. If it is only package setup,
document as low-value.
-*** TODO [#C] Add or triage first coverage for =gloss-config.el= (missing from SimpleCov) :tests:
+*** TODO [#B] Add or triage first coverage for =gloss-config.el= (missing from SimpleCov) :tests:
Review glossary lookup/parsing helpers. Add pure tests where possible and smoke
test interactive commands with completion stubbed.
@@ -3755,85 +3292,85 @@ commands and buffer-selection behavior with display functions stubbed.
High-value missing module. Cover lookup/formatting helpers and error behavior
for unknown symbols/topics.
-*** TODO [#D] Triage =httpd-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =httpd-config.el= as missing from SimpleCov :tests:
Check for local server helpers. If it only configures simple-httpd, document as
configuration-only.
-*** TODO [#D] Triage =latex-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =latex-config.el= as missing from SimpleCov :tests:
Review for local compile/view helper logic. Avoid asserting package setup unless
there are project-owned predicates.
-*** TODO [#D] Triage =ledger-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =ledger-config.el= as missing from SimpleCov :tests:
Check for project-owned ledger helpers; otherwise document as mode setup.
-*** TODO [#C] Add or triage first coverage for =local-repository.el= (missing from SimpleCov) :tests:
+*** TODO [#B] Add or triage first coverage for =local-repository.el= (missing from SimpleCov) :tests:
Review repository path/discovery helpers and add filesystem-backed tempdir tests
for meaningful local logic.
-*** TODO [#D] Triage =markdown-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =markdown-config.el= as missing from SimpleCov :tests:
Check for local markdown command helpers. If it is only mode configuration,
document as no unit tests appropriate.
-*** TODO [#C] Add or triage first coverage for =media-utils.el= (missing from SimpleCov) :tests:
+*** TODO [#B] Add or triage first coverage for =media-utils.el= (missing from SimpleCov) :tests:
Original audit called this out with process boundaries. Cover launcher/command
selection helpers with external commands stubbed.
-*** TODO [#C] Add or triage first coverage for =mu4e-org-contacts-integration.el= (missing from SimpleCov) :tests:
+*** TODO [#B] Add or triage first coverage for =mu4e-org-contacts-integration.el= (missing from SimpleCov) :tests:
Cover contact lookup/insert integration with mu4e and org-contacts calls
stubbed.
-*** TODO [#C] Add or triage first coverage for =mu4e-org-contacts-setup.el= (missing from SimpleCov) :tests:
+*** TODO [#B] Add or triage first coverage for =mu4e-org-contacts-setup.el= (missing from SimpleCov) :tests:
Cover setup helpers and contact field defaults where project-owned logic exists.
-*** TODO [#D] Triage =org-agenda-config-debug.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =org-agenda-config-debug.el= as missing from SimpleCov :tests:
Check for debug helper functions worth testing. If it is ad-hoc diagnostics,
document as low-value.
-*** TODO [#D] Triage =org-babel-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =org-babel-config.el= as missing from SimpleCov :tests:
Review for project-owned org-babel helper logic. Avoid tests that only assert
language registration.
-*** TODO [#D] Triage =org-drill-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =org-drill-config.el= as missing from SimpleCov :tests:
Check for local drill helpers beyond package setup. Add characterization tests
only for those helpers.
-*** TODO [#D] Triage =org-export-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =org-export-config.el= as missing from SimpleCov :tests:
Review export option/path helpers. If configuration-only, document as no unit
tests appropriate.
-*** TODO [#D] Triage =pdf-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =pdf-config.el= as missing from SimpleCov :tests:
Check for local PDF helper commands. Avoid tests tied to external PDF tools
unless command construction can be isolated.
-*** TODO [#D] Triage =popper-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =popper-config.el= as missing from SimpleCov :tests:
Review popup classification helpers. Add tests for predicates; document pure
package setup as intentionally untested.
-*** TODO [#D] Triage =prog-general.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =prog-general.el= as missing from SimpleCov :tests:
Check for local development command helpers beyond global key setup. Add tests
only for project-owned logic.
-*** TODO [#D] Triage =prog-lisp.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =prog-lisp.el= as missing from SimpleCov :tests:
Review Lisp-mode helper behavior. If it is only hooks/mode setup, document as
low-value.
-*** TODO [#D] Triage =prog-training.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =prog-training.el= as missing from SimpleCov :tests:
Check for exercise/training command helpers and add characterization tests if
present.
@@ -3848,20 +3385,20 @@ URL parsing, and process error paths with external commands stubbed.
High-value missing module. Cover kill-ring formatting, selection behavior, and
empty-ring boundaries.
-*** TODO [#D] Triage =text-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =text-config.el= as missing from SimpleCov :tests:
Review for local text helper commands. If it only configures packages/hooks,
document as no unit tests appropriate.
-*** TODO [#D] Triage =tramp-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =tramp-config.el= as missing from SimpleCov :tests:
Check for local TRAMP path helpers. Avoid tests requiring remote connections.
-*** TODO [#C] Add or triage first coverage for =vc-config.el= (missing from SimpleCov) :tests:
+*** TODO [#B] Add or triage first coverage for =vc-config.el= (missing from SimpleCov) :tests:
Review project-owned VC helpers and command wrappers. Stub git/process calls.
-*** TODO [#D] Triage =weather-config.el= as missing from SimpleCov :tests:
+*** TODO [#B] Triage =weather-config.el= as missing from SimpleCov :tests:
Check for request/format helpers. Avoid live network tests; stub weather calls.
@@ -4050,22 +3587,476 @@ The 2026-05-11 lorem-optimum perf work (=7f353e9=) dropped the `:slow' tags from
*** DONE [#B] Verify Nov EPUB renders at the right width on first open :bug:
There WAS a regression, deeper than =b3b537f=: `cj/nov--text-width-for-window' computed the column from `window-body-width' (post-margin), so `cj/nov-update-layout' (on `window-configuration-change-hook') shrank the column on every pass — a feedback loop bottoming out at `cj/nov-min-text-width' (40 cols) regardless of `cj/nov-margin-percent'. Fixed 2026-05-12 in =1c5c8bd= "fix(nov): rework the EPUB reading-width layout": width now from the window's natural column count (idempotent), pure `cj/nov--text-width' helper + regression test, `cj/nov-margin-percent' default 12 (~76% text), `b3b537f's `(nov-render-document)' re-added for the cold open, `cj/nov-update-layout' made a command, and `+'/`='/`-'/`_' added in `nov-mode-map' to adjust the width live (50%..100%). Visual confirm in real Emacs still pending Craig's restart.
-*** DONE [#C] Surface `cj/slack-message-add-reaction' errors outside a Slack buffer :ux:
+*** DONE [#B] Surface `cj/slack-message-add-reaction' errors outside a Slack buffer :ux:
`cj/slack-message-add-reaction' (C-; S !, added in =bbd1b73=) silently no-ops when `slack-current-buffer' is nil — e.g. if the binding fires outside a Slack buffer. The `when-let*' chain just bails with no feedback. Add a `user-error "Not in a Slack buffer"' (and the same for `slack-buffer-team' returning nil) so the misuse surfaces instead of being swallowed.
-*** DONE [#C] Rename `cj/lsp--disable-eldoc-hover' to `cj/lsp--remove-eldoc-provider' :refactor:
+*** DONE [#B] Rename `cj/lsp--disable-eldoc-hover' to `cj/lsp--remove-eldoc-provider' :refactor:
The function (added in =96d5d6a=) removes one specific provider — `lsp-eldoc-function' — from the buffer-local `eldoc-documentation-functions'. If lsp-mode ever adds another eldoc provider, the current function wouldn't catch it; the name promises more than it does. Rename to match what it actually does and update the `add-hook' callsite + the regression test.
-*** DONE [#C] Add `bats' test infra and cover `scripts/setup-email.sh' helpers :tests:
+*** DONE [#B] Add `bats' test infra and cover `scripts/setup-email.sh' helpers :tests:
The 2026-05-11 email-setup work (=eddc103=) added `install_encrypted_password' and `decrypt_password' — cleanly factored (filenames in, file-or-`exit 1' out) but untested, since the repo has no shell-test infrastructure. With a temp `$PASSWORD_DEST_DIR' and mocked `gpg'/`cp', they'd test cleanly. Add `bats' (or pick an alternative), wire a `make test-shell' target, and cover the two helpers plus the dest-exists-skip and missing-source-fails paths.
-*** DONE [#C] Split the mu4e attachment workflow out of `mail-config.el' :refactor:
+*** DONE [#B] Split the mu4e attachment workflow out of `mail-config.el' :refactor:
The 2026-05-11 mu4e attachment commit (=1aa8d0f=) added ~247 lines to `mail-config.el' for a self-contained attachment-save UI (helpers + three commands + a `special-mode'-derived selection buffer). None of it depends on the rest of `mail-config'. As that file grows, moving this into `modules/mu4e-attachments.el' (or `mail-attachments-lib.el', matching the `-lib.el' convention) would keep both files easier to read. The seam is clean.
-*** DONE [#C] Clear marks (or auto-quit) after `cj/mu4e-attachment-selection-save-marked' :ux:
+*** DONE [#B] Clear marks (or auto-quit) after `cj/mu4e-attachment-selection-save-marked' :ux:
After saving the marked attachments, the `*mu4e attachments*' buffer (=1aa8d0f=) stays open with the same marks intact — pressing `s' again re-saves the same set silently. Decide what the workflow wants: auto-`quit-window' after a successful save, or clear the marks and stay so the user can save another batch. Right now it does neither.
** DONE [#D] Create print function for dirvish bound to uppercase P :feature:
Add a print function that works on printable files (PDF, txt, org, etc.) and bind it to uppercase P in dirvish-mode. Should detect file type and use appropriate print command (lpr for text files, print dialog for PDFs, etc.).
** DONE [#D] Collapse the duplicated per-file test loop in the Makefile :chore:
=test-unit=, =test-integration=, and =coverage= each carry a near-identical ~40-line shell loop (run each file in its own Emacs, count passes, collect failures, print a summary box). The three drifted once already (the =:perf= tag filter had to be added in three places). Extract a single =define=d shell function or a helper recipe parametrized by test list + extra =-l= args + label, and have the three targets call it. Cosmetic — the Makefile works — so low priority. Noticed 2026-05-12 while adding =make benchmark=.
+** DONE [#A] Add Telegram Messaging
+https://github.com/zevlg/telega.el
+Make sure there is a setup script to run, so that the docker container can be installed post emacs dotfiles repository clone on a fresh install
+also, let's add an icon to the dashboard for this. perhaps this is the beginning of the third row? If so, add a child task to review and balance the dashboard icons
+
+Shipped this session:
+- =modules/telega-config.el= -- =use-package telega= with
+ =telega-use-docker t=; launcher on =C-; G= (=C-; t= and =C-; m t=
+ were both taken).
+- Dashboard Row 3 added with the Telegram icon; dashboard-mode-map =g=
+ key launches =telega=.
+- =scripts/setup-telega.sh= -- verifies docker presence + daemon
+ reachability; pulls =$TELEGA_DOCKER_IMAGE= when set, otherwise
+ announces =M-x telega-server-build= for the in-Emacs build path.
+ 7 bats tests in =tests/test-setup-telega.bats= (docker stubbed).
+- Auth (phone + verification code) is interactive on first =M-x telega=
+ -- not scripted.
+
+*** DONE [#A] Add =scripts/setup-telega.sh= for TDLib docker container :feature:
+Pull or build the telega TDLib container so a fresh-clone install can
+run telega without a system-wide TDLib build. Mirror the
+=scripts/setup-email.sh= pattern: =main()= wrapped in a
+=BASH_SOURCE == 0= guard so the script is sourceable for bats tests;
+bats test file =tests/test-setup-telega.bats= with =docker= stubbed.
+
+*** DONE [#A] Review and balance dashboard icon layout :refactor:
+CLOSED: [2026-05-14 Thu]
+Adding the Telegram icon started a third row that has only one entry.
+Decide whether to (a) leave it asymmetric and let the row fill in as
+new launchers arrive, (b) move an existing icon down to balance 5/5/2
+or similar, or (c) reorganize by category (work / read / chat / play).
+
+Picked (c) -- the categories actually exist and a 4/4/4 grid
+balances cleanly:
+- Row 1 Work: Code / Files / Terminal / Agenda
+- Row 2 Read & Learn: Feeds / Books / Flashcards / Music
+- Row 3 Communication: Email / IRC / Slack / Telegram
+
+Drive-by fix in the same commit: Music had an icon but no
+`dashboard-mode-map' keybinding, so the visual launcher couldn't be
+fired without the mouse. Added =m=. Reordered the existing
+`define-key' calls to mirror the row layout so reading the keymap
+top-to-bottom matches the icons left-to-right.
+Surfaced when Telegram landed in Row 3 alone.
+** DONE [#B] Add VERIFY and DOING blocks to the main agenda view :feature:
+
+The main agenda "d" command (=cj/main-agenda-display=, F8) currently
+renders four blocks: OVERDUE -> HIGH PRIORITY UNRESOLVED -> SCHEDULE
+-> PRIORITY B. Insert two new blocks around SCHEDULE so a glance at
+the daily view also surfaces what's in flight and what's waiting on a
+manual check:
+
+- Above SCHEDULE: all tasks with TODO state VERIFY (header:
+ =VERIFICATION=).
+- Below SCHEDULE: all tasks with TODO state DOING (header:
+ =IN-PROGRESS=).
+
+Resulting block order:
+
+ OVERDUE -> HIGH PRIORITY -> *VERIFICATION* -> SCHEDULE -> *IN-PROGRESS* -> PRIORITY B
+
+Decisions:
+- *Scope*: same as the other blocks -- every entry in
+ =org-agenda-files=, no per-project filter.
+- *Scheduled / deadlined entries*: included. A VERIFY task with a
+ scheduled date for today appears in both the VERIFICATION block and
+ the SCHEDULE block. Mirrors the HIGH PRIORITY block's behavior.
+- *Habit / PROJECT skips*: skip habits via
+ =cj/org-skip-subtree-if-habit=. Don't skip PROJECT-keyword entries
+ (the =(todo "VERIFY")= and =(todo "DOING")= match is keyword-exact
+ so PROJECT parents wouldn't appear anyway, and a PROJECT in VERIFY
+ state would be deliberate).
+
+Implementation locations:
+- =modules/org-agenda-config.el= -- two new entries inside
+ =org-agenda-custom-commands= "d" block, each a =(todo "STATE" ...)=
+ with =org-agenda-overriding-header=, the shared
+ =cj/--main-agenda-prefix-format=, and the habit skip-function.
+- =modules/org-agenda-config.el= -- two header defvars
+ (=cj/main-agenda-verify-title= / =cj/main-agenda-doing-title=) for
+ symmetry with =cj/main-agenda-overdue-title= etc.
+
+Regression coverage:
+- Extend =tests/test-org-agenda-config-skip-functions.el= with
+ structural assertions: the "d" command has six blocks in the
+ expected order, the new VERIFICATION / IN-PROGRESS blocks reference
+ the shared prefix-format symbol, carry the right
+ =org-agenda-overriding-header=, and run the habit skip.
+** DONE [#A] Org Agenda fixes :bug:
+*** 2026-05-13 Wed @ 13:05:21 -0500 Skip CANCELLED entries from main agenda SCHEDULE
+see the following screenshot
+/home/cjennings/pictures/screenshots/2026-05-13_071428.png
+
+Fix shipped on main: commit =8e57950=. Added an org-agenda-skip-function
+to the SCHEDULE block of the "d" command in =org-agenda-custom-commands=
+that filters entries with TODO state CANCELLED. Scope is deliberately
+narrow -- DONE and FAILED scheduled tasks still render.
+
+Tests in =tests/test-org-agenda-config-skip-functions.el= (Normal +
+Boundary) lock in the configuration form on the agenda block and
+verify the other blocks aren't accidentally carrying the same skip.
+*** DONE [#A] Refactor: extract org-agenda-prefix-format literal :refactor:
+=modules/org-agenda-config.el= currently inlines =" %i %-15:c%?-15t% s"=
+across four blocks of =org-agenda-custom-commands= (overdue, hi-pri,
+schedule, priority-B). Extract into a defvar (e.g.
+=cj/--main-agenda-prefix-format=) and reference it from each block.
+Surfaced during the audit for the CANCELLED-schedule fix.
+
+Fix: new =cj/--main-agenda-prefix-format= defvar in
+=modules/org-agenda-config.el=; all four blocks of the "d" command now
+reference the symbol instead of inlining the literal. Regression test
+in =tests/test-org-agenda-config-skip-functions.el= walks the blocks
+and asserts each =org-agenda-prefix-format= entry resolves to the
+shared symbol -- so a future tweak to one block can't silently diverge
+from the others.
+*** 2026-05-13 Wed @ 13:27:39 -0500 Clear dedicated before toggling window split
+Reproduction steps
+- open an org file (I was using this projects's todo.org file
+- hit the f8 button to open the agenda view. it opens fine.
+- toggle-window-split
+>>> The agenda displays on both panes after the toggle.
+see snapshot below
+/home/cjennings/pictures/screenshots/2026-05-13_071603.png
+
+Fix shipped on main: commit =97f0f8e=. Root cause was the dedicated
+=*Org Agenda*= window (set via =display-buffer-alist= rule) rejecting
+the internal =set-window-buffer= swap. The non-dedicated buffer never
+crossed and both panes ended up showing the agenda.
+
+=modules/ui-navigation.el= now clears dedicated on both windows at the
+top of =toggle-window-split= before the swap. The toggle is an
+explicit layout change, so preserving per-window dedicated through it
+would just re-trigger the same wedge on the next invocation.
+
+Tests in =tests/test-ui-navigation--toggle-window-split.el= (5 tests,
+Normal + Boundary) cover the no-dedicated baseline, the bug-trigger,
+post-toggle cleared state, and the 1-window / 3-window no-op cases.
+Verified red against the unfixed code (the bug-trigger test errored
+=Window is dedicated to '*test-toggle-b*'=) before applying the fix.
+
+Live verification pending: =M-x load-file modules/ui-navigation.el=,
+then walk through F8 + M-S-t in a fresh session.
+*** DONE [#A] Enhancement: replace todo indicators with project name
+In the overdue section, the high priority section, and the priority B section, each of the entries has a todo: indicator, which is the name of the file.
+This is not useful. Based on how I'm using emacs, every entry is likely to come from a file named todo.org.
+A much preferrable option would be to have the project's name there instead.
+so, for instance this todo.org file would show "emacs.d" as the project name in place of todo.
+
+see snapshot below for an example of the current state.
+/home/cjennings/pictures/screenshots/2026-05-13_071840.png
+
+Fix: =cj/--org-todo-category-from-file= +
+=cj/--org-set-todo-category= in =modules/org-agenda-config.el=
+hook =org-category= buffer-locally to the parent directory's
+basename (with a single leading dot stripped, so =.emacs.d= reads
+as =emacs.d=) whenever a todo.org file opens. Explicit
+=#+CATEGORY:= still wins. 14 tests in
+=tests/test-org-agenda-config-category.el= cover normal /
+boundary / error paths; full =make test-unit= green.
+** DONE [#A] Save journal buffer after marking a task DONE :bug:
+CLOSED: [2026-05-14 Thu]
+
+When a task transitions to a done state, =cj/org-roam-copy-todo-to-today=
+in =modules/org-roam-config.el= refiles a copy of the heading into the
+day's roam journal (creating the file if missing) -- example:
+=/home/cjennings/sync/org/roam/journal/2026-05-14.org=. The journal
+buffer is left modified-but-unsaved, so closing Emacs always prompts
+about open unsaved buffers with no obvious source.
+
+Function intends to save via two routes:
+- =org-after-refile-insert-hook= let-bound to =#'save-buffer= -- a
+ single-function value rather than a list. =run-hooks= calls a bare
+ function value, so this should fire, but verify it does in the
+ capture+refile context (and that it runs in the target buffer, not
+ the source).
+- The =:config= block of =use-package org-refile= advises =org-refile=
+ =:after= with =org-save-all-org-buffers=. That only runs once
+ =org-refile= is loaded; if the DONE transition fires before
+ org-refile's =:defer .5= elapses, the advice isn't attached yet.
+
+Fix candidates:
+- Drop the let-binding and call =(save-buffer)= explicitly in the
+ target buffer after the =org-refile= call, before
+ =save-window-excursion= unwinds. Deterministic, doesn't depend on
+ hook timing.
+- Or eager-load =org-refile= so the =:after= advice attaches before any
+ DONE transition can fire.
+
+Test: mark a TODO done from a non-journal buffer, then check
+=buffer-modified-p= on the dated journal buffer. Should be nil.
+** DONE [#B] Extend dired/dirvish =T= to transcribe videos, not just audio :feature:
+CLOSED: [2026-05-14 Thu]
+
+Today =T= on an audio file in dired/dirvish triggers
+=cj/transcribe-audio-at-point= and only accepts files matching
+=cj/audio-file-extensions= (=cj/--audio-file-p= rejects anything
+else with a =user-error=). Want the same one-key flow on video
+files -- so a =.mp4= or =.mkv= recording can be transcribed without
+hand-extracting the audio track first.
+
+Likely shape:
+- New =cj/video-file-extensions= in user-constants.el (mp4, mkv,
+ mov, webm, avi, m4v, ...).
+- =cj/--video-file-p= sibling of =cj/--audio-file-p=.
+- =cj/--start-transcription-process= (or a wrapper) detects video,
+ shells out to ffmpeg to extract the audio track to a temp file
+ (=ffmpeg -i in.mp4 -vn -acodec copy out.m4a= or similar; pick a
+ codec the backend accepts), then transcribes the temp file and
+ cleans up.
+- =cj/transcribe-audio-at-point= accepts both audio and video via
+ =(or (cj/--audio-file-p f) (cj/--video-file-p f))=; the
+ surrounding pipeline knows when to insert the ffmpeg step.
+
+Open design questions:
+- Keep the function named =transcribe-audio-at-point= (treats video
+ as "audio-bearing") or rename to =transcribe-media-at-point= and
+ add an alias? Rename probably cleaner.
+- ffmpeg availability check + =cj/executable-find-or-warn= pattern
+ on first use.
+- Where the temp audio file lives -- alongside the video (visible),
+ or =temporary-file-directory= (clean). Probably the latter for
+ videos the user doesn't want to clutter.
+- Do we keep the temp audio after transcription, or always delete?
+ The log file already retains diagnostic info; extracted audio is
+ derivable. Default to delete; offer a custom to keep.
+
+Test surface: =cj/--video-file-p= happy/edge cases, the ffmpeg
+extract step (stub =call-process=), and the dispatch in
+=cj/transcribe-audio-at-point= against a video path.
+** DONE [#C] Surface org narrowing + sparse-tree under =C-; O= :refactor:
+CLOSED: [2026-05-14 Thu]
+Final layout flatter than the original proposal: no =n= or =s=
+sub-prefixes. Lowercase letters create / narrow / sparse-tree;
+the same letter capitalized cancels. `n' / `N' = narrow / widen.
+`s' / `S' = match-sparse-tree / show-all. `t' / `T' =
+show-todo-tree / show-all (both capitals point at the same
+`org-show-all' so the mental model is "capital cancels the
+lowercase I just ran"). `R' = `org-reveal' (no lowercase pair --
+`r' is the table-row sub-prefix); F2 (the old reveal binding) is
+freed up. Sibling-stepping is on `>' / `<' at the top level.
+
+Four new ERT assertions in
+=tests/test-org-config-keymap-ownership.el= lock the shape.
+
+The narrowing and sparse-tree commands already exist in
+=modules/org-config.el=, but they're bound only inside the
+=:bind (:map org-mode-map ...)= block and scattered across `C-c'
+shortcuts -- nothing in `cj/org-map' (`C-; O') surfaces them, so
+which-key never shows them and discoverability is poor.
+
+Existing bindings worth promoting (org-config.el ~line 141-150):
+- =C-\\= =org-match-sparse-tree=
+- =C-c N= =org-narrow-to-subtree=
+- =C-c >= =cj/org-narrow-forward=
+- =C-c <= =cj/org-narrow-backwards=
+- =C-c <ESC>= =widen=
+- =<f2>= =org-reveal= (the reveal-narrowed-context command,
+ not org-reveal-config.el)
+
+Proposal:
+
+Add a sub-menu under `C-; O':
+
+- `C-; O n' -- narrow operations (sub-prefix)
+ - `n s' narrow to subtree
+ - `n e' narrow to element
+ - `n >' narrow forward sibling
+ - `n <' narrow backward sibling
+ - `n w' widen
+
+- `C-; O s' -- sparse-tree operations (sub-prefix)
+ - `s s' org-match-sparse-tree (by tag/property/todo match)
+ - `s t' show all TODOs
+ - `s p' show entries with a priority
+ - `s r' org-reveal (open the surrounding context of point)
+
+Add the which-key labels alongside. Keep the existing `C-c'
+bindings as-is for muscle memory.
+
+Open question: should `cj/org-narrow-forward' /
+`cj/org-narrow-backwards' have their own sub-letters under `n', or
+just be under `n >' / `n <' as written above? The arrow-symbol
+keys read naturally as "next/previous" so probably keep them.
+** DONE [#B] F9 toggle should restore single-window layout for AI-vterm :bug:
+
+When the AI-vterm buffer is the only window in the frame (e.g. after =C-x 1=) and
+F9 is pressed, =cj/ai-vterm= buries the buffer (correct), but the next F9
+redisplays the agent in a split rather than restoring the single-window full-frame
+layout. F9 should toggle off/on while preserving the lone-window state.
+
+Fix: new =cj/--ai-vterm-last-was-bury= flag in =modules/ai-vterm.el=.
+The toggle-off branch sets it to t when =one-window-p= is true (bury
+path) or nil when =delete-window= runs. =cj/--ai-vterm-display-saved=
+checks the flag at toggle-on: if t and the frame is still single-window,
+it replaces the selected window's buffer in place via =set-window-buffer=
+rather than splitting via =display-buffer-in-direction=. Flag is
+consumed (cleared) by either branch so it never stays stale.
+
+5 tests in =tests/test-ai-vterm--single-window-toggle.el= cover:
+flag set on bury, flag cleared on delete-window, flag respected only
+when still one-window, flag not set when bury didn't run, and the
+end-to-end roundtrip. Full =make test-unit= green.
+** DONE [#B] AI-vterm scrollback history should replace agent buffer in place :feature:
+
+When viewing the scrollback history of an AI-vterm buffer, the history view should
+replace the live agent buffer in the same window rather than splitting or popping
+a separate window. Goal: read past output without losing the agent's frame slot,
+then snap back to the live buffer when done.
+
+Decisions on the open questions:
+- *Trigger*: reused the existing =cj/vterm-tmux-history= command (=C-; x h=).
+ No new command -- it already captures the tmux pane and runs from any
+ vterm buffer including agents.
+- *Round-trip*: =q= / =<escape>= / =C-g= already restore the origin in
+ the same window via =cj/vterm-tmux-history-quit=. Same key as the
+ scrollback mode's other exits.
+- *F9 integration*: deferred. Pressing F9 in history mode now treats the
+ history buffer as non-agent (its name is =*vterm tmux history: ...*=,
+ not =agent [...]=) so dispatch falls through to redisplay-recent + a
+ saved-direction split. A user who wants to toggle agent off should
+ press =q= first, then F9. Filed as a follow-up if it bites.
+
+Fix: =modules/vterm-config.el= -- one line. =pop-to-buffer buffer=
+became =switch-to-buffer buffer= so the history view replaces the origin
+in the selected window instead of going through display-buffer's split
+logic. Quit was already in-place via =set-window-buffer=.
+
+New test in =tests/test-vterm-tmux-history.el= asserts the selected
+window's buffer becomes the history buffer with no extra window
+created (=one-window-p= still t). Existing tests dropped their
+=pop-to-buffer= stub since =switch-to-buffer= works directly in batch.
+Full =make test-unit= green.
+** DONE [#B] Add ERT coverage for modules below 70% :tests:
+CLOSED: [2026-05-14 Thu]
+
+Coverage snapshot from =make coverage= on 2026-05-14 (post-push):
+=5958/6861= executable lines covered (=86.8%=) across 73 tracked module files.
+
+All tracked modules now sit at 70% or above. The two stragglers
+(=system-defaults.el= and =system-commands.el=) were resolved by
+attacking the instrumentation pattern rather than adding more tests:
+
+- =system-defaults.el= 8.3% -> 100%: switched the helper-functions
+ test from per-test =(load ...)= reloads inside =cl-letf= to a
+ single top-level =(require 'system-defaults)= wrapped in stubs,
+ so undercover sees one load instead of N reloads.
+
+- =system-commands.el= 69.4% -> 100%: switched =cj/system-cmd='s
+ =(interactive (list (read-shell-command ...)))= to the equivalent
+ string spec =(interactive "sSystem command: ")=, which lets
+ undercover instrument the function body that was previously
+ showing as 0 hits, plus added one test that captures and invokes
+ the =run-at-time= lambda body directly.
+
+Lesson for future coverage work: when ERT tests exercise a function
+but the body shows 0 hits, suspect undercover/edebug instrumentation
+failing on a specific Lisp form (=interactive= with a destructured
+spec, backquote-destructured =pcase-let=, etc.), not the tests.
+
+*** DONE [#B] Add ERT tests for =modules/prog-python.el= (21/21, 100.0%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/selection-framework.el= (3/3, 100.0%) :tests:
+*** DONE [#B] Add ERT tests for =modules/keyboard-compat.el= (29/29, 100.0%) :tests:
+*** DONE [#B] Add ERT tests for =modules/prog-webdev.el= (21/21, 100.0%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/calibredb-epub-config.el= (95/133, 71.4%) :tests:
+*** DONE [#B] Add ERT tests for =modules/system-defaults.el= (12/12, 100.0%) :tests:
+CLOSED: [2026-05-14 Thu]
+Rewrote =tests/test-system-defaults-functions.el= to call
+=(require 'system-defaults)= once at top level (with the
+side-effecting primitives stubbed via =cl-letf= wrapping the
+require) instead of looping per-test =(load ...)= reloads inside
+=cl-letf=. Undercover only saw the first load, so the function
+bodies showed as uncovered even though every test ran them. Loading
+once -- with the same stubs -- fixed the gauge and pulled coverage
+to 12/12. Added two more tests to exercise the previously-unhit
+list-without-comp guard and the non-string-message format branch.
+*** DONE [#B] Add ERT tests for =modules/ui-navigation.el= (46/48, 95.8%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/prog-go.el= (24/27, 88.9%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/system-commands.el= (51/51, 100.0%) :tests:
+CLOSED: [2026-05-14 Thu]
+The =interactive= form on =cj/system-cmd= was =(interactive (list
+(read-shell-command "...")))= -- a destructured list form. Edebug-
+based undercover instrumentation didn't see past it, so the function
+body registered 0 hits even though the tests called it directly.
+Switched to the equivalent string spec =(interactive "sSystem
+command: ")= and the body instrumented as expected. Added one more
+test that captures the =run-at-time= lambda inside
+=cj/system-cmd-restart-emacs= and invokes it directly so the inner
+=call-process-shell-command= branch registers as covered.
+*** DONE [#B] Add ERT tests for =modules/external-open.el= (31/33, 93.9%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/org-webclipper.el= (59/59, 100.0%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/system-utils.el= (26/26, 100.0%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/org-reveal-config.el= (68/81, 84.0%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/coverage-elisp.el= (19/19, 100.0%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/org-noter-config.el= (72/99, 72.7%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/ai-config.el= (160/191, 83.8%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/slack-config.el= (70/74, 94.6%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/org-roam-config.el= (80/80, 100.0%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/custom-text-enclose.el= (145/145, 100.0%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/dirvish-config.el= (174/185, 94.1%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/hugo-config.el= (88/96, 91.7%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/org-refile-config.el= (50/51, 98.0%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/org-contacts-config.el= (64/79, 81.0%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/transcription-config.el= (150/162, 92.6%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/music-config.el= (213/278, 76.6%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/mail-config.el= (14/19, 73.7%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/custom-buffer-file.el= (167/212, 78.8%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/org-agenda-config.el= (103/104, 99.0%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/host-environment.el= (53/57, 93.0%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/custom-ordering.el= (101/101, 100.0%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/ui-theme.el= (39/40, 97.5%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/custom-comments.el= (317/358, 88.5%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/ui-config.el= (28/31, 90.3%) :tests:
+*** DONE [#B] Add ERT tests for =modules/custom-whitespace.el= (80/82, 97.6%) :tests:
+*** DONE [#B] Add ERT tests for =modules/jumper.el= (97/99, 98.0%) :tests:
+*** DONE [#B] Add ERT tests for =modules/test-runner.el= (160/222, 72.1%) :tests:
+CLOSED: [2026-05-14 Thu]
+*** DONE [#B] Add ERT tests for =modules/browser-config.el= (62/76, 81.6%) :tests:
+** DONE [#A] Fix Python tree-sitter font-lock query syntax error :bug:
+CLOSED: [2026-05-14 Thu]
+
+Diagnosed 2026-04-26 — paused at /start-work Gate 2. Root cause was system-level, not in =.emacs.d=: Emacs 30.2 + tree-sitter library 0.26.x predicate-syntax mismatch. Emacs sent =#match= (no =?= suffix), tree-sitter 0.26 rejected anything but =#match?=. Affected every =:match=, =:equal=, =:pred= predicate in every treesit-aware mode, not just Python.
+
+Full investigation, reproduction, and fix-option analysis in:
+
+[[file:docs/python-treesit-predicate-mismatch.txt][docs/python-treesit-predicate-mismatch.txt]]
+
+Resolved 2026-05-14 by an upstream emacs Arch-package revision bump (=30.2-2= → =30.2-3=, shipped 2026-05-03) — most likely carrying a downstream patch to =treesit.c='s predicate translation. Bug no longer reproduces: the exact failing query runs cleanly via =treesit-query-capture=, and =font-lock-ensure= on a real Python file under =python-ts-mode= completes with no =treesit-query-error=. No local override applied to =modules/prog-python.el=. Matches option A from the investigation's fix-options ("WAIT FOR UPSTREAM EMACS FIX").