summaryrefslogtreecommitdiff
path: root/issues.org
diff options
context:
space:
mode:
Diffstat (limited to 'issues.org')
-rw-r--r--issues.org1879
1 files changed, 0 insertions, 1879 deletions
diff --git a/issues.org b/issues.org
deleted file mode 100644
index c61b8517..00000000
--- a/issues.org
+++ /dev/null
@@ -1,1879 +0,0 @@
-#+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! 🚀