1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
|
#+TITLE: Module Inventory — init.el Load Graph
#+AUTHOR: Craig Jennings
#+DATE: 2026-05-24
* Purpose
Living per-module inventory for the [[file:init-load-graph.org][init.el load-graph refactor]]. The
spec's module-category table is the seed; this file is the per-module truth as
each module is inspected and classified. A module moves from [[*Pending
classification][Pending classification]] into [[*Classified modules][Classified
modules]] once its source has been read and its load-graph header written.
Phase 1 exit criterion: every module required by =init.el= (102 total) is
represented here with a category and target load shape, every eager survivor
has a documented reason, and top-level timer/process/network side effects are
identified. Classification proceeds in batches; the header-validation test
=tests/test-init-module-headers.el= enforces the contract on each classified
module.
This inventory is independent from the helper inventory owned by
=utility-consolidation.org=.
* Status
- Phase 1 (Inventory and Contracts), in progress.
- Batch 1 (Foundation, Layer 1): classified. 7 modules.
- Batch 2 (Text/editing command modules, Layer 2): classified. 9 modules.
- Batch 3 (Core libraries and command modules): classified. 7 modules.
- Batch 4 (UI / core-UX modules, Layer 2): classified. 10 modules.
- Batch 5 (Dev entry-points, diff, help, lint, VC, Layer 2): classified. 9 modules.
- Batch 6 (Programming modules, Layer 2-4): classified. 10 modules.
- Batch 7 (Org modules, Layer 3-4): classified. 13 modules.
- Batch 8 (Domain / integration / optional modules, Layer 2-4): classified. 17 modules.
- 82 of 102 modules classified. (elfeed-config deferred — see Pending classification.)
- No load-order changes have been made; =init.el= keeps its current eager order.
* Legend
Category key (a module may carry two, e.g. =F/S=):
- =F= foundation or shared library/config
- =C= core eager UX
- =P= package configuration (usually hook/command/package loaded)
- =D= domain workflow with a possible eager reason
- =S= startup side-effect / timer / process owner
- =O= optional, entertainment, experimental, rarely used
- =L= pure-ish library / command helpers, easy to load directly
Load shape: =eager= | =hook= | =mode= | =command= | =after-load=.
Direct test load: =yes= | =conditional= | =no= — can the module be loaded
directly in batch without first loading the rest of init?
* Classified modules
** Batch 1 — Foundation (Layer 1)
| Module | Layer | Cat | Current | Target | Runtime requires | Top-level side effects | Direct load |
|--------+-------+-----+---------+--------+------------------+------------------------+-------------|
| =system-lib= | 1 | F/L | eager | eager | none (auth-source on demand) | none | yes |
| =user-constants= | 1 | F | eager | eager | none | file writes: creates configured dirs/files at load | conditional |
| =host-environment= | 1 | F/L | eager | eager | none (battery on demand) | none | yes |
| =system-defaults= | 1 | F/S | eager | eager | autorevert, server, bookmark, host-environment, user-constants | global setq, display-warning advice, display-buffer-alist entry, one key remap | conditional |
| =keyboard-compat= | 1 | F/S | eager | eager | host-environment | adds emacs-startup-hook | yes |
| =keybindings= | 1 | F/C | eager | eager | user-constants, which-key, free-keys | defines cj/custom-keymap + cj/jump-map, binds global keys, which-key labels | conditional |
| =config-utilities= | 1 | C/O | eager | eager | cl-lib, cl-generic, eieio, find-lisp, profiler | defines cj/debug-config-keymap, binds C-c d | yes |
Every Batch 1 module stays eager: each satisfies a spec eager-reason condition
(shared helpers, path constants, core Emacs behavior, or the global keymap
owner). Reasons are recorded in each module's load-graph header.
** Batch 2 — Text/editing command modules (Layer 2)
The =custom-*= text-command helpers. Each is eager only to register a =C-;=
submap (or direct bindings) at load; none has a necessary eager reason, so all
are deferral candidates for Phase 3 (registration API) and Phase 4
(command/autoload). Target load shape is command-driven.
| Module | Layer | Cat | Current | Target | Runtime requires | Top-level side effects | Direct load |
|--------+-------+-----+---------+--------+------------------+------------------------+-------------|
| =custom-case= | 2 | L/C | eager | command | keybindings | cj/case-map under C-; c; remaps capitalize-region | yes |
| =custom-comments= | 2 | L/C | eager | command | keybindings | cj/comment-map under C-; C | yes |
| =custom-datetime= | 2 | L/C | eager | command | keybindings | cj/datetime-map under C-; d | yes |
| =custom-buffer-file= | 2 | L/C | eager | command | external-open, mm-decode, system-lib | cj/copy-buffer-content-map, cj/buffer-and-file-map; C-; b registration boundp-guarded | conditional |
| =custom-line-paragraph= | 2 | L/C | eager | command | keybindings (expand-region on demand) | cj/line-and-paragraph-map under C-; l | yes |
| =custom-misc= | 2 | L/C | eager | command | keybindings | align-regexp advice; direct C-; bindings | yes |
| =custom-ordering= | 2 | L/C | eager | command | cl-lib, keybindings (org-config on demand) | cj/ordering-map under C-; o | yes |
| =custom-text-enclose= | 2 | L/C | eager | command | keybindings (change-inner on demand) | cj/enclose-map under C-; s | yes |
| =custom-whitespace= | 2 | L/C | eager | command | keybindings | cj/whitespace-map under C-; w | yes |
** Batch 3 — Core libraries and command modules
The remainder of init.el's early/core block: shared command libraries and a few
eager side-effect owners.
| Module | Layer | Cat | Current | Target | Runtime requires | Top-level side effects | Direct load |
|--------+-------+-----+---------+--------+------------------+------------------------+-------------|
| =external-open= | 2 | L/D | eager | command | host-environment, system-lib, external-open-lib, cl-lib | none | yes |
| =media-utils= | 3 | D/L | eager | command | system-lib | none | yes |
| =auth-config= | 1 | F/D | eager | eager | system-lib, user-constants | auth-source/epa config | yes |
| =keyboard-macros= | 2 | C/L | eager | command | subr-x, user-constants | none | yes |
| =system-utils= | 2 | L/C/S | eager | eager | system-lib, external-open-lib | 3 global keys, 1 startup hook | yes |
| =text-config= | 2 | C/P | eager | eager | none | 3 add-hook, package config | yes |
| =undead-buffers= | 2 | C | eager | eager | none | 3 global keys (kill-buffer remap) | yes |
** Batch 4 — UI / core-UX modules (Layer 2)
Modules that shape the first interactive frame: theme, font, modeline, window
navigation, completion stack, and landing page. Most have a real first-frame
eager reason and stay eager.
| Module | Layer | Cat | Current | Target | Runtime requires | Top-level side effects | Direct load |
|--------+-------+-----+---------+--------+------------------+------------------------+-------------|
| =ui-config= | 2 | C/S | eager | eager | user-constants | UI defaults, post-command hook, display-buffer-alist | yes |
| =ui-theme= | 2 | C | eager | eager | none | theme load path, theme key | yes |
| =ui-navigation= | 2 | C/P | eager | eager | none | nav keymap, 5 global keys, package config | yes |
| =font-config= | 2 | C/P/S | eager | eager | host-environment, keybindings | 5 global font keys, font-install checks | yes |
| =selection-framework= | 2 | C/P | eager | eager | none | 1 global key, 15 use-package forms | yes |
| =modeline-config= | 2 | C/S | eager | eager | user-constants | 2 add-hook (VC cache) | yes |
| =mousetrap-mode= | 2 | C | eager | eager | cl-lib | 3 add-hook, 1 add-to-list, 1 global key | yes |
| =popper-config= | 2 | C/P | eager | eager | none | package config (enabled-state open question) | yes |
| =dashboard-config= | 2 | C/S | eager | eager | none | builds/opens dashboard buffer at startup | conditional |
| =nerd-icons-config= | 2 | C/P | eager | eager | none | package config | yes |
** Batch 5 — Dev entry-points, diff, help, lint, VC (Layer 2)
Command entry points and package config for the development workflow: coverage
(F7), F-keys (F4/F6), diff, help, lint, the ERT runner, and Magit.
| Module | Layer | Cat | Current | Target | Runtime requires | Top-level side effects | Direct load |
|--------+-------+-----+---------+--------+------------------+------------------------+-------------|
| =coverage-core= | 2 | C/L | eager | eager | seq, subr-x, system-lib | 1 global key (F7) | yes |
| =coverage-elisp= | 2 | C/P | eager | eager | coverage-core | backend registration | yes |
| =dev-fkeys= | 2 | C | eager | eager | cl-lib, system-lib | 6 F-key bindings; C-; P boundp-guarded | conditional |
| =diff-config= | 2 | C/P | eager | eager | none | package config | yes |
| =help-config= | 2 | C/P | eager | eager | none | 2 global keys, package config | yes |
| =help-utils= | 2 | L/D | eager | command | none | 1 global key, package config | yes |
| =flycheck-config= | 2 | C/P | eager | hook | none (needs keybindings for :map) | package config; cj/custom-keymap :map binding | conditional |
| =test-runner= | 2 | C/L | eager | eager | ert, cl-lib, keybindings | test keymap under cj/custom-keymap | yes |
| =vc-config= | 2 | C/P | eager | eager | user-constants, keybindings | 2 keymaps under cj/custom-keymap, package config | yes |
** Batch 6 — Programming modules (Layer 2-4)
prog-general owns the shared programming defaults and tree-sitter/LSP policy and
stays eager. The language modules are eager only by init order and should load
by major mode (Phase 6); prog-training is optional and already autoloads.
| Module | Layer | Cat | Current | Target | Runtime requires | Top-level side effects | Direct load |
|--------+-------+-----+---------+--------+------------------+------------------------+-------------|
| =prog-general= | 2 | C/P/S | eager | eager | none | 4 add-hook, package config | yes |
| =prog-c= | 3 | D/P | eager | mode | none | 6 add-hook, package config | yes |
| =prog-go= | 3 | D/P | eager | mode | none | package config | yes |
| =prog-lisp= | 3 | D/P | eager | mode | none | 2 add-hook, package config | yes |
| =prog-python= | 3 | D/P | eager | mode | system-lib | package config; warns if pyright missing | yes |
| =prog-webdev= | 3 | D/P | eager | mode | system-lib | package config; warns if prettier missing | yes |
| =prog-json= | 3 | D/P | eager | mode | none | 1 add-hook, package config | yes |
| =prog-yaml= | 3 | D/P | eager | mode | none | 1 add-hook, package config | yes |
| =prog-shell= | 3 | D/P/S | eager | mode | none | 5 add-hook (after-save executable hook flagged) | yes |
| =prog-training= | 4 | O/D/P | eager | command | none | package config (autoloaded) | yes |
** Batch 7 — Org modules (Layer 3-4)
The daily Org workflows (config, agenda, capture, refile, roam) stay eager per
the spec's Phase 6 target. Babel and contacts move to after-load; export,
reveal, drill, noter, webclipper, and hugo become command-loaded. The agenda
and refile idle-timer caches are the side effects the spec tracks separately.
| Module | Layer | Cat | Current | Target | Runtime requires | Top-level side effects | Direct load |
|--------+-------+-----+---------+--------+------------------+------------------------+-------------|
| =org-config= | 3 | C/D/P | eager | eager | keybindings | org-protocol, cj/custom-keymap binding, package config | yes |
| =org-agenda-config= | 3 | D/S | eager | eager | user-constants, system-lib, cj-cache-lib | add-hook, idle-timer agenda cache | yes |
| =org-babel-config= | 3 | D/P | eager | after-load | none | 1 global key, package config | yes |
| =org-capture-config= | 3 | D/P | eager | eager | none | capture templates, org-protocol handlers | yes |
| =org-contacts-config= | 3 | D/P | eager | after-load | user-constants | 1 global key, package config | yes |
| =org-drill-config= | 4 | O/D/P | eager | command | user-constants, keybindings | drill keymap under cj/custom-keymap | yes |
| =org-export-config= | 3 | D/P | eager | command | system-lib | package config | yes |
| =org-noter-config= | 4 | O/D/P | eager | command | cl-lib, user-constants, keybindings | add-hook, keymap under cj/custom-keymap | yes |
| =org-refile-config= | 3 | D/S | eager | eager | system-lib, cj-cache-lib | idle-timer refile cache | yes |
| =org-reveal-config= | 4 | O/D/P | eager | command | none | package config | yes |
| =org-roam-config= | 3 | D/P/S | eager | eager | user-constants | 1 global key, roam db setup, package config | yes |
| =org-webclipper= | 4 | O/D/P | eager | command | none | org-protocol handler | yes |
| =hugo-config= | 3 | D/P | eager | command | user-constants, host-environment | package config | yes |
** Batch 8 — Domain / integration / optional modules (Layer 2-4)
AI, browser, calendar, ebook, file-manager, shell, IRC, web, games, and other
domain workflows. Almost all are eager only by init order and become command-,
hook-, or mode-loaded. calendar-sync stays eager when its .local.el is present.
flyspell-and-abbrev is the one Core-UX member (text-mode hooks).
| Module | Layer | Cat | Current | Target | Runtime requires | Top-level side effects | Direct load |
|--------+-------+-----+---------+--------+------------------+------------------------+-------------|
| =ai-config= | 3 | D/P | eager | command | keybindings, system-lib | cj/ai-keymap under cj/custom-keymap | yes |
| =ai-vterm= | 3 | D | eager | command | cl-lib, seq, cj-window-geometry-lib, cj-window-toggle-lib, host-environment | 4 global keys | yes |
| =browser-config= | 3 | D/P | eager | command | cl-lib | 1 global key | yes |
| =calendar-sync= | 3 | D/S | eager (.local.el) | eager (.local.el) | cl-lib, subr-x, system-lib, cj-org-text-lib | calendar keymap (boundp-guarded), guarded timer/network | conditional |
| =calibredb-epub-config= | 4 | O/D/P | eager | command | user-constants, subr-x | add-hook, advice-add, package config | yes |
| =chrono-tools= | 3 | D/P | eager | command | user-constants | package config | yes |
| =dirvish-config= | 3 | D/P | eager | command | user-constants, system-utils, host-environment, system-lib, external-open-lib | 3 add-hook, package config | yes |
| =dwim-shell-config= | 3 | D/P | eager | command | cl-lib | package config | yes |
| =erc-config= | 4 | O/D/P | eager | command | cl-lib, keybindings | ERC keymap under cj/custom-keymap | yes |
| =eshell-config= | 3 | D/P | eager | command | system-utils | add-hook, advice-add, package config | yes |
| =eww-config= | 3 | D/P | eager | command | cl-lib | package config | yes |
| =flyspell-and-abbrev= | 2 | C/P | eager | hook | cl-lib | mode-hook package config | yes |
| =games-config= | 4 | O | eager | command | none | package config | yes |
| =gloss-config= | 4 | O/D/P | eager | command | none | package config | yes |
| =httpd-config= | 4 | O/D/P | eager | command | none | package config | yes |
| =jumper= | 4 | O/L | eager | command | cl-lib | jumper keymap | yes |
| =latex-config= | 3 | D/P | eager | mode | none | package config | yes |
* Hidden dependencies found
Discoveries that belong to Phase 2 (make dependencies explicit). Recorded here,
not fixed here.
- =system-defaults= reads =env-bsd-p= (host-environment) and =user-home-dir=
(user-constants) at *load* time, but declares both only via
=eval-when-compile=. The compiled module therefore cannot load standalone —
it works at startup only because =init.el= requires host-environment and
user-constants earlier. Phase 2 fix: promote the two =eval-when-compile=
requires to plain runtime =require=. (Test
=test-system-defaults-functions.el= was updated to require host-environment
so the unit loads in isolation; the production fix is still pending.)
- =custom-buffer-file= registers its =C-; b= submap behind =(when (boundp
'cj/custom-keymap) ...)= and declares =cj/custom-keymap= only via
=eval-when-compile=. Loaded standalone without keybindings, the =boundp= guard
is nil and the binding silently drops. That guard is the
"define-if-not-present" shim the spec warns against. Phase 2/3 fix: require
keybindings (or use the registration API once it exists) and drop the guard.
- =dev-fkeys= uses the same =(when (boundp 'cj/custom-keymap) ...)= shim plus an
=eval-when-compile= declaration for its =C-; P= binding, so the binding
silently drops when loaded without keybindings. Same Phase 2/3 fix.
- =flycheck-config= binds =(:map cj/custom-keymap ...)= through use-package but
does not require keybindings, so the binding fails when the module loads
standalone. Phase 2 fix: require keybindings (or move to the registration API).
- =calendar-sync= guards its =C-; g= registration with =(when (boundp
'cj/custom-keymap) ...)= and does not require keybindings, so the binding
silently drops standalone. Same boundp-shim pattern. Phase 2/3 fix.
* Deferred classification
- =elfeed-config= is read and understood (Layer 4, O/D/P, command-loaded; runtime
requires user-constants, system-lib, media-utils) but its header is not yet
annotated. Annotating triggers the PostToolUse byte-compile, and its tests
(=tests/test-elfeed-config-helpers.el=) only pass when elfeed-config loads as
interpreted source: the byte-compiled =cj/elfeed-process-entries= inlines the
=elfeed-entry-link= struct accessor, so the tests' function stubs are bypassed
and the inlined accessor type-checks a real =elfeed-entry=. The batch test
environment has no elfeed package on the load-path, so the tests cannot build
real structs either. Classifying elfeed-config needs that test rewritten first
(define a stand-in =elfeed-entry= struct, or make elfeed loadable in batch).
Tracked as a todo task.
* Pending classification
The remaining 20 modules required by =init.el=, awaiting per-module inspection.
Each batch reads these against their source and moves them into [[*Classified
modules][Classified modules]] with a load-graph header. Suggested batch order
follows the spec: text/editing command modules, then UI, then programming, then
Org, then optional integrations.
- [ ] elfeed-config
- [ ] linear-config
- [ ] local-repository
- [ ] lorem-optimum
- [ ] mail-config
- [ ] markdown-config
- [ ] music-config
- [ ] pdf-config
- [ ] quick-video-capture
- [ ] reconcile-open-repos
- [ ] restclient-config
- [ ] slack-config
- [ ] system-commands
- [ ] telega-config
- [ ] tramp-config
- [ ] transcription-config
- [ ] video-audio-recording
- [ ] vterm-config
- [ ] weather-config
- [ ] wrap-up
|