diff options
Diffstat (limited to 'issues.org')
| -rw-r--r-- | issues.org | 1879 |
1 files changed, 1879 insertions, 0 deletions
diff --git a/issues.org b/issues.org new file mode 100644 index 00000000..c61b8517 --- /dev/null +++ b/issues.org @@ -0,0 +1,1879 @@ +#+TITLE: Emacs Configuration Issues & Enhancement Roadmap +#+AUTHOR: Claude Code Analysis +#+DATE: 2025-10-30 + +* New Open Work + +** TODO [#A] Duplicate variable declaration in early-init.el :bug:architecture:critical: +:PROPERTIES: +:FILE: early-init.el +:LINES: 76-78, 104-105 +:END: + +The variable =cj/network-available= is declared twice (lines 76-78 and 104-105). + +*Impact:* Confusing code structure, potential initialization issues. + +*Fix:* Remove the duplicate declaration at lines 104-105. The first declaration at line 76 is sufficient. + +** TODO [#A] Missing function definition in vc-config.el :bug:critical: +:PROPERTIES: +:FILE: modules/vc-config.el +:LINE: 123 +:END: + +The keymap binding references =cj/goto-git-gutter-diff-hunks= (line 123) but this function is never defined anywhere in the config. + +*Impact:* Keybinding =C-; v d= will fail with "Symbol's function definition is void" error. + +*Fix:* Either implement the missing function or remove the keybinding. Likely should use =git-gutter:popup-hunk= or create a wrapper that calls =consult-line= filtered to lines with git-gutter markers. + +** TODO [#A] Missing function in wrap-up.el :bug:critical: +:PROPERTIES: +:FILE: modules/wrap-up.el +:LINE: 28 +:END: + +Calls =cj/log-silently= but function is never defined in the config. + +*Impact:* Init will fail at startup with undefined function error. + +*Fix:* Define =cj/log-silently= in system-defaults or config-utilities, or replace with a simple =(message ...)= call. + +** TODO [#A] Network check runs synchronously during startup :performance:critical: +:PROPERTIES: +:FILE: early-init.el +:LINES: 189, 206 +:END: + +The =cj/internet-up-p= function is called *synchronously* during early-init (lines 189, 206), which means Emacs startup blocks waiting for ping or network connection attempts. + +*Impact:* Adds 1+ seconds to every startup, even when network is available. Completely unacceptable UX. + +*Fix:* +1. Default =cj/network-available= to =t= (assume network is available) +2. Run network check asynchronously in background after startup +3. Only use network check when user explicitly runs =package-refresh-contents= +4. Consider removing network check entirely - let package.el handle its own errors + +Modern approach: Remove =cj/use-online-repos= flag entirely. Let package operations fail gracefully if network is down, rather than preemptively checking. + +** TODO [#A] Commented-out non-commented variable in org-config.el :bug:functionality: +:PROPERTIES: +:FILE: modules/org-config.el +:LINE: 129-130 +:END: + +Line 129 has a TASK comment about removing a non-existent variable =org-indent-indentation-per-level=, but line 130 actually tries to set it. + +*Impact:* Setting an undefined variable that does nothing. + +*Fix:* Remove lines 129-130 entirely if the variable doesn't exist. Verify in org-mode source whether this was renamed or removed. + +** TODO [#B] Replace Company with Corfu for better performance and maintenance :enhancement:architecture: +:PROPERTIES: +:FILE: modules/selection-framework.el +:LINES: 202-236 +:END: + +Currently using Company for completion, but Corfu is the more modern choice: + +*Why Corfu is better:* +- ~3x faster than Company (uses child frames vs overlays) +- Better maintained (company-mode development has slowed) +- Smaller, simpler codebase +- Works seamlessly with modern completion frameworks (Vertico/Consult) +- Native corfu-popupinfo is cleaner than company-quickhelp +- Better integration with Cape for completion-at-point backends + +*Migration path:* +1. Replace =company= with =corfu= + =corfu-popupinfo= +2. Use =cape= for additional backends (cape-file, cape-keyword, cape-dabbrev) +3. Keep company-mode disabled in mu4e (corfu already respects this) +4. Much simpler configuration, no company-box needed + +*Estimated effort:* 30 minutes. High value/low effort. + +** TODO [#B] Consolidate debug logging functions :architecture:maintainability: +:PROPERTIES: +:FILES: modules/org-agenda-config-debug.el, modules/wrap-up.el +:END: + +Debug logging is inconsistent: +- =org-agenda-config-debug.el= defines =cj/log-silently= +- =wrap-up.el= *calls* =cj/log-silently= but it's not loaded yet +- =config-utilities.el= has debug functions but not logging + +*Fix:* Create =modules/debug-logging.el= with consistent logging infrastructure: +- =cj/log-debug=, =cj/log-info=, =cj/log-warning=, =cj/log-error= +- Buffer-based logging with timestamps +- Easy toggle for debug mode +- Load early in init.el before other modules + +** TODO [#B] Missing error handling in org-agenda rebuild :stability: +:PROPERTIES: +:FILE: modules/org-agenda-config.el +:LINES: 91-105 +:END: + +=cj/build-org-agenda-list= recursively searches directories but has no error handling for: +- Permission denied errors +- Circular symlinks +- Very deep directory trees +- Network filesystem timeouts (if projects-dir is on NFS/CIFS) + +*Impact:* Agenda rebuild can hang or fail silently. + +*Fix:* Add condition-case wrapper with specific handlers: +#+begin_src elisp +(condition-case err + (cj/add-files-to-org-agenda-files-list projects-dir) + (file-error + (message "Warning: Could not scan %s: %s" projects-dir err)) + (error + (message "Error building agenda: %s" err))) +#+end_src + +** TODO [#B] Unify path handling functions :architecture:maintainability: +:PROPERTIES: +:FILES: modules/dirvish-config.el, modules/custom-buffer-file.el +:END: + +Path handling is duplicated across modules: +- =cj/get-project-root= in dirvish-config.el (lines 408-426) +- Similar logic needed in custom-buffer-file for relative paths +- Different approaches to home directory abbreviation + +*Fix:* Create =modules/path-utils.el= with: +- =cj/get-project-root= (shared implementation) +- =cj/abbreviate-path= (handles ~, project-relative, absolute) +- =cj/path-relative-to= (generalized relative path function) +- Consistent path handling across all modules + +** TODO [#B] Treesit-auto grammar pinning is fragile :stability:maintenance: +:PROPERTIES: +:FILE: modules/prog-general.el +:LINES: 106-113 +:END: + +Go grammar is pinned to v0.19.1 by directly modifying the treesit-auto-recipe-list vector with =aset=. + +*Issues:* +- Hardcoded slot index (6) will break if treesit-auto changes struct layout +- No validation that the recipe exists or that slot 6 is :revision +- Comment says "Emacs 30.2" but doesn't check version +- Will fail silently if treesit-auto changes + +*Fix:* +1. Use proper accessor functions if available (treesit-auto-recipe-revision setter) +2. Add version check: =(when (version< emacs-version "30.3") ...)= +3. Add error handling and warning if pinning fails +4. Document why pinning is needed in comments + +** TODO [#B] Test coverage gaps in critical modules :testing:quality: +:PROPERTIES: +:FILES: modules/org-agenda-config.el, modules/mail-config.el, modules/ai-config.el +:END: + +Excellent test coverage for utility functions (custom-*, undead-buffers, org-roam helpers), but missing tests for: + +*High-value untested areas:* +1. =org-agenda-config.el=: + - =cj/build-org-agenda-list= (file discovery logic) + - =cj/org-skip-subtree-if-*= functions (agenda filtering) + - =cj/org-agenda-skip-subtree-if-not-overdue= (date logic) + +2. =mail-config.el=: + - =cj/mu4e-toggle-remote-images= (security critical) + - Email header parsing/address completion logic + +3. =ai-config.el=: + - =cj/gptel-add-file= (file path handling) + - =cj/gptel--fresh-org-prefix= (timestamp formatting) + +4. =org-roam-config.el=: + - =cj/move-org-branch-to-roam= (complex string manipulation) + - Already has tests for helpers, needs integration test + +*Recommendation:* Prioritize testing the org-agenda filtering functions - they're date-sensitive and easy to break. + +** TODO [#B] System-defaults autoload has wrong function name :bug:minor: +:PROPERTIES: +:FILE: modules/system-defaults.el +:LINE: 24 +:END: + +Line 24 has =autoload= for =env-bsd-p= from =host-environment=, but this file *requires* host-environment at compile time anyway (line 20). + +*Impact:* Redundant autoload, no functional issue. + +*Fix:* Remove the autoload statement. The eval-when-compile require is sufficient. + +** TODO [#B] Inconsistent coding system declarations :maintainability: +:PROPERTIES: +:FILES: early-init.el, modules/system-defaults.el +:END: + +UTF-8 coding system is set in two places: +- =early-init.el= lines 252-261 (before package loading) +- =system-defaults.el= lines 60-69 (after package loading) + +*Impact:* Redundant configuration, harder to maintain, unclear which takes precedence. + +*Fix:* Consolidate all coding system setup in early-init.el since it needs to be set before packages load. Remove from system-defaults.el. + +** TODO [#B] Chime notification times documentation mismatch :documentation:usability: +:PROPERTIES: +:FILE: modules/org-agenda-config.el +:LINES: 268-270 +:END: + +Comment says "5 minutes before and at event time (0 minutes)" but the value is ='(5 0)=. + +*Issue:* It's unclear if 0 means "at event time" or "immediately". Better documentation needed. + +*Fix:* Improve comment: +#+begin_src elisp +;; Alert 5 minutes before event and again at event time (0 = event start) +(setq chime-alert-time '(5 0)) +#+end_src + +** TODO [#C] Org-roam template paths use string concatenation :maintainability: +:PROPERTIES: +:FILE: modules/org-roam-config.el +:LINES: 43, 48, 53, 144, 150 +:END: + +Template file paths are built with =concat= instead of using =expand-file-name= or constants. + +*Impact:* Harder to change template location, no validation that files exist. + +*Fix:* Define template constants at top of file: +#+begin_src elisp +(defconst cj/org-roam-template-dir + (expand-file-name "org-roam-templates" user-emacs-directory)) +(defconst cj/org-roam-template-v2mom + (expand-file-name "v2mom.org" cj/org-roam-template-dir)) +;; etc... +#+end_src + +Validate files exist during config load. + +** TODO [#C] Dirvish-config has long function that should be split :refactor:maintainability: +:PROPERTIES: +:FILE: modules/dirvish-config.el +:LINES: 350-406 +:END: + +=cj/dired-copy-path-as-kill= is 57 lines with multiple responsibilities: +- Validation +- Project root detection +- Home directory handling +- Org-link formatting +- Clipboard operations +- User feedback + +*Fix:* Extract smaller functions: +- =cj/--path-type-for-file= (returns :project-relative, :home-relative, or :absolute) +- =cj/--format-as-org-link= (wraps path in org syntax) +- =cj/dired-copy-path-as-kill= (orchestrates the above) + +Better testability, clearer logic flow. + +** TODO [#C] Consider Embark for file operations instead of custom dirvish bindings :enhancement:usability: +:PROPERTIES: +:FILE: modules/dirvish-config.el +:LINES: 298-334 +:END: + +Dirvish config defines many custom keybindings (d, l, L, o, O, f, P) for file operations. + +*Alternative approach:* Use Embark for contextual file actions: +- Embark already knows about files in dired +- Can define actions once, use everywhere (not just dired) +- More discoverable (embark-act shows all options) +- Less cognitive load (one keybinding to remember: C-.) + +*Recommendation:* Keep the custom functions (good implementations), but expose them via embark-act instead of direct keybindings. Cleaner keymap, more flexible. + +** TODO [#C] Missing :ensure nil for more built-in packages :correctness:minor: +:PROPERTIES: +:FILE: Multiple modules +:END: + +Some built-in package use-package declarations are missing =:ensure nil=: +- =selection-framework.el=: =delsel= (line 54) +- Several others throughout + +*Impact:* Minimal - use-package won't try to install built-ins anyway. But inconsistent with the pattern used elsewhere. + +*Fix:* Add =:ensure nil= for consistency, or document why some have it and others don't. + +** TODO [#C] Consider lazy-loading more packages :performance:startup: +:PROPERTIES: +:FILE: modules/selection-framework.el +:LINES: 22, 40, 48, 151, 166, 240-253 +:END: + +Several completion packages use =:demand t= (vertico, marginalia, orderless, prescient): +- Vertico: Reasonable to demand (core UI) +- Marginalia: Could be :after vertico +- Orderless: Could be :after vertico +- Prescient: Could be :after vertico +- Company-prescient: Could be :after (vertico company) + +*Impact:* All load at startup, adds ~100ms. Not critical, but could be optimized. + +*Fix:* Profile with esup to see actual impact. If it's <100ms, probably not worth the complexity. + +** TODO [#C] Add validation for chime.el local path :stability:enhancement: +:PROPERTIES: +:FILE: modules/org-agenda-config.el +:LINE: 264 +:END: + +Config loads chime from local directory =~/code/chime.el= without checking if it exists. + +*Impact:* If directory doesn't exist, load fails silently or with cryptic error. + +*Fix:* Add existence check with helpful error message: +#+begin_src elisp +:load-path (lambda () + (let ((path "~/code/chime.el")) + (if (file-directory-p path) + path + (warn "chime.el not found at %s" path) + nil))) +#+end_src + +** TODO [#C] Document why org-msg uses local fork :documentation:maintenance: +:PROPERTIES: +:FILE: modules/mail-config.el +:LINE: 287 +:END: + +Loading org-msg from local path =/home/cjennings/code/org-msg= instead of MELPA. + +*Question:* Is this a temporary fork with patches? Does it need to stay local? + +*Fix:* Add comment explaining: +- What changes are in local fork +- Whether it's temporary or permanent +- Link to upstream PR if patches are submitted +- When it can switch back to MELPA + +** TODO [#C] Performance: Company idle-delay is very high :performance:usability: +:PROPERTIES: +:FILE: modules/selection-framework.el +:LINE: 212 +:END: + +Company idle-delay is set to 2 seconds, which is quite slow. Modern editors typically use 0.1-0.3s. + +*Impact:* Completion feels sluggish, requires manual triggering often. + +*Recommendation:* +- If keeping Company: Try 0.3 seconds +- If switching to Corfu: Default 0.1s is perfect + +User preference, but current setting feels unresponsive. + +** TODO [#C] org-protocol may not be fully configured :functionality:enhancement: +:PROPERTIES: +:FILE: modules/org-config.el +:LINES: 94-95, 116 +:END: + +=org-protocol= is loaded (line 95) and added to org-modules (line 116), but: +- No desktop entry file mentioned +- No documentation on how to use it +- org-webclipper exists but unclear if it uses org-protocol + +*Fix:* Either: +1. Document org-protocol setup (desktop file, browser integration) +2. Remove if unused (org-webclipper might be the preferred approach) + +** TODO [#D] Consider adding use-package :diminish for minor modes :enhancement:ui: +:PROPERTIES: +:FILE: Multiple modules +:END: + +Many minor modes appear in the modeline even though doom-modeline hides them. + +*Enhancement:* Add =:diminish= to use-package declarations for: +- =symbol-overlay-mode= +- =git-gutter-mode= +- =flycheck-mode= +- =yas-minor-mode= +- =ws-butler-mode= +- =rainbow-mode= + +Cleaner modeline if doom-modeline isn't used or fails to load. + +** TODO [#D] Add .dir-locals.el for project-specific settings :enhancement:devex: +:PROPERTIES: +:FILE: N/A (new file) +:END: + +Create =.dir-locals.el= in =~/.emacs.d= to: +- Set =fill-column= to 80 for elisp files +- Enable =flycheck-mode= for all elisp +- Set =checkdoc= settings locally +- Configure =outline-minor-mode= for easy navigation + +Helps maintain consistency when editing config. + +** TODO [#D] Consider moving from doom-modeline to mood-line :enhancement:performance: +:PROPERTIES: +:FILE: modules/modeline-config.el +:END: + +Doom-modeline is feature-rich but heavy. For the minimal info you're displaying, mood-line might be better: + +*Mood-line advantages:* +- ~10x faster than doom-modeline +- Pure elisp (no external dependencies) +- Very simple, hackable codebase +- Shows exactly what you need: filename, position, major-mode +- No nerd-icons dependency (can still use them if wanted) + +*Doom-modeline advantages:* +- More battle-tested +- Better git integration +- More features (even if disabled) + +*Recommendation:* Try mood-line. If you miss doom-modeline features, easy to switch back. Performance difference is noticeable on older hardware. + +** TODO [#D] Add commentary headers to test utility files :documentation:testing: +:PROPERTIES: +:FILES: tests/testutil-general.el, tests/testutil-filesystem.el +:END: + +Test utility files should have Commentary sections explaining: +- What utilities are provided +- When to use each utility +- Examples of usage +- Relationship to quality-engineer.org guidelines + +Helps future test authors understand available infrastructure. + +** TODO [#D] keybindings.el trains user away from C-x u :ux:philosophy: +:PROPERTIES: +:FILE: modules/keybindings.el +:LINES: 126-130 +:END: + +The rebinding of =C-x u= to a message telling users to use =C-/= instead is... philosophically interesting. + +*Discussion points:* +- Enforces good habits (C-/ is indeed faster) +- But violates principle of least surprise +- Could frustrate users who know Emacs bindings +- Muscle memory is hard to retrain + +*Alternative:* Just leave =C-x u= working. Add which-key hint that C-/ is faster. + +** TODO [#D] Missing which-key labels for some custom keymaps :usability:discoverability: +:PROPERTIES: +:FILES: Multiple modules +:END: + +Some custom keymaps lack which-key descriptions: +- =cj/vc-map= is well documented (vc-config.el) +- =cj/jump-map= is well documented (keybindings.el) +- =cj/org-table-map= is well documented (org-config.el) + +But check all custom keymaps for completeness. Run: +#+begin_src elisp +(apropos-variable "^cj/.*-map$") +#+end_src + +And verify each has which-key labels. + +** TODO [#D] early-init.el could benefit from more section comments :documentation:maintainability: +:PROPERTIES: +:FILE: early-init.el +:END: + +File has good top-level commentary but individual sections could use more context: +- Why network check exists (explain offline package strategy) +- Why GC threshold is increased (explain impact) +- Benchmark-init section is good, replicate that style + +Makes it easier for others (or future you) to understand decisions. + +** TODO [#D] Consider adding persistent-scratch package :enhancement:usability: +:PROPERTIES: +:FILE: N/A +:END: + +The *scratch* buffer is very useful but loses content on restart. + +*Enhancement:* Add =persistent-scratch= package to: +- Auto-save scratch buffer contents +- Restore on startup +- Optionally persist multiple scratch buffers + +Useful for quick notes, code experiments, calculations that span sessions. + +* Priority Explanation + +** Priority A (Critical) +Issues that: +- Prevent functionality from working +- Cause errors or crashes +- Block startup or major features +- Have security implications +- Significantly impact performance (>1s delay) + +** Priority B (High) +Issues that: +- Affect architecture or maintainability significantly +- Better alternative packages exist +- Moderate performance impact (>100ms) +- Testing gaps in critical code +- Risk of future breakage + +** Priority C (Medium) +Issues that: +- Affect code quality or consistency +- Minor refactoring opportunities +- Documentation improvements +- Small usability enhancements +- Package optimization opportunities + +** Priority D (Low) +Issues that: +- Nice-to-have features +- Polish and refinement +- Alternative approaches to consider +- Documentation expansion +- Style/convention improvements + +* Package Recommendations Summary + +** Recommended Changes +1. *Company → Corfu*: Better performance, more modern, better maintained +2. *Consider mood-line over doom-modeline*: Much faster for minimal info needs +3. *Add Cape*: Modern completion-at-point backends (if switching to Corfu) +4. *Consider persistent-scratch*: QOL improvement for scratch buffer + +** Packages to Keep +- Vertico: Excellent choice, fast, well-maintained +- Consult: Perfect companion to Vertico +- Orderless: Best completion style available +- Embark: Powerful, could be used more extensively +- Marginalia: Great for annotations +- Magit: Industry standard, no alternatives +- Org-roam: Best Zettelkasten for Emacs +- Which-key: Essential for discoverability + +** Packages to Evaluate +- Dirvish: Heavy package. Consider if dired + nerd-icons + embark is sufficient +- Doom-modeline: Feature-rich but heavy. Mood-line is lighter alternative +- Projectile vs project.el: You're using Projectile. Project.el is built-in and lighter, but Projectile is more mature. Keep unless performance is an issue. + +* Testing Recommendations + +Based on your quality-engineer.org guidelines and existing test coverage: + +** Well-Tested Areas ✓ +- All custom-* utility functions (excellent coverage) +- undead-buffers (comprehensive tests) +- org-roam helpers (good coverage) +- keyboard-macros +- Test utilities themselves + +** Priority Testing Gaps + +*** High Priority +1. *org-agenda-config.el filtering functions* + - =cj/org-skip-subtree-if-habit= + - =cj/org-skip-subtree-if-not-overdue= (date-sensitive!) + - =cj/build-org-agenda-list= (file discovery) + +2. *custom-buffer-file.el interactive functions* + - Split =cj/move-buffer-and-file= into =cj/--move-buffer-and-file= (testable core) and interactive wrapper + - Same for =cj/rename-buffer-and-file= + - Same for =cj/delete-buffer-and-file= + +*** Medium Priority +3. *org-roam-config.el integration test* + - =cj/move-org-branch-to-roam= workflow test + - Test actual file creation and org-roam db update + +4. *path-utils consolidation* (after creating module) + - Test project-relative, home-relative, absolute path logic + - Test with symlinks, non-existent paths, permission errors + +5. *ai-config.el GPTel context management* + - =cj/gptel-add-file= with various file types + - =cj/gptel--fresh-org-prefix= timestamp formatting + +*** Lower Priority +6. *mail-config.el address completion* +7. *Browser/external open logic* +8. *Theme persistence functions* + +** Testing Patterns to Follow + +Your existing tests follow excellent patterns from quality-engineer.org: +- One file per function for unit tests +- Clear normal/boundary/error case organization +- Good use of test utilities (testutil-general, testutil-filesystem) +- Proper setup/teardown +- Descriptive test names + +*Keep this pattern!* It's working well for you. + +** Test-Driven Refactoring Opportunities + +When fixing Priority A-B issues, write tests first: +1. Test for the bug/missing function +2. See it fail +3. Fix the code +4. See it pass +5. Refactor with confidence + +This is especially important for: +- The missing =cj/goto-git-gutter-diff-hunks= function +- The duplicate =cj/network-available= variable +- Network check refactoring (mock the network!) + +* Architecture Observations + +** What You're Doing Right ✓ + +1. *Excellent module organization*: Clean separation of concerns +2. *Good use of user-constants.el*: Centralized configuration +3. *Comprehensive custom utilities*: Well-tested, reusable functions +4. *Modern completion framework*: Vertico/Consult/Embark stack is excellent +5. *Debug infrastructure pattern*: org-agenda-config-debug.el is a good model +6. *Test coverage philosophy*: Following your own guidelines well +7. *Early-init optimization*: Good understanding of startup optimization +8. *Custom keymap prefix*: C-; is a good choice, well-organized + +** Architectural Patterns to Consider + +1. *Consolidate common patterns*: + - Path handling → path-utils.el + - Debug logging → debug-logging.el + - File validation → file-utils.el + +2. *Reduce coupling*: + - Some modules depend on specific implementations (e.g., projectile vs project.el) + - Consider adapter pattern for switching between implementations + +3. *Error handling strategy*: + - Add condition-case wrappers around file operations + - Log errors consistently to *Messages* or debug buffer + - Fail gracefully (warn user but don't break session) + +4. *Configuration validation*: + - Add startup checks for critical paths/files + - Warn about missing optional dependencies + - Provide helpful error messages with solutions + +** Code Smells Observed + +1. *String concatenation for paths*: Use =expand-file-name= consistently +2. *Silent failures*: Some functions fail without user feedback +3. *Synchronous network operations*: Blocks startup unnecessarily +4. *Duplicate logic*: Path handling, project root detection +5. *Large functions*: Some functions >50 lines, should be split +6. *Global state*: Some functions modify global variables without clear ownership + +* Next Steps Recommendation + +** Week 1: Fix Critical Bugs (Priority A) +1. Fix duplicate =cj/network-available= declaration +2. Implement missing =cj/goto-git-gutter-diff-hunks= +3. Define =cj/log-silently= or remove usage +4. Fix network check blocking (biggest impact) + +** Week 2: Architectural Improvements (Priority B) +1. Evaluate Company vs Corfu (try Corfu for a week) +2. Consolidate debug logging +3. Create path-utils.el module +4. Add error handling to org-agenda rebuild + +** Week 3: Testing (Priority B) +1. Add tests for org-agenda filtering functions +2. Split interactive/non-interactive in custom-buffer-file +3. Add integration test for org-roam branch extraction + +** Week 4: Polish (Priority C-D) +1. Consolidate coding system setup +2. Improve documentation +3. Add which-key labels where missing +4. Clean up minor issues + +** Ongoing +- Add tests for any new functionality +- Profile startup time with esup periodically +- Review package updates for breaking changes +- Keep org-gcal, chime.el forks in sync with upstream + +* Questions for You + +1. *Company vs Corfu*: Do you have strong feelings about Company? Willing to try Corfu? + Answer: I'd be excited to try Corfu. I don't know how I'd configure it yet and it seems like a big change. However, I planned to move to Corfu sometime soon. + +2. *Network check*: Do you actually need to support offline operation, or can we simplify this? + Answer: We can simplify. There was a time when I was traveling a lot and didn't have network access. The current offline operation seemed to help startup performance. The sooner we realized we were offline, the sooner I'd have a working Emacs. Now, I don't think it provides much benefit. + +3. *Local forks*: Are chime.el and org-msg local forks temporary? When can they move to MELPA? + Answer: These are packages I currently either maintain or are in the process of creating. I need the local-paths set so I can test. If you have better ideas on how I can switch them from using a local package to using the stable version using vc-install, I'd be grateful. + +4. *Doom-modeline*: Are you using features beyond what I saw in config? Would mood-line be sufficient? + No, I'm not. Mood-line would be welcomed. + +5. *Testing coverage*: What modules do you most want tests for? Anything you're nervous about breaking? + I'll probably look for large methods with lots of LOC and multiple tasks, then break them up for better testability. I was hoping you would point those out for me so I could focus on them. Will you? + +6. *Performance*: Any particular operations feel slow? (Agenda rebuild? Mail sync? Completion?) + Org Agenda is problematic and slow. I could get some insights into how to optimize it if it were well tested. + + +Other than that, here's my roadmap for fixing/improving +- video/audio config could use some testing around which sinks to attach to. It doesn't matter to me that this is Linux centric since it's my config. +- I'm not sure the UX in flyspell and abbrev is right. It could use some more attention. +- Same with Elfeed. While I had Elfeed dashboard at some point, it doesn't bring any joy to see a mostly blank screen when I check Elfeed. +- DWIM shell commands is alright. I wonder if I can leverage them better into eshell. +- calibredb is slow, but that's the package working on 14k+ pdfs and epubs. The right thing to do is to shrink the ebook inventory to < 1k or so books. +- I was thinking of investigating bettter diffs with difftastic. If it's straightforward and simple, I should jump at it. +- I would like to implement code-maat and code-compass within Emacs +- It would be good to have a solid profiler workflow so I can see what's going on inside my own methods and packages. For instance, I don't have the tools to tell me why it takes so long to generate my daily agenda. There's little observability. +- I absolutely need a workflow so I can go from org-mode file to reveal.js presentation using httpd server in Emacs with the press of a button. I know it can be done. Export the org file with injected css into the httpd directory, start the httpd server, then open a browser fullscreen on the file. That's pretty much 90% of the work right there. +- If I could figure out the latex-config so that I could author something non-trivial, I'd be happy. Right now, it's a bunch of cut/paste snippets from everyone else's config and it doesn't work. +- mail-config: the whole attachments workflow is awkward, but so necessary for daily life. It really needs improvement. +- then there's the whole org-contacts issue working with chime.el and birthdays. sexp expressions for dates? I should either figure them out and support them in chime, or I should disable them and find another way. +- it would be good to work with llms the way I do with claude code. gptel feels like a step in that direction and you can switch backends besides. but I need to either build a bunch of tools or install an MCP server. Which way to go? +- I would like to extract the code in local-repository that creates the repo so I can unit test the hell out of it and use it as a library for my own local repo. Seems like there's too much extraneous functionality there. +- lorem seems to have an error pop up each session about the markhov chain resetting. either that's bad and should be fixed or it's not bad and lorem should stfu. +- I believe jumper should probably be my next package, but I haven't had the time to spend on it. + +* Final Thoughts + +Your config is **quite good overall**: +- Well-organized modular structure +- Excellent test coverage for utilities +- Modern package choices (mostly) +- Good documentation and comments +- Following elisp idioms reasonably well + +The critical issues (Priority A) are straightforward fixes. The architectural suggestions (Priority B) are about making the codebase more maintainable long-term. Everything else is refinement. + +Focus on: +1. Fix the bugs that break functionality +2. Consider Corfu migration (biggest ROI for effort) +3. Add tests for date-sensitive agenda logic +4. Everything else is gravy + +You're clearly thoughtful about your config and willing to refactor when needed. These issues are normal for a mature, actively-used configuration. Keep up the good work! + +* Second Opinion: Ruthless Prioritization & Reality Checks +:PROPERTIES: +:DATE: 2025-10-30 +:END: + +After reviewing your answers and additional roadmap items, here's honest feedback on what matters, what doesn't, and what you should actually do. + +** Key Insights from Your Answers + +*** Company → Corfu Migration: GREEN LIGHT +You're excited, you were planning it anyway. Corfu configuration is actually *simpler* than Company, not harder. This is a quick win with immediate UX improvement. + +*Reality:* 2 hours of work, noticeable performance improvement. Do it this weekend. + +*** Network Check: DELETE IT IMMEDIATELY +You confirmed it's technical debt from when you traveled. It's adding 1+ seconds to every startup for a problem you no longer have. + +*Reality:* 15 minutes to delete ~100 lines of code. Instant gratification. Do this first. + +*** Local Package Development Workflow: NEEDS PATTERN +You maintain chime.el and org-msg locally for testing. Current approach works but isn't elegant. See implementation pattern below. + +*** Mood-line Switch: EASY WIN +You don't need doom-modeline features. Mood-line is 10x faster and simpler. + +*Reality:* 30 minutes. Do it. + +*** Large Functions to Break Up: MOSTLY FINE +Analyzed your codebase. Most functions are reasonably sized. The ones you mentioned: +- =dirvish-config.el:350-406= =cj/dired-copy-path-as-kill= (57 lines) - Could split, but works fine +- =org-roam-config.el:250-298= =cj/move-org-branch-to-roam= (49 lines) - Actually well-designed with helpers +- =org-agenda-config.el:91-105= =cj/build-org-agenda-list= (15 lines) - Small but THIS IS YOUR PERFORMANCE BOTTLENECK + +*Reality:* Your code organization is fine. Your problem is *performance observability*, not function length. + +*** Org Agenda Performance: ROOT CAUSE IDENTIFIED +This is your actual pain point. But you're flying blind without profiling infrastructure. + +*Hypothesis:* =cj/build-org-agenda-list= recursively scans entire projects directory on every agenda open with: +- No caching (even when files unchanged) +- No error handling +- Synchronous file I/O blocking UI +- Probably hundreds of file-exists-p calls + +*Reality:* You need profiling tools FIRST, then optimization. Don't guess, measure. + +** TIER 1: Do These First (High Impact, Low Effort) +*Time investment: One weekend* +*Status: START HERE* + +*** TODO [#A] Remove network check entirely :performance:critical: +:PROPERTIES: +:FILE: early-init.el +:LINES: 76-78, 104-105, 189, 206 +:EFFORT: 15 minutes +:IMPACT: Removes 1+ seconds from every startup +:END: + +Delete the following: +1. =cj/network-available= variable declarations (both) +2. =cj/internet-up-p= function and all calls +3. =cj/use-online-repos= flag logic +4. Any package.el conditionals based on network status + +Let package.el handle its own errors gracefully. Modern Emacs does this fine. + +*** TODO [#A] Fix missing cj/log-silently definition :bug:critical: +:PROPERTIES: +:FILE: modules/wrap-up.el +:LINE: 28 +:EFFORT: 5 minutes +:END: + +Either: +1. Move =cj/log-silently= from =org-agenda-config-debug.el= to =config-utilities.el= (load early) +2. Or replace with simple =(message ...)= call + +The function is only used in one place, so simple message might be sufficient. + +*** TODO [#A] Remove duplicate cj/network-available declaration :bug:critical: +:PROPERTIES: +:FILE: early-init.el +:LINES: 104-105 +:EFFORT: 2 minutes +:END: + +Delete the duplicate at lines 104-105. Keep only the first declaration at line 76. + +*Note:* This becomes moot if you delete network check entirely (recommended). + +*** TODO [#A] Fix missing cj/goto-git-gutter-diff-hunks function :bug:critical: +:PROPERTIES: +:FILE: modules/vc-config.el +:LINE: 123 +:EFFORT: 15 minutes +:END: + +Keybinding =C-; v d= references undefined function. + +Implement as: +#+begin_src elisp +(defun cj/goto-git-gutter-diff-hunks () + "Jump to git-gutter diff hunks using consult." + (interactive) + (require 'git-gutter) + (consult-line "^[+\\-]")) ;; Or use git-gutter:popup-hunk +#+end_src + +Or remove the keybinding if not needed. + +*** TODO [#B] Migrate Company to Corfu :enhancement:performance: +:PROPERTIES: +:FILE: modules/selection-framework.el +:LINES: 202-236 +:EFFORT: 2 hours +:IMPACT: 3x faster completion, simpler config +:END: + +*Why Corfu:* +- ~3x faster (child frames vs overlays) +- Better maintained +- Simpler codebase (~500 LOC vs ~3000 LOC) +- Works seamlessly with Vertico/Consult +- Better integration with Cape for backends + +*Migration:* +#+begin_src elisp +(use-package corfu + :ensure t + :demand t + :bind (:map corfu-map + ("TAB" . corfu-next) + ("S-TAB" . corfu-previous)) + :custom + (corfu-auto t) + (corfu-auto-delay 0.1) ;; Much faster than your 2s company delay! + (corfu-auto-prefix 2) + (corfu-quit-no-match 'separator) + :init + (global-corfu-mode)) + +(use-package corfu-popupinfo + :after corfu + :hook (corfu-mode . corfu-popupinfo-mode) + :custom + (corfu-popupinfo-delay '(0.5 . 0.2))) + +(use-package cape + :ensure t + :after corfu + :init + (add-to-list 'completion-at-point-functions #'cape-dabbrev) + (add-to-list 'completion-at-point-functions #'cape-file) + (add-to-list 'completion-at-point-functions #'cape-keyword)) +#+end_src + +Remove entire company configuration. Keep mu4e completion settings (Corfu respects them). + +*Configuration is actually simpler than Company.* Just do it. + +*** TODO [#B] Switch doom-modeline to mood-line :enhancement:performance: +:PROPERTIES: +:FILE: modules/modeline-config.el +:EFFORT: 30 minutes +:IMPACT: 10x faster modeline updates +:END: + +Replace doom-modeline config with: +#+begin_src elisp +(use-package mood-line + :ensure t + :demand t + :config + (mood-line-mode)) +#+end_src + +That's it. Mood-line shows: buffer name, position, major-mode, vc status. Pure elisp, very fast. + +If you miss doom-modeline features, easy to switch back. But try mood-line for a week. + +*** TODO [#C] Fix lorem markov chain error :bug:minor: +:PROPERTIES: +:FILE: modules/lorem-config.el (or wherever lorem is configured) +:EFFORT: 5 minutes +:END: + +The error message is probably harmless initialization noise. Either: +1. Wrap lorem initialization in =(with-demoted-errors ...)= +2. Configure lorem to suppress the warning +3. Disable lorem if you don't use it + +Check your config for lorem setup and add error suppression. + +** TIER 2: Build Observability Infrastructure (HIGHEST VALUE) +*Time investment: One week* +*Status: CRITICAL DEPENDENCY FOR EVERYTHING ELSE* + +*** TODO [#A] Create debug-profiling.el module :infrastructure:performance: +:PROPERTIES: +:FILE: modules/debug-profiling.el (new file) +:EFFORT: 3-4 hours +:IMPACT: Unlocks all performance optimization work +:END: + +This is your #1 blocker for fixing org-agenda and other performance issues. + +Create comprehensive profiling infrastructure: + +#+begin_src elisp +;;; debug-profiling.el --- Profiling and performance debugging tools -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unified profiling infrastructure for measuring and optimizing performance. +;; Provides helpers for: +;; - Function timing (individual and batch) +;; - Memory profiling +;; - Startup profiling +;; - Interactive profiling sessions +;; +;; Usage: +;; M-x cj/profile-function RET cj/build-org-agenda-list RET +;; M-x cj/profile-startup +;; (cj/time-it "agenda rebuild" (cj/build-org-agenda-list)) + +;;; Code: + +(require 'profiler) +(require 'benchmark) + +(defvar cj/profiling-log-buffer "*CJ Profile Log*" + "Buffer name for profiling results.") + +(defvar cj/profiling-enabled nil + "When non-nil, enable automatic timing of instrumented functions.") + +(defun cj/time-it (description &rest body) + "Execute BODY and log timing with DESCRIPTION." + (let* ((start-time (current-time)) + (result (car body)) + (elapsed (float-time (time-subtract (current-time) start-time)))) + (with-current-buffer (get-buffer-create cj/profiling-log-buffer) + (goto-char (point-max)) + (insert (format "[%s] %s: %.3f sec\n" + (format-time-string "%H:%M:%S") + description + elapsed))) + (message "%s: %.3f sec" description elapsed) + result)) + +(defmacro cj/measure (&rest body) + "Measure execution time of BODY, return result and timing." + `(let ((start-time (current-time)) + (result (progn ,@body)) + (end-time (current-time))) + (cons result (float-time (time-subtract end-time start-time))))) + +(defun cj/profile-function (function &optional runs) + "Profile FUNCTION by calling it RUNS times (default 1). +Shows results in dedicated buffer with call tree." + (interactive + (list (intern (completing-read "Function to profile: " obarray 'fboundp t)) + (when current-prefix-arg + (read-number "Number of runs: " 10)))) + (setq runs (or runs 1)) + (profiler-start 'cpu) + (dotimes (_ runs) + (funcall function)) + (profiler-stop) + (profiler-report) + (message "Profiled %s (%d run%s)" function runs (if (> runs 1) "s" ""))) + +(defun cj/profile-startup () + "Profile Emacs startup by restarting with profiler enabled. +Results are saved to profile.el in user-emacs-directory." + (interactive) + (let ((profile-file (expand-file-name "startup-profile.el" user-emacs-directory))) + (profiler-start 'cpu+mem) + (message "Restart Emacs to profile startup. Results will be in %s" profile-file) + (add-hook 'emacs-startup-hook + (lambda () + (profiler-report) + (profiler-report-write-profile profile-file) + (profiler-stop) + (message "Startup profiling complete. See %s" profile-file))))) + +(defun cj/benchmark-function (function &optional iterations) + "Benchmark FUNCTION over ITERATIONS (default 100). +Shows average, min, max, and total time." + (interactive + (list (intern (completing-read "Function to benchmark: " obarray 'fboundp t)) + (read-number "Iterations: " 100))) + (setq iterations (or iterations 100)) + (let ((times '())) + (dotimes (_ iterations) + (let* ((result (cj/measure (funcall function))) + (elapsed (cdr result))) + (push elapsed times))) + (let* ((total (apply #'+ times)) + (avg (/ total iterations)) + (min-time (apply #'min times)) + (max-time (apply #'max times))) + (message "%s (%d iterations): avg=%.4fs min=%.4fs max=%.4fs total=%.2fs" + function iterations avg min-time max-time total)))) + +(defun cj/instrument-package (package) + "Instrument all functions in PACKAGE for profiling with elp." + (interactive + (list (intern (completing-read "Package to instrument: " obarray)))) + (elp-instrument-package (symbol-name package)) + (message "Instrumented package: %s. Run M-x elp-results to see data." package)) + +(defun cj/profile-agenda-rebuild () + "Profile org-agenda rebuild with detailed timing." + (interactive) + (require 'org-agenda) + (cj/time-it "Full agenda rebuild" (cj/build-org-agenda-list))) + +(defun cj/show-profile-log () + "Display the profiling log buffer." + (interactive) + (display-buffer (get-buffer-create cj/profiling-log-buffer))) + +(defun cj/clear-profile-log () + "Clear the profiling log buffer." + (interactive) + (with-current-buffer (get-buffer-create cj/profiling-log-buffer) + (erase-buffer))) + +;; Keybindings (add to your keybindings.el) +;; (global-set-key (kbd "C-; p f") #'cj/profile-function) +;; (global-set-key (kbd "C-; p b") #'cj/benchmark-function) +;; (global-set-key (kbd "C-; p l") #'cj/show-profile-log) +;; (global-set-key (kbd "C-; p a") #'cj/profile-agenda-rebuild) + +(provide 'debug-profiling) +;;; debug-profiling.el ends here +#+end_src + +Load this early in init.el: +#+begin_src elisp +(when (or (eq cj/debug-modules t) + (memq 'profiling cj/debug-modules)) + (require 'debug-profiling (expand-file-name "modules/debug-profiling" user-emacs-directory))) +#+end_src + +*** TODO [#A] Profile org-agenda-rebuild to find bottleneck :performance:critical: +:PROPERTIES: +:FILE: modules/org-agenda-config.el +:EFFORT: 1 hour +:DEPENDS: debug-profiling.el +:END: + +Once profiling infrastructure exists: + +1. Run: =M-x cj/profile-function RET cj/build-org-agenda-list RET= +2. Run: =M-x cj/benchmark-function RET cj/build-org-agenda-list RET 10= +3. Instrument org-agenda package: =M-x cj/instrument-package RET org-agenda RET= +4. Review results with =M-x elp-results= + +*Hypothesis to test:* +- Is time spent in file I/O (directory-files-recursively)? +- Is it parsing org files? +- Is it just too many files? +- Is there a slow helper function being called repeatedly? + +*Expected findings:* +- Probably 80%+ time in =cj/add-files-to-org-agenda-files-list= +- Likely no caching, runs every time +- Possible N+1 file access pattern + +*Next steps after profiling:* +- Add file-notify watchers to invalidate cache only when projects-dir changes +- Cache the file list +- Run rebuild async on startup + +*** TODO [#B] Add instrumentation to cj/build-org-agenda-list :performance:observability: +:PROPERTIES: +:FILE: modules/org-agenda-config.el +:EFFORT: 30 minutes +:DEPENDS: debug-profiling.el, profiling results +:END: + +After profiling identifies bottleneck, add instrumentation: + +#+begin_src elisp +(defun cj/build-org-agenda-list () + "Rebuilds the org agenda list with instrumentation." + (interactive) + (let ((start-time (current-time))) + ;; reset org-agenda-files + (setq org-agenda-files (list inbox-file schedule-file gcal-file)) + + ;; Time the expensive operation + (let ((scan-start (current-time))) + (cj/add-files-to-org-agenda-files-list projects-dir) + (message "Scanned projects-dir in %.3f sec" + (float-time (time-subtract (current-time) scan-start)))) + + (let ((total-time (float-time (time-subtract (current-time) start-time)))) + (message "Rebuilt org-agenda-files in %.3f sec (%d files)" + total-time + (length org-agenda-files))))) +#+end_src + +This gives you observability on every agenda rebuild. + +*** TODO [#B] Add caching to org-agenda rebuild :performance:critical: +:PROPERTIES: +:FILE: modules/org-agenda-config.el +:EFFORT: 2 hours +:DEPENDS: Profiling results +:END: + +*Only do this after profiling confirms file scanning is the bottleneck.* + +Implement caching with file-notify: + +#+begin_src elisp +(defvar cj/org-agenda-cache nil + "Cached list of org-agenda files.") + +(defvar cj/org-agenda-cache-dirty t + "When non-nil, agenda cache needs rebuilding.") + +(defun cj/invalidate-agenda-cache (&rest _) + "Mark agenda cache as dirty." + (setq cj/org-agenda-cache-dirty t)) + +(defun cj/build-org-agenda-list-cached () + "Build org-agenda list with caching." + (interactive) + (when (or (not cj/org-agenda-cache) + cj/org-agenda-cache-dirty) + (let ((start-time (current-time))) + (setq cj/org-agenda-cache (list inbox-file schedule-file gcal-file)) + (cj/add-files-to-org-agenda-files-list projects-dir) + (setq cj/org-agenda-cache org-agenda-files) + (setq cj/org-agenda-cache-dirty nil) + (message "Rebuilt agenda cache in %.3f sec (%d files)" + (float-time (time-subtract (current-time) start-time)) + (length cj/org-agenda-cache)))) + (setq org-agenda-files cj/org-agenda-cache)) + +;; Set up file watching +(require 'filenotify) +(file-notify-add-watch projects-dir + '(change) + #'cj/invalidate-agenda-cache) +#+end_src + +*** TODO [#B] Test org-agenda filtering functions :testing:critical: +:PROPERTIES: +:FILE: tests/org-agenda-config-test.el (new file) +:EFFORT: 2-3 hours +:END: + +These functions are date-sensitive and break silently. High-value tests: + +#+begin_src elisp +;;; org-agenda-config-test.el --- Tests for org-agenda-config -*- lexical-binding: t; -*- + +(require 'ert) +(require 'org-agenda-config) + +(ert-deftest test-cj/org-skip-subtree-if-habit () + "Test habit filtering." + (with-temp-buffer + (org-mode) + (insert "* TODO Test\n:PROPERTIES:\n:STYLE: habit\n:END:\n") + (goto-char (point-min)) + (should (cj/org-skip-subtree-if-habit)))) + +(ert-deftest test-cj/org-skip-subtree-if-not-overdue () + "Test overdue task detection." + (with-temp-buffer + (org-mode) + ;; Task scheduled yesterday (overdue) + (insert "* TODO Overdue\nSCHEDULED: <2025-10-29>\n") + (goto-char (point-min)) + (should-not (cj/org-skip-subtree-if-not-overdue)) + + ;; Task scheduled tomorrow (not overdue) + (erase-buffer) + (insert "* TODO Future\nSCHEDULED: <2025-10-31>\n") + (goto-char (point-min)) + (should (cj/org-skip-subtree-if-not-overdue)))) +#+end_src + +** TIER 3: Quick Wins (After Tier 1 & 2) +*Time investment: 1-2 hours each* +*Status: Only tackle after profiling infrastructure is done* + +*** TODO [#B] Implement org-to-reveal.js workflow :enhancement:usability: +:PROPERTIES: +:FILE: modules/org-config.el or new modules/org-present-config.el +:EFFORT: 2 hours +:IMPACT: Solves specific, concrete need +:END: + +You said: "I know it can be done. Export to reveal.js, start httpd, open browser." + +You're right, it's straightforward: + +#+begin_src elisp +(use-package ox-reveal + :ensure t + :after org + :custom + (org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js")) + +(use-package simple-httpd + :ensure t + :custom + (httpd-port 8080) + (httpd-root "~/presentations")) + +(defun cj/org-present-reveal () + "Export current org file to reveal.js and present via httpd." + (interactive) + (unless (eq major-mode 'org-mode) + (user-error "Not in an org-mode buffer")) + + (let* ((httpd-dir (expand-file-name httpd-root)) + (output-file (expand-file-name + (concat (file-name-base) ".html") + httpd-dir))) + + ;; Ensure output directory exists + (make-directory httpd-dir t) + + ;; Export to reveal.js HTML + (org-reveal-export-to-html) + + ;; Move to httpd directory + (let ((exported-file (concat (file-name-base) ".html"))) + (rename-file exported-file output-file t)) + + ;; Start httpd if not running + (unless (process-status "httpd") + (httpd-start) + (message "Started httpd server on port %d" httpd-port)) + + ;; Open in browser (fullscreen) + (let ((url (format "http://localhost:%d/%s" + httpd-port + (file-name-nondirectory output-file)))) + (browse-url url) + (message "Presenting: %s" url)))) + +(with-eval-after-load 'org + (define-key org-mode-map (kbd "C-c C-v p") #'cj/org-present-reveal)) +#+end_src + +*That's it.* 30 lines. One keybinding. Done. + +Test with a simple org file: +#+begin_src org +,#+TITLE: Test Presentation +,#+REVEAL_THEME: moon + +,* Slide 1 +Content here + +,* Slide 2 +More content +#+end_src + +Press =C-c C-v p= and you're presenting. + +*** TODO [#B] Set up difftastic integration :enhancement:usability: +:PROPERTIES: +:FILE: modules/vc-config.el or modules/magit-config.el +:EFFORT: 30 minutes +:END: + +Install difftastic: =sudo pacman -S difftastic= or =cargo install difftastic= + +Then add to magit: + +#+begin_src elisp +(use-package magit-difftastic + :ensure t + :after magit + :bind (:map magit-status-mode-map + ("C-c d" . magit-difftastic-show)) + :custom + (magit-difftastic-executable "difft")) +#+end_src + +Or for native integration: +#+begin_src elisp +(setq magit-diff-refine-hunk 'all) ;; Already have this probably + +(defun cj/magit-difftastic-diff (args files) + "Show diff using difftastic." + (interactive (magit-diff-arguments)) + (require 'magit) + (let ((command (concat "difft " (mapconcat #'identity args " ")))) + (magit-git-command command))) + +(transient-append-suffix 'magit-diff '(-1 -1) + '("D" "Difftastic diff" cj/magit-difftastic-diff)) +#+end_src + +Simple and useful. Just do it. + +*** TODO [#C] Implement local package development workflow :enhancement:architecture: +:PROPERTIES: +:FILES: modules/org-agenda-config.el, modules/mail-config.el +:EFFORT: 1 hour +:END: + +You maintain chime.el and org-msg. You need to switch between local dev and stable versions easily. + +*Clean pattern:* + +In =user-constants.el=: +#+begin_src elisp +(defcustom cj/development-packages '() + "List of packages to load from local development paths. +When a package symbol is in this list, load from ~/code/<package>. +Otherwise use stable version via vc-install or MELPA. + +Example: (setq cj/development-packages '(chime org-msg))" + :type '(repeat symbol) + :group 'cj) + +(defun cj/use-local-package-p (package) + "Return non-nil if PACKAGE should be loaded from local path." + (memq package cj/development-packages)) +#+end_src + +In your package configs: +#+begin_src elisp +;; Chime configuration +(if (cj/use-local-package-p 'chime) + ;; Development mode: use local path + (use-package chime + :load-path "~/code/chime.el" + :demand t + ;; ... rest of config ... + ) + ;; Production mode: use stable version + (use-package chime + :vc (:url "https://github.com/yourusername/chime.el") + :demand t + ;; ... rest of config ... + )) +#+end_src + +To switch modes, just toggle the symbol in =cj/development-packages= and restart Emacs. + +*Benefits:* +- Clear intent: variable name says what it does +- Easy to toggle: add/remove symbols from list +- Version controlled: your config documents which packages you're actively developing +- No path duplication: define path logic once + +** TIER 4: Maybe/Someday (Proceed with Caution) +*Time investment: Unknown, possibly infinite* +*Status: Only do if you have concrete use case* + +*** HOLD [#D] Investigate code-maat/code-compass in Emacs :enhancement:tooling: +:PROPERTIES: +:REASON: Complexity vs value unclear +:END: + +*Hard truth:* This is a big project. Code-maat is a mature Java tool with significant functionality. + +*Questions before investing time:* +1. What would Emacs integration give you that CLI doesn't? +2. How often do you actually use code-maat? +3. Is this solving a real problem or is it intellectually interesting? + +*Recommendation:* Use the CLI tools. Call them from Emacs if needed: +#+begin_src elisp +(defun cj/code-maat-analyze (git-log-file) + "Run code-maat analysis on GIT-LOG-FILE." + (interactive "fGit log file: ") + (async-shell-command + (format "code-maat -l %s -c git2 -a coupling" git-log-file) + "*Code Maat Results*")) +#+end_src + +Don't reimplement it in elisp unless you have a very specific need the CLI can't meet. + +*** HOLD [#D] Fix LaTeX configuration :documentation:enhancement: +:PROPERTIES: +:REASON: No concrete use case yet +:END: + +You said: "Right now, it's a bunch of cut/paste snippets from everyone else's config and it doesn't work." + +*Hard truth:* LaTeX config is complex because LaTeX is complex. Don't invest time until you have a specific document you need to write. + +*When you do need it:* +1. Start with one concrete document (paper? letter? resume?) +2. Get that one document working +3. Extract the config that made it work +4. Then generalize + +*Don't* try to build a complete LaTeX environment speculatively. You'll waste time on edge cases you'll never hit. + +*Minimal starting point when you need it:* +#+begin_src elisp +(use-package auctex + :ensure t + :defer t + :mode ("\\.tex\\'" . LaTeX-mode) + :custom + (TeX-auto-save t) + (TeX-parse-self t) + (TeX-master nil)) +#+end_src + +That's it. Start there. Add more when you hit actual problems. + +*** HOLD [#D] Improve Elfeed dashboard UX :enhancement:ui: +:PROPERTIES: +:REASON: Need to clarify actual usage patterns +:END: + +You said: "it doesn't bring any joy to see a mostly blank screen when I check Elfeed." + +*Questions:* +1. Do you actually use RSS regularly, or is it aspirational? +2. If blank screen = no new items, is that a problem? +3. Would you use RSS more if the UX was better, or is RSS not part of your workflow? + +*Options:* +1. *If you don't use RSS:* Remove Elfeed. One less thing to maintain. +2. *If you want to use RSS more:* Start by curating better feeds, not fixing UI +3. *If blank screen bothers you:* Add a "no new items" message or show last N items regardless of read status + +Don't fix tooling for a workflow you're not sure you want. + +*** HOLD [#D] DWIM shell + eshell integration :enhancement:shell: +:PROPERTIES: +:REASON: Current solution works fine +:END: + +You have 815 lines of dwim-shell-config. It works. Leave it alone. + +*Hard truth:* This is a working system. "I wonder if I can leverage them better" is not a problem statement. + +Unless you have a specific workflow that's currently painful, don't touch this. + +*** HOLD [#D] Calibredb performance optimization :performance: +:PROPERTIES: +:REASON: Root cause is data volume, not code +:END: + +You correctly identified: "The right thing to do is to shrink the ebook inventory to < 1k or so books." + +*You already know the answer.* Don't optimize code when the problem is 14k+ files. + +Curate your library. Delete what you won't read. This is a data problem, not a code problem. + +*** HOLD [#D] Flyspell/abbrev UX improvements :enhancement:usability: +:PROPERTIES: +:REASON: Vague complaint, no specific pain points +:END: + +You said: "I'm not sure the UX is right. It could use some more attention." + +*This is not actionable.* Come back when you can say: +- "Pressing X does Y but I want Z" +- "It's too slow when..." +- "It conflicts with..." + +Until then, it's bikeshedding. + +*** HOLD [#D] Video/audio config sink testing :testing:enhancement: +:PROPERTIES: +:REASON: Nice to have, not blocking anything +:END: + +You said: "could use some testing around which sinks to attach to" + +*Questions:* +1. Does it currently work for your use case? +2. Do you switch audio sinks often? +3. Is this actually painful or just "would be nice"? + +If it works, leave it. If it doesn't work, describe the failure mode first. + +*** HOLD [#D] Build jumper package :project:packaging: +:PROPERTIES: +:REASON: Another package to maintain, unclear value +:END: + +You said: "I believe jumper should probably be my next package, but I haven't had the time." + +*Hard questions:* +1. What problem does jumper solve? +2. Do existing navigation packages (avy, ace-window, consult-line) not solve it? +3. Are you building this for yourself or for others? +4. How will you maintain it alongside chime and org-msg? + +If you're excited about it: Build it. But recognize it's a significant time investment. + +If you're not excited: Don't. You already maintain two packages. + +*** HOLD [#D] Extract local-repository creation logic :refactor:library: +:PROPERTIES: +:REASON: Unclear benefit +:END: + +You said: "extract the code... so I can unit test the hell out of it and use it as a library" + +*Questions:* +1. What would you use the library for beyond local-repository? +2. Is local-repository not working currently? +3. Are you planning to build other tools that need this? + +Don't extract libraries speculatively. Extract when you have two concrete use cases. + +*** HOLD [#D] Org-contacts + chime.el birthdays integration :enhancement:integration: +:PROPERTIES: +:REASON: Should be fixed in chime, not config +:END: + +You said: "I should either figure out sexp expressions and support them in chime, or disable them" + +*This is a chime.el issue, not a config issue.* + +File it as a chime.el feature request or bug. Your config shouldn't work around package limitations. + +** What NOT To Do + +*** DON'T: Optimize Before Profiling +You can't fix performance issues you can't measure. Build profiling infrastructure FIRST. + +*** DON'T: Build Speculative Libraries +Extract code into libraries when you have 2+ concrete use cases. Not before. + +*** DON'T: Fix Vague Complaints +"UX could be better" is not actionable. Come back with specific pain points. + +*** DON'T: Reimplement Mature CLI Tools +code-maat is Java. It works. Don't rewrite it in elisp. + +*** DON'T: Configure for Hypothetical Use Cases +LaTeX doesn't work? Come back when you need to write a specific LaTeX document. + +*** DON'T: Maintain More Packages Than You Can Support +You have chime and org-msg. That's plenty. Think hard before adding jumper. + +** Large Functions Analysis + +You asked me to identify large, complex functions that need breaking up. + +*Reality check:* Your functions are mostly fine. Here's what I found: + +*** Functions You Mentioned + +*1. dirvish-config.el:350-406 =cj/dired-copy-path-as-kill= (57 lines)* +- Has 6 responsibilities: validation, project detection, path formatting, org-link formatting, clipboard, feedback +- *Status:* Works fine, moderately complex +- *Recommendation:* Could extract helpers (=cj/--determine-path-type=, =cj/--format-path-for-display=), but not urgent +- *Priority:* [#C] - Refactor for testability when you have time + +*2. org-roam-config.el:250-298 =cj/move-org-branch-to-roam= (49 lines)* +- Already well-decomposed with helper functions! +- Uses =cj/org-link-get-description=, =cj/--generate-roam-slug=, =cj/--demote-org-subtree=, =cj/--format-roam-node= +- *Status:* Good design, clear logic flow +- *Recommendation:* Add integration test, but don't refactor further +- *Priority:* [#B] - Add tests, keep implementation + +*3. org-agenda-config.el:91-105 =cj/build-org-agenda-list= (15 lines)* +- Small function, but THIS IS YOUR PERFORMANCE BOTTLENECK +- Not because of size, but because it recursively scans directories without caching +- *Status:* CRITICAL ISSUE +- *Recommendation:* Profile it, add caching, add error handling +- *Priority:* [#A] - Profile and optimize ASAP + +*** Other Large Files + +*dwim-shell-config.el (815 lines)* +- Collection of DWIM commands +- *Status:* Works fine +- *Recommendation:* Leave it alone unless something breaks + +*custom-comments.el (636 lines)* +- Well-tested utility functions +- *Status:* No issues +- *Recommendation:* Keep as is + +*music-config.el (484 lines)* +- EMMS configuration +- *Recommendation:* Review when you mentioned video/audio sink testing + +*** The Real Issue + +Your problem isn't function length. Your problem is *performance observability*. + +You need: +1. Profiling infrastructure (debug-profiling.el) +2. Instrumentation in critical paths +3. Caching where appropriate +4. Error handling in file operations + +Focus on TIER 2 (observability infrastructure) before worrying about function decomposition. + +** Concrete Action Plan: This Weekend + +*** Saturday Morning (2-3 hours) + +1. *Delete network check* (15 min) + - Remove =cj/internet-up-p=, =cj/network-available=, all related code + - Test startup, should be 1+ seconds faster + +2. *Fix Priority A bugs* (45 min) + - Fix duplicate =cj/network-available= (if not deleted above) + - Implement =cj/goto-git-gutter-diff-hunks= or remove binding + - Fix =cj/log-silently= (move to config-utilities or use message) + +3. *Fix lorem error* (5 min) + - Find lorem config, suppress error message + +4. *Test everything still works* (15 min) + +*** Saturday Afternoon (2-3 hours) + +5. *Corfu migration* (2 hours) + - Remove company configuration + - Add corfu + corfu-popupinfo + cape + - Test completion in elisp, org, mu4e + - Verify mu4e still excludes completion + +6. *Switch to mood-line* (30 min) + - Remove doom-modeline + - Add mood-line configuration + - Restart, verify modeline works + +*** Sunday Morning (3-4 hours) + +7. *Create debug-profiling.el* (3 hours) + - Use the template I provided above + - Add to modules/ + - Configure in user-constants.el with =cj/debug-modules= + - Test basic commands work + +8. *Profile org-agenda* (30 min) + - Run =M-x cj/profile-agenda-rebuild= + - Run =M-x cj/profile-function RET cj/build-org-agenda-list= + - Document findings in *CJ Profile Log* + +9. *Write up findings* (30 min) + - What's slow? + - Why is it slow? + - What's the fix? + +*** Sunday Afternoon (Optional, 1-2 hours) + +10. *Reveal.js workflow* (2 hours) + - Implement =cj/org-present-reveal= + - Test with simple presentation + - Bind to key + +11. *Difftastic* (30 min) + - Install difftastic + - Configure magit integration + - Test on a real diff + +** Expected Outcomes + +After this weekend: +- ✅ Startup 1+ seconds faster (network check removed) +- ✅ No more broken keybindings or missing functions +- ✅ Completion is faster and more responsive (Corfu) +- ✅ Modeline updates are faster (mood-line) +- ✅ You have profiling infrastructure for all future optimization +- ✅ You understand why org-agenda is slow +- ✅ You have concrete data to guide optimization +- ✅ (Optional) You can present org files as reveal.js slides + +*** After This Weekend + +*Then stop and reassess.* + +- How do you feel about the changes? +- What's still bothering you? +- Is org-agenda faster after caching? (if you implement it) +- What's the next pain point? + +Don't commit to more work until you've lived with these changes for a week. + +** My Question for You + +Looking at your list of improvements, I see someone who: +- Likes building tools (jumper, code-maat integration, etc.) +- Gets excited by new technologies (difftastic, MCP, reveal.js) +- Has many "wouldn't it be cool if..." ideas + +But I also see someone who: +- Already has a working, sophisticated config +- Knows what's actually painful (org-agenda performance, mail attachments) +- Has limited time (maintaining chime, org-msg) + +*So here's my question:* + +*Are you building tools because they solve real problems, or because you enjoy building?* + +Both are valid answers! But the answer changes what you should prioritize: + +*If solving real problems:* +- Focus on TIER 1 & 2 (bugs, performance, observability) +- Ignore most of TIER 4 +- Build reveal.js workflow (concrete need) +- Fix mail attachments (daily pain) + +*If you enjoy building:* +- Pick ONE project (jumper? code-compass? profiling infrastructure?) +- Go deep on it +- Accept that other stuff will wait +- Be honest that it's for learning/fun, not necessity + +*My read:* You're mostly problem-solving, but you get distracted by shiny tools. + +*My advice:* Fix the pain points first (TIER 1 & 2). Then build one fun project. Then reassess. + +** gptel vs MCP: Specific Guidance + +You asked: "I need to either build a bunch of tools or install an MCP server. Which way to go?" + +*Answer: Build gptel functions. Here's why:* + +*** Why gptel + elisp Functions + +*Advantages:* +1. *Simpler:* Just elisp functions, no external server +2. *Integrated:* Native Emacs, shares your environment +3. *Debuggable:* Use edebug, step through code +4. *Maintainable:* You understand elisp, you wrote the config +5. *Flexible:* Can use any elisp API (org, dired, magit, etc.) +6. *Portable:* Works on any system with Emacs + gptel + +*What you can build:* +#+begin_src elisp +;; Example gptel tools +(defun cj/gptel-add-project-context () + "Add project README and key files to gptel context." + (interactive) + (let ((readme (expand-file-name "README.md" (project-root)))) + (when (file-exists-p readme) + (cj/gptel-add-file readme)))) + +(defun cj/gptel-analyze-function-at-point () + "Send function at point to LLM for analysis." + (interactive) + (let ((func (elisp--fnsym-in-current-sexp))) + (gptel-send (format "Analyze this function: %s" (symbol-function func))))) + +(defun cj/gptel-git-diff-review () + "Send current git diff to LLM for review." + (interactive) + (let ((diff (shell-command-to-string "git diff HEAD"))) + (gptel-send (concat "Review this diff:\n\n" diff)))) +#+end_src + +*This is much simpler than MCP and covers 90% of use cases.* + +*** When MCP Makes Sense + +*Only use MCP if:* +1. You want to share tools across multiple applications (VS Code + Emacs + CLI) +2. You need sandboxed execution for security +3. You're building reusable infrastructure for others +4. You want to use existing MCP servers someone else maintains + +*For Emacs-only workflows: Use gptel + elisp.* + +*** Recommended Approach + +1. Start with gptel + simple elisp functions +2. Build 3-5 useful commands (add-file, analyze-function, git-diff, org-context) +3. Use them for a month +4. If you find yourself wanting features MCP provides, reassess + +*Don't build MCP infrastructure speculatively.* Build it when gptel + elisp isn't enough. + +** Final Thoughts: Adjusted Priorities + +Your original document was thorough and well-analyzed. My additions: + +1. *Ruthless prioritization* - 4 tiers instead of flat list +2. *Observability first* - Can't optimize what you can't measure +3. *Reality checks* - Some "nice to haves" are rabbit holes +4. *Concrete implementations* - Not just "do this" but "here's the code" +5. *Hard questions* - Before building, ask "why?" and "for whom?" + +*Your config is good.* The issues are normal for a mature system. + +*Focus on:* +1. Fix the bugs (TIER 1) +2. Build profiling infrastructure (TIER 2) +3. Pick ONE fun project (reveal.js or difftastic) +4. Ignore everything else for now + +*Then live with it for a month* and see what still bothers you. + +You're thoughtful, skilled, and self-aware. You'll make good decisions. Just don't try to do everything at once. + +Good luck! 🚀 |
