summaryrefslogtreecommitdiff
path: root/issues.org
diff options
context:
space:
mode:
Diffstat (limited to 'issues.org')
-rw-r--r--issues.org1879
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! 🚀