| Commit message (Collapse) | Author | Age | Files | Lines |
| ... | |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
system-defaults, chrono-tools
Six small fixes the 2026-05-15 module-by-module re-review surfaced:
- Consolidate `user-home-dir` -- canonical defconst stays in
early-init.el (package-archive bootstrap needs it before normal
modules load); user-constants.el switches to a `defvar` with the
identical `(getenv "HOME")` expression so the module still loads /
byte-compiles standalone, but at runtime early-init's defconst
wins.
- Drop the redundant `(autoload 'env-bsd-p ...)` line in
system-defaults.el. The `(eval-when-compile (require
'host-environment))` already exposes the symbol to the byte
compiler, and at runtime host-environment is loaded earlier in
init.el. Added a comment documenting the boundary.
- Convert `cj/debug-modules` and `cj/use-online-repos` from `defvar`
to `defcustom`, with `:type`, `:group 'cj`, and a top-level
`(defgroup cj ...)` so both show up in M-x customize.
- Name the package-archive priorities in early-init.el. Nine new
defconsts replace the magic numbers (200 / 125 / 120 / 115 / 100 /
25 / 20 / 15 / 5) with one constant each, plus a header comment
explaining the local-first ordering and the gnu > nongnu > melpa >
melpa-stable trust ranking within each tier.
- Delete the 19-line commented-out `use-package time` world-clock
block in chrono-tools.el. `time-zones` immediately above is the
active replacement; git history preserves the old config if anyone
needs it.
- Add coverage for `cj/tmr-select-sound-file`. Collapsed the
prefix-arg branch into a delegation to
`cj/tmr-reset-sound-to-default` (single reset source) and
extracted `cj/tmr--available-sound-files` as a pure helper that
tests directly. 9 ERT tests across Normal / Boundary / Error
cover the available-sounds helper, the reset path, the prefix-arg
delegation (no prompt), the normal selection path, and the
empty-dir / missing-dir / cancel boundaries.
|
| |
|
|
|
| |
Move the closed Gptel Work PROJECT and the flycheck modeline task
from Open Work into Resolved. Both shipped this round.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The custom modeline builds `mode-line-format` from explicit segments
and skips `minor-mode-alist`, so flycheck's lighter never appears.
That hid error and warning counts even in buffers where flycheck was
auto-enabling (every emacs-lisp and sh buffer).
The fix is Option 4 from the design doc: customize the flycheck
modeline variables, then add a single guarded `(:eval ...)` form to
`mode-line-format`. Five new lines total, two-file change.
`modules/flycheck-config.el` :custom block gets:
(flycheck-mode-line-prefix "🐛")
(flycheck-mode-success-indicator " ✓")
`flycheck-mode-line-color` stays default-t so error / warning counts
pick up their faces automatically.
`modules/modeline-config.el` `mode-line-format` gets an `(:eval ...)`
between the recording indicator and `cj/modeline-vc-branch`:
(:eval (when (and (mode-line-window-selected-p)
(bound-and-true-p flycheck-mode))
(flycheck-mode-line-status-text)))
The `mode-line-window-selected-p` guard mirrors `cj/modeline-vc-branch`
and `cj/modeline-misc-info` -- segments hide in inactive windows.
The `bound-and-true-p flycheck-mode` guard keeps the form silent in
buffers where flycheck hasn't loaded or isn't enabled, which is
safer than referencing `flycheck-mode` directly.
The `(:eval ...)` is inline rather than a named `defvar-local`, so no
addition to the risky-local-variable list is needed.
`tests/test-modeline-config-flycheck-segment.el` -- 3 smoke tests
asserting the segment is present and both guards are in place. All
existing tests stay green.
Manual verification (per the design doc) is the user's call -- the
emoji prefix and the colored count behavior need a running GUI Emacs
to observe.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The Gptel Work project asked for a survey of published gptel tools
with adopt / skip / defer decisions per candidate. I can't do a
live community-tool survey from this session, so the doc covers
the candidates the task body called out plus a few obvious
adjacents.
Decisions:
- ADOPT (7): `search_in_files`, `git_status` / `git_log` /
`git_diff` (three tools), `web_fetch`, `search_emacs_help`,
`find_file_by_name`, `take_screenshot`. Each gets a sketch in
the doc -- args, validation posture, implementation outline.
- DEFER (2): `run_shell_command` (huge surface, click-fatigue
risk; the ADOPT-bucket tools cover most legit use cases),
`org_capture` (needs UX design for template pre-fill and the
round-trip).
- SKIP (1): `eval_elisp` (code execution from a model is too
dangerous even with confirm-each-call).
The doc also lists three follow-ups: the live community survey
that this session couldn't do, per-tool implementation sub-tasks
to be filed under the next iteration of Gptel Work, and a
sandboxing-convention decision for `web_fetch` (allowlist of
outbound URLs vs description-only warning).
Three open questions at the bottom of the doc for review:
build-all-at-once vs paired stages, `fd` as a hard dep vs `find`
fallback, Hyprland-only screenshot vs Wayland-generic via a
portal.
Closes the Gptel Work PROJECT for this iteration -- all 9 in-scope
sub-tasks landed this session.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
conversations
`cj/gptel-load-conversation` prompts via `completing-read`. A
dedicated browser shows what each conversation is about at a
glance and supports single-key load / delete / rename without
having to scroll a minibuffer list.
New module `modules/ai-conversations-browser.el` +
`cj/gptel-browse-conversations` entry point bound to `C-; a b`
("browse conversations"). Opens `*GPTel-Conversations*` in
`cj/gptel-browser-mode` (a `special-mode` derivative).
Each row shows date, time, topic slug, and a preview of the most
recent message (length configurable via
`cj/gptel-browser-preview-length`, default 60 chars). Rows sort
newest first.
In the browser:
- `RET` / `l`: load the conversation (delegates to
`cj/gptel-load-conversation` with the file pre-selected via a
`cl-letf` stub on `completing-read` so the user isn't prompted
twice), then bury the window.
- `d`: delete the file under point after `y-or-n-p` confirmation,
re-render.
- `r`: rename the file under point. Preserves the timestamp,
slugifies the new topic, refuses unchanged input and existing
targets.
- `g`: refresh.
- `n` / `p`: next / previous row.
- `q`: quit-window.
21 tests cover the helpers (topic parsing, header stripping,
preview shaping for truncate / short / empty cases, row-for-file
with conversation + non-conversation filenames, rows enumeration,
render output for empty + populated cases, newest-first sort,
rename-target preservation of timestamp + slug, rename-target
error on missing timestamp) and the file-touching actions (delete
with y, cancel with n, rename, rename-on-empty-line error).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`gptel-rewrite` is the killer feature for the keep-gptel decision,
and it now lives behind two commands instead of the bare call:
- `cj/gptel-rewrite-with-directive` (`C-; a r`, replacing the
former bare `gptel-rewrite` binding): completing-read on a
directive name from `cj/gptel-rewrite-directives`, then rewrite
the active region.
- `cj/gptel-rewrite-redo-with-different-directive` (`C-; a R`):
replay the prior region with a different directive. The region
is preserved via markers stored buffer-local on the first call so
it survives accept/reject of the prior rewrite.
I picked the hook injection approach over an `:after`-advice +
state-capture pattern. `gptel-rewrite-directives-hook` is an
abnormal hook gptel-rewrite already consults for a per-call
system message. Wrapping the call in a one-shot `let`-binding on
that hook gives the directive exactly the lifetime of the rewrite
and leaves nothing to clean up. Mutating `gptel-directives`
globally would mean either restoring it afterward or living with
the change -- both worse than the hook.
Directives ship inline as a `defcustom` alist with the six names
called out in the task -- `terse`, `fix-grammar`,
`refactor-readability`, `add-docstring`, `explain-as-comment`,
`shorten`. Customization is a `customize-variable` or `setq`
away.
9 tests cover the defcustom shape (default names present, bodies
non-empty strings), the wrapper (normal path, no-region error,
unknown-directive error, last-state recording), and the redo
(replays the prior region, errors when no previous, excludes the
current directive from the re-pick prompt). `gptel-rewrite`
stubbed in tests so no rewrite UI fires.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
New module `modules/ai-quick-ask.el`. Bound to `C-; a q` via
`cj/ai-keymap` ("quick ask").
`cj/gptel-quick-ask` reads a prompt in the minibuffer, creates a
transient `*GPTel-Quick*` buffer in `cj/gptel-quick-mode` (a
special-mode derivative with `q` / `escape` / `c` bindings), inserts
"Q: <prompt>" plus a response marker, then calls `gptel-request`
with `:stream t` so the answer streams into the buffer. Doesn't
touch `*AI-Assistant*`, doesn't autosave.
Two follow-up commands work in the buffer:
- `cj/gptel-quick-dismiss` (`q` / `escape`): delete the window and
kill the buffer. Idempotent when the buffer is absent.
- `cj/gptel-quick-continue` (`c`): extract the prompt + response,
seed them into `*AI-Assistant*` under proper org headings (matching
the `cj/gptel--fresh-org-prefix` shape), display the side window,
then dismiss the quick buffer.
13 tests cover the pure helpers (initial-text shape, response
extraction across normal / multi-line / no-marker / empty inputs,
seed-text shape), the ask path (buffer created in right mode,
prompt recorded, gptel-request called, empty-prompt error), the
dismiss path (kills buffer / no-op when absent), and the continue
path (seeds `*AI-Assistant*`, dismisses quick buffer, errors
outside a quick buffer). `gptel-request` is stubbed in tests so
nothing hits the network.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
indicator
`cj/gptel-autosave-enabled` flipped to t inside the save/load entry
points with no way back off short of editing the variable or
clearing the buffer, and no visible indicator that it was on.
Two pieces:
- `cj/gptel-autosave-toggle` flips the buffer-local state in the
current GPTel buffer. Bound to `C-; a A` via `cj/ai-keymap`
(which-key: "toggle autosave"). When autosave is OFF and no
filepath is configured yet, the command prompts to save the
conversation first so a save target exists; otherwise it just
flips the bit.
- `cj/gptel-autosave-mode-line-format` surfaces " [AS]" in the
mode-line when autosave is on, blank when off. Installed via a
`gptel-mode-hook` so every GPTel buffer picks it up. The install
helper is idempotent.
6 new tests cover enable/disable paths, the no-filepath prompt path,
the not-a-gptel-buffer error path, the mode-line format evaluation,
and the install idempotence.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The gptel-tools files had zero direct coverage outside of
`update_text_file`, which landed with its rewrite earlier this
session. This commit adds 52 tests across the five other tools.
For three of the tools the helpers were already top-level defuns
(`read_text_file`, `list_directory_files`, `move_to_trash`). The
other two had their main bodies inlined into the `gptel-make-tool`
lambda -- I extracted them so the work is testable without mocking
gptel itself:
read_buffer.el -> `cj/read-buffer--get-content`
write_text_file.el -> `cj/write-text-file--run` plus
`--validate-path`, `--backup-name`,
`--ensure-parent`
Test files, by tool:
- read_buffer.el (5 tests): normal, empty, buffer-object,
text-property-stripping, missing buffer.
- write_text_file.el (10 tests): validate-path, backup-name
shape, ensure-parent (creates missing / rejects unwritable), run
with normal / overwrite / existing-no-overwrite / empty content /
outside-home.
- read_text_file.el (12 tests): validate-file-path (normal +
three error shapes), metadata plist shape, size limits (no-op /
hard cap / warning bypass with no-confirm), binary detection
(text vs null-byte), special-type EPUB and generic-binary paths.
- list_directory_files.el (15 tests): mode-to-permissions (file /
dir / executable), get-file-info (file / directory), extension
filter (keep / drop / always-dir / nil-extension), format-file-
entry, list-directory flat / recursive / error, format-output
with and without files.
- move_to_trash.el (10 tests): unique-name (no conflict /
conflict with timestamp / no-extension), validate-path (HOME / /tmp
/ outside / critical-dir / missing), perform on file and
directory.
Each test file uses the same load-path / gptel-stub idiom
(`eval-and-compile` block, gptel stub when the real package isn't
available) so the byte-compile hook is happy.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
ai-conversations.el shipped without direct tests. This file covers
every helper and interactive entry point across Normal / Boundary /
Error.
Helpers: `cj/gptel--slugify-topic` (ASCII, empty input, all-special,
unicode stripped, idempotent, trim, digits); `cj/gptel--timestamp-
from-filename` (normal decode, year-edge boundaries, malformed
inputs returning nil); `cj/gptel--existing-topics` and `cj/gptel--
latest-file-for-topic` (multi-topic / multi-timestamp temp dirs,
empty dir, missing dir, prefix-overlap isolation); `cj/gptel--
conversation-candidates` (newest-first and oldest-first sort order,
display-string shape, error on missing dir); `cj/gptel--save-buffer-
to-file` (visibility headers prepended, round-trip through `cj/
gptel--strip-visibility-headers`).
Autosave: post-response hook saves only when gptel-mode + enabled +
filepath are all set; autosave-after-send swallows write errors via
`message` instead of signaling; the install-once guard prevents
double-registration.
Interactive entry points: save/delete exercised via `cl-letf` stubs
on `completing-read` and `y-or-n-p`.
Per-test temp directories; no writes outside them.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The wiring keyed on `with-eval-after-load 'magit` fires while two
of its three references are still undefined. `magit.el` calls
`(provide 'magit)` BEFORE its `cl-eval-when (load eval)` block
requires `magit-commit` and `magit-stash`. At that moment the
`magit-commit` transient prefix doesn't exist, and
`transient-append-suffix` silently no-ops on missing prefixes
(default `transient-error-on-insert-failure` is nil). The "g
Generate commit" and "x Explain" suffixes never landed. Only the
M-g binding worked, because `git-commit` IS required before
provide.
Three per-feature hooks replace the single `'magit` hook: one each
on `git-commit`, `magit-commit`, and `magit-diff`. Each hooks the
exact dependency the wiring needs, side-stepping the load-order
race entirely.
The companion test was rewritten to check `after-load-alist`
registration rather than drive the hooks through `provide`. Emacs
30 batch mode doesn't fire registered `eval-after-load` callbacks
on `provide` alone -- only an actual `load` does. Inspecting the
registration is the stronger guard anyway: the regression is "a
single `'magit` hook," and the right shape of that check is "no
entry under `magit`, entries under `git-commit`, `magit-commit`,
`magit-diff`."
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
I rewrote `update_text_file.el` in pure Elisp. The previous version
shelled out to sed for everything, had a stray quote terminator at EOF
(line 149) that broke loading, produced literal backslash-n where
actual newlines were expected, and prompted via `y-or-n-p` redundantly
with gptel's own `:confirm t` flag.
The five operations -- replace, append, prepend, insert-at-line,
delete-lines -- split into pure string transforms that test without
touching the disk. The file-level wrapper validates the path, enforces
a 10MB size limit, takes a timestamped backup, and writes atomically.
No backup is created when the operation is a no-op. Patterns are
literal substrings (not regex) so the model can't trip over
metacharacter quoting.
`tests/test-update-text-file.el` covers Normal / Boundary / Error per
operation plus the file-level wrapper. 48 tests green. Added
`update_text_file` to `cj/gptel-local-tool-features` so gptel exposes
the tool after restart.
|
| |
|
|
|
| |
Move the two DONE entries (gptel-magit install + gptel org-mode
prompt-buffer tab-width) from Emacs Open Work to Resolved.
|
| | |
|
| |
|
|
|
|
|
|
| |
gptel's `gptel--with-buffer-copy-internal` copies the source buffer's `major-mode` symbol but doesn't run mode hooks. An inherited-org-mode prompt buffer keeps `tab-width` at this config's global default of 4 instead of the 8 that `org-mode-hook` would set. When gptel later parses the prompt buffer with `org-element`, Org's `tab-width=8` guard raises "Tab width in Org files must be 8, not 4."
I was hitting this on every second `gptel-magit-generate-message` from COMMIT_EDITMSG. `vc-config.el` sets `git-commit-major-mode 'org-mode'`, and the diffs contained list-shaped content that `org-element--list-struct` parsed.
The advice forces `tab-width=8` in the prompt buffer when its inherited mode is org-mode. It's a local workaround for an upstream gap. An upstream patch to run `(delay-mode-hooks (funcall major-mode))` in the buffer-copy is the real fix. I'll send it next.
|
| | |
|
| | |
|
| |
|
|
|
|
|
| |
Replace raw autoload calls with a `use-package` declaration so
`use-package-always-ensure` installs gptel-magit on machines that
haven't run `package-install`, fixing the "Cannot open load file"
error on transient setup.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The shape (if (not (abbrev-mode)) (abbrev-mode)) calls abbrev-mode with
no argument -- that's the toggle signature, not a query. When the mode
was already on the function flipped it off then on instead of being a
no-op. Replaced with (unless (bound-and-true-p VAR) (MODE 1)) for both
abbrev-mode and flycheck-mode. 4 ERT tests cover both-off, both-on, and
the two mixed states.
Also ran the module hardening pass across 24 newly-added modules,
renamed the six completed Review sub-tasks to Harden, filed 11 new
findings under their Harden parents, and broke three design specs
(EMMS-free music, dev F-keys, dev-setup-project) into 20
dependency-ordered sub-tasks via parallel subagents. Verified the
sqlite finalizer bug from 2026-04-26 is gone and closed its tracking
entry.
|
| |
|
|
|
|
|
|
| |
The three integration tests in test-video-audio-recording-process-cleanup spawn wf-recorder via cj/ffmpeg-record-video and assert on pgrep counts. They guard with executable-find and XDG_SESSION_TYPE checks, but neither catches the case where the subprocess can run wf-recorder yet lacks Wayland screencast permission. wf-recorder picks a region, retries "Failed to copy frame" 17 times, then exits with code 183 inside a second. The assertion fires against an empty pgrep.
I added test-cleanup--can-capture-frames, which calls cj/ffmpeg-record-video against a temp dir, waits 1s, and checks pgrep. If wf-recorder didn't survive, the three integration tests skip. The result is cached, so the ~2.5s cost is paid once per batch.
I added the same guard to test-integration-video-recording-multiple-start-stop-cycles. Its assertion is (= count initial-count), so it trivially passed in any environment where capture didn't work. Skipping is more honest than passing for the wrong reason.
|
| |
|
|
|
|
| |
`--archive-done` moved two completed level-2 PROJECTs into Resolved: the `<cj` universal yasnippet conversion and the LSP file-watch ignored-directories task — both closed earlier this session.
`lint-org` applied a small batch of mechanical heading-line merges. The remaining judgment items (broken file links + one invalid fuzzy link) went to the lint follow-ups file for later review. `--sync-child-priority` is a no-op now that the two top-level review PROJECTs carry `:no-sync:` and the inheritance fix lives upstream in claude-templates.
|
| |
|
|
|
|
|
|
|
|
|
| |
All four sub-tasks complete, parent advances to DONE + CLOSED:
- Wired yasnippet for universal availability (yas-global-mode + the fundamental-mode extras hook).
- Created the <cj snippet at snippets/fundamental-mode/.
- Removed the org-tempo cj entry.
- Audited existing per-mode snippets — all 28 are correctly mode-scoped, no movers.
Sub-task headings landed as dated event-log entries per todo-format.md's depth-based completion rule. The parent stays task-shaped at level-2 for agenda visibility.
|
| |
|
|
| |
Now that yasnippet handles `<cj` + TAB in every buffer including org-mode, the `cj` entry in `org-structure-template-alist` is redundant. I removed it and left a one-line comment pointing to where the marker now lives.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
New `snippets/fundamental-mode/cj-comment-block` expands `<cj` + TAB to the literal three-line marker block:
#+begin_src cj: comment
<cursor>
#+end_src
It lives in `fundamental-mode/` so yas's parent-chain lookup finds it in every buffer, plus the activation hook from the previous commit explicitly turns on `fundamental-mode` as an extra mode in every buffer — so even modes that don't descend from `fundamental-mode` (like `special-mode`-derived buffers) pick it up.
The marker is what my Python scanner skill picks out across files. Now I can plant it in any buffer, not just org where the old org-tempo entry lived.
|
| |
|
|
|
|
|
|
| |
The yasnippet use-package block switches from `:hook (prog-mode . yas-minor-mode)` to `:demand t` + `(yas-global-mode 1)`. That makes yas-minor-mode active in every buffer, not just prog-mode-derived ones.
I added a small helper, `cj/--yas-activate-fundamental-extras`, attached as `:hook (yas-minor-mode . ...)`. It calls `(yas-activate-extra-mode 'fundamental-mode)` so the snippet table at `snippets/fundamental-mode/` is consulted in every buffer regardless of the buffer's own major mode. That's what makes universal triggers like `<cj` work everywhere.
The new `tests/test-prog-general-yas-activation.el` covers both wiring (yas-global-mode on, fundamental-mode in yas-extra-modes, yas-minor-mode active in org/text buffers) and end-to-end expansion (the marker snippet expands correctly in fundamental, text, org, emacs-lisp, and python-ts modes). 9 tests, all green; full unit suite green with no regressions.
|
| |
|
|
|
|
| |
Today <cj only expands in org-mode via org-structure-template-alist. A Claude skill scans for the literal #+begin_src cj: comment marker across files, so I need to insert the exact same text in any buffer regardless of major mode.
The new task captures four sub-tasks: wire yasnippet to yas-global-mode + activate fundamental-mode as an extra mode in every buffer, create the snippet at snippets/fundamental-mode/, remove the now-redundant org-tempo entry from modules/org-babel-config.el:144, and a smaller follow-up audit of existing per-mode snippets that should probably live in fundamental-mode/.
|
| |
|
|
|
|
|
|
|
|
| |
The Gptel Work heading was a one-line placeholder. I filled it in with nine sub-tasks after deciding to keep gptel for one-off conversations, impromptu help, and the rewrite-region helper (workflow distinct from the F9 ai-vterm agents, so per-project sessions stay uncluttered).
Bumped the parent to PROJECT [#B] and merged the standalone "Investigate gptel-magit not working properly" task in as a sub-task — the gptel-magit work belongs with the rest of the gptel surface.
Four [#B] work items: wire the existing update_text_file tool into cj/gptel-local-tool-features, fix the three gptel-magit triggers, add ERT coverage for ai-conversations.el (zero today), add ERT coverage for the gptel-tools .el files (also zero).
Five [#C] proposals for review: research and shortlist additional gptel tools, promote gptel-rewrite ergonomics with a directive picker, build a saved-conversations browser, add a one-shot quick-ask command, and ship an autosave toggle + mode-line indicator.
|
| |
|
|
| |
The implementation shipped 2026-04-26 in 781b46e (cj/lsp-file-watch-ignored-extras plus the hook in modules/prog-lsp.el, 10/10 tests green). The manual smoke check in the dashboard MVP and the redundant .dir-locals.el cleanup on velox are tracked separately as active reminders, so todo.org doesn't need to carry the VERIFY state.
|
| |
|
|
|
|
|
|
|
|
|
| |
All five children shipped earlier this month but were left tagged TODO/VERIFY. I verified each against the repo, marked them DONE with their actual closure dates, and closed the PROJECT today.
Children closed:
- Write full utility consolidation design spec — 2026-05-04 (b3ef232a)
- Inventory private helpers across modules — 2026-05-10 (502bcf41)
- Extract executable lookup with warning helper — cj/executable-find-or-warn in system-lib.el (c75e36f4)
- Extract argv-based process runner helper — cj/process-output-or-error and cj/git-output-or-error in system-lib.el (57e558ce)
- Extract Org-safe text sanitizers — cj-org-text-lib.el (0f9e3087)
|
| |
|
|
|
|
|
|
| |
I ran the wrap-up hygiene that the previous session skipped.
`--archive-done` moved 7 DONE subtrees out of Open Work into Resolved. `lint-org` applied 8 mechanical heading-line merges. 12 remaining judgment items (mostly broken file links and one malformed timestamp) went to the lint follow-ups file for later.
I tagged the two top-level review projects `:no-sync:` so their subtrees stop cascading priority bumps — `:refactor:no-sync:` on the architecture review project and `:review:no-sync:` on the module-by-module review project. The latter also dropped from `[#A]` to `[#B]`. It was sitting at A only because the cascade was forcing it. B is where the work belongs now that the opt-out works.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
added modules
Second-pass review of the existing six review tracks (foundation, custom
editing, UI / navigation, Org workflow, programming workflow,
integrations) walked each track as if it had not been reviewed before.
Existing child tasks were deliberately excluded from re-reporting; only
concrete, file-cited, actionable findings not already on the parent
project's task list were filed.
Findings filed:
- Foundation (5): duplicate user-home-dir constant; redundant
eval-when-compile alongside autoload in system-defaults; defcustom
conversions for cj/debug-modules and cj/use-online-repos; surface
the custom-file trash-binning; name package-archive priorities.
- Custom editing (6): region-or-buffer scope inconsistency between
text-enclose and ordering helpers; trailing-newline preservation
in custom-ordering; non-file-buffer guard for
cj/duplicate-line-and-comment; idempotent advice setup in
external-open; comment-delimiter validation in custom-comments;
state-machine coverage for cj/title-case-region.
- UI / navigation (9): popper-mode :init activation defeats
:disabled t; vc-git--symbolic-ref fallback in modeline; theme-aware
face in cj/display-available-fonts; TTY-first frame race in font
setup; per-profile cache in mousetrap-mode keymap rebuilds;
symlink-aware VC modeline cache invalidation; C-s binding shadows
isearch; cursor-color hook guard for non-GUI frames; nerd-icons
advice deferral via with-eval-after-load.
- Org workflow (3): de-duplicate org-protocol handler registration
in org-webclipper; validate :url and :title in
cj/org-protocol-webclip; replace global cj/webclip-current-* with
structured per-invocation state.
- Programming (6): load-time executable-find checks for prettier
and pyright; idempotent server-after-make-frame-hook in
keyboard-compat; dev-fkeys F6 typescript runner clause; prog-lsp
eldoc-provider removal scope; externalize flycheck LanguageTool
script path; externalize latex-config Zathura viewer.
- Integrations (2): markdown-preview httpd-start side effect;
externalize hardcoded SSH hostnames in eshell-config.
Plus: a new "Review newly added modules" sibling task lists all
24 modules that were either added after the parent task was written
(post-2026-04) or fell outside the original review-track scope lists.
Each is routed to its target track; module-specific findings are
filed under the relevant track above.
The integrations track re-review subagent derailed mid-run; rather
than re-issue it, the track's existing 16 child tasks plus the
newly-added-modules findings are deemed adequate coverage. Future
re-review passes can revisit if needed.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`cj/org-roam-filter-by-tag' called `org-roam-node-tags' directly.
That accessor is generated by `cl-defstruct' and ships with a
compiler-macro that inlines the call to an `aref' against the
`cl-struct-org-roam-node-tags' tag variable at byte-compile time.
In tests, `cl-letf' on `(symbol-function 'org-roam-node-tags)' sets
the function cell but the byte-compiled call site never consults it
-- it executes the inlined `aref' instead. When org-roam isn't
loaded (legitimate for a tag-filter unit test), the inlined code
fails with `void-variable cl-struct-org-roam-node-tags'.
Wrap the accessor in `cj/--org-roam-node-tags' that calls through
`funcall' with a quoted symbol. Quoted symbols skip the
compiler-macro (which only fires on direct call forms), so the
funcall resolves the function cell at runtime and picks up the
test's `cl-letf' stub. Production behavior is unchanged; tests
no longer need org-roam loaded.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`org-web-tools-pandoc-sleep-time' is a plain float with no
custom-set handler that needs to fire. `setopt' adds the entire
customize-variable validation machinery -- which, lazily, depends on
wid-edit being loaded.
The handler's tests stub `require' so org-web-tools never really
loads, then mock `setopt' via `cl-letf' on the function cell. That
mock has no effect on byte-compiled code because `setopt' is a
macro: the production handler has already expanded to a call into
`setopt--set'. When `setopt--set' runs, it walks into the customize
machinery and hits an unbound `widget-field-keymap' (wid-edit not
loaded), and the test fails with a confusing wrong-type-argument.
`setq' has identical runtime effect for this variable and dodges
the customize machinery entirely. Tests now pass without contorted
mocking.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The module had `(eval-when-compile (require 'keybindings))`, which
silences the byte-compiler but doesn't make `cj/custom-keymap'
available when the module is required. The top-level
`(keymap-set cj/custom-keymap "!" cj/system-command-map)' at the tail
of the file then fails with `void-variable cj/custom-keymap'.
Normal Emacs startup happened to work because `init.el' requires
`keybindings' before `system-commands'. But requiring the module in
isolation -- including from `make test-file
FILE=test-system-commands-resolve-and-run.el' -- blows up.
Fix: use a plain `(require 'keybindings)' so the load-time
dependency matches the load-time reference.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Both follow-up sub-TODOs filed earlier today are resolved:
- cj/toggle-gptel cross-module boundary is now an explicit autoload
declaration in ai-vterm.el; make compile is clean and a regression
test in test-ai-vterm--f9-in-vterm.el asserts the contract.
- Info-mode dispatcher in cj/buffer-source-functions now returns the
full org bracket-link form [[info:(manual)Node][(manual) Node]];
the existing .info.gz test asserts the new form and a new boundary
test covers the uncompressed .info path.
Parent tasks return to DONE. Sub-TODOs become dated done headings
recording the actual work done.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The Info-mode entry in cj/buffer-source-functions copied the bare
target string info:(manual)Node. Per the task body that introduced
the dispatcher, the intended output is the labeled org-link form
[[info:(manual)Node][(manual) Node]] -- a paste into notes lands as
a clickable link with a human-readable label, not a bare URI.
The label uses (manual) Node so the manual name and node name are
both grep-friendly in note files.
Existing test on a compressed .info.gz file now asserts the bracket
form. Added a boundary test for an uncompressed .info file (the
other branch of the suffix-stripping logic) so both compression
shapes are locked in.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
make compile warned that cj/toggle-gptel is not known to be defined
when ai-vterm.el is byte-compiled. The M-F9 binding still worked
during normal startup because init.el loads ai-config.el after
ai-vterm.el, but the dependency was implicit -- byte-compile saw the
function symbol unresolved, and loading ai-vterm.el in isolation
left M-F9 bound to an undefined function.
Declare cj/toggle-gptel as an interactive autoload pointing at
ai-config. This silences the warning, keeps ai-vterm.el free of a
load-time (require 'ai-config), and makes the load-order contract
explicit: the binding works as long as ai-config eventually loads.
Test asserts that requiring ai-vterm in isolation leaves
cj/toggle-gptel fboundp as an autoload sigil (not a real function).
A regression that adds (require 'ai-config) at the top of
ai-vterm.el would flip this, and a regression that drops the
autoload form would leave fboundp nil.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Three related housekeeping updates that piled up during the session.
The "Write spec on what's needed for music-config not to depend on
EMMS" task is DONE — spec lives at
docs/design/music-config-without-emms.org. Adds a follow-up TODO to
implement the spec, with the complexity and time estimates and the
acceptance checks the spec already defines.
The module-by-module review project moves from PROJECT to DOING. All
six review tracks (foundation, custom editing utilities, UI and
navigation, Org workflows, programming workflows, integrations and
applications) move to DOING with completion-review notes appended
under each. The notes summarize what was re-checked, what coverage
now exists, and which child tasks are still actionable.
Two new sub-TODOs add focused test coverage:
- "Add UI/navigation runtime smoke coverage" — font-config,
modeline-config, popper-config policy.
- "Add smoke coverage for lightweight programming modules with no
direct tests" — prog-general, prog-lisp, prog-training.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Two recent DONE tasks turned out to have follow-up issues worth tracking
explicitly instead of leaving them in the closed pile.
The gptel-toggle work (=M-<f9>= rebind) now triggers a make compile
warning that =cj/toggle-gptel= is not known to be defined when
ai-vterm.el loads. The binding still works in normal startup because
ai-config.el loads later, but the cross-module load-order dependency
should be made explicit via an autoload declaration plus a test that
loads ai-vterm.el without preloading ai-config.el.
The buffer-source extension shipped an Info-mode dispatcher that
copies =info:(manual)Node= rather than the documented org-style
bracket link =[[info:(manual)Node][label]]=. The output is a valid
org link target but not the labeled form a Notes paste would expect.
Both parent tasks move back to DOING with sub-TODOs that name the
remaining work, the file/test to update, and the commands to re-run.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Add a tiny source-level architecture suite at
tests/test-architecture-startup-contracts.el with two checks:
- Only keybindings.el may globally own the exact C-; prefix. Catches
accidental cross-module rebinding before it ships.
- Top-level timer scheduling (run-with-timer / run-at-time /
run-with-idle-timer) must be guarded by (unless noninteractive ...)
so requiring a module in batch / test mode does not schedule
startup timers. Timer calls inside defuns are exempt -- the test
only rejects forms that execute their body when the module loads.
Four modules had unguarded top-level timer scheduling and would have
tripped the new test. Wrap their startup hooks/timers in
(unless noninteractive ...):
- modules/org-agenda-config.el: 10s idle cache build
- modules/org-refile-config.el: 5s idle cache build
- modules/quick-video-capture.el: after-init-hook + 2s fallback
- modules/wrap-up.el: emacs-startup-hook bury-buffers delay
The contract being protected is "requiring a module in batch should
not start a clock running." Test failures will now point straight at
the offending file/form.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
=make coverage= used to print a line-weighted percentage that only saw
files SimpleCov instrumented. 104 modules existed on disk but only 49
appeared in =.coverage/simplecov.json=, so the headline number was
flattering: untouched modules counted for nothing.
The summary script now adds two things on top of the existing report:
- A =Not in SimpleCov report= section listing modules present under
=modules/*.el= but absent from the SimpleCov output. Missing-module
detection is exactly direct =modules/*.el=; subdirectories and =.elc=
files are ignored.
- A =Project module coverage= line that is module-weighted across every
direct =modules/*.el= file. Tracked modules contribute their per-file
coverage percentage; missing modules contribute 0%.
The original line-weighted SimpleCov percentage stays as the
=instrumented coverage= number. The new module-weighted score is the
honest project-level reading: missing modules count as 0% without
inventing a fake executable-line denominator for them.
Tests assert the missing-module section, the new percentage, and the
ignore rules for .elc / nested files.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The spec lays out the EMMS-removal design: package-owned track and
playlist structs, a narrow backend protocol with mpv as the v1 backend,
state-change hooks replacing EMMS player hooks, an overlay-based
selected-track marker, a fake-backend test architecture, a quantified
performance budget, a 22-step parity walk, and the migration plan.
The review tracks implementation readiness: which migration-plan step
is safe to start, which decisions still block the rest, and the exact
spec edits required.
Two decisions landed this session and are now baked into the spec:
- Platform support: Linux and macOS get full features; Windows runs in
degraded mode (play/stop/next/previous only) because Emacs cannot
natively connect to mpv's Windows named-pipe IPC. Anyone who wants
full Windows parity can wire mpvc.exe shellout or a w32-* named-pipe
client as a follow-up.
- File-extension scope: cj/music-file-extensions stays as-is. webm and
ape files in ~/music are intentionally skipped.
Socket path now references temporary-file-directory instead of a
hardcoded /tmp/ prefix so the spec stays consistent with the Windows
section.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Replaces the .ai/ draft (2025-11-14) with a corrected and tightened
version under docs/design/. The earlier draft had stale line numbers
pointing at a modeline-config.el layout that no longer exists,
conflated Option 3's risky-local-variable requirement with Option 4's
inline (:eval ...) approach, and missed the active-window gating
convention used by the rest of the modeline.
The new spec uses concrete line refs against current code, calls out
flycheck-mode-line-color (which the old draft missed), recommends
calling flycheck-mode-line-status-text directly instead of returning
the nested (:eval ...) cons, and gates the segment to active window
for consistency with cj/modeline-vc-branch and cj/modeline-misc-info.
todo.org task points at the new path and drops the broken
docs/flycheck-modeline-customization-spec.org link.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Replaces a thin third-party config snippet (one use-package corfu + one
use-package cape, with no migration steps and no prescient piece) with
a full spec covering the current company stack: corfu, cape,
corfu-popupinfo, kind-icon, corfu-prescient.
Maps every current company setting to its corfu equivalent
(idle-delay, prefix-length, tooltip-limit, wrap, require-match,
global-mode exclusions, doc popups, icon kinds, prescient sort).
Walks the per-module fixups -- selection-framework, mail-config,
ledger-config, latex-config, eshell-config, and the three prog-*
mode hooks. Adds a test plan and risks section.
todo.org points at the new doc; the broken :COMPLETE_CONFIG:
property (which referenced the wrong line range in someday-maybe)
is gone.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Add two dispatchers to cj/buffer-source-functions so C-; b p yields a
useful link form in two more major modes.
mu4e-view-mode returns "mu4e:msgid:<id>" so the result pastes into org
as a clickable link and matches mu4e's own org-protocol handler.
Falls through to buffer-file-name when point isn't on a real message.
Info-mode returns "info:(manual)node" -- the form org-info-store-link
produces. file-name-base only strips one extension, so a compressed
"emacs.info.gz" comes back as "emacs.info"; trim the trailing ".info"
to get the bare manual name. Falls through when Info hasn't populated
its current-file / current-node vars yet.
Tests cover normal + boundary fallthrough for each new mode.
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`cj/org-noter-insert-note-dwim' is the most-used action in a noter
session; it deserves the doubled-prefix letter. Move it from
`C-; n i' to `C-; n n'.
Sibling-stepping moves off `n'/`p' (which were sync-next /
sync-prev) onto the angle-bracket pair `>'/`<' to free up `n' and
to read more naturally as direction. `.' stays as
sync-current-note.
Updated `which-key' labels to match. Four new ERT tests in
`tests/test-org-noter-config-keymap.el' lock the keymap shape so a
casual edit doesn't silently drift the layout.
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`make compile' had been flagging `cj/calibredb-clear-filters' and
`cj/nov-jump-to-calibredb' as "defined multiple times in this file"
since 2026-05-12. Investigation: there's only one `(defun ...)' of
each in the source -- use-package's `:bind' expansion makes the
byte-compiler count the referenced symbol as a definition when the
function is defined in the same file, then it sees the real
`defun' later and warns about a redefinition.
Reorder so each `defun' appears before the `use-package' that
references it via `:bind':
- `cj/calibredb-clear-filters' moved above (use-package calibredb).
- `cj/nov--metadata-get', `cj/nov--file-path', and
`cj/nov-jump-to-calibredb' moved above (use-package nov). The
two helpers had to move with the public function so the
byte-compiler doesn't emit fresh free-function warnings.
Source content unchanged; only line positions move. Both
duplicate-definition warnings are gone after this; full unit suite
still green.
|
| | |
|