diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-14 21:45:32 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-14 21:45:32 -0500 |
| commit | a5519bc151700d1a76eba9baebe541c06958e9ce (patch) | |
| tree | 4aa1325acb417626be7b0c54d5636160e875847a /todo.org | |
| parent | 7807e5318b2ca352d65651ab4ea25914a38299d7 (diff) | |
| download | dotemacs-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.
Diffstat (limited to 'todo.org')
| -rw-r--r-- | todo.org | 1081 |
1 files changed, 536 insertions, 545 deletions
@@ -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"). |
