aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/design/2026-07-10-org-module-ownership.org138
1 files changed, 138 insertions, 0 deletions
diff --git a/docs/design/2026-07-10-org-module-ownership.org b/docs/design/2026-07-10-org-module-ownership.org
new file mode 100644
index 00000000..5717b66f
--- /dev/null
+++ b/docs/design/2026-07-10-org-module-ownership.org
@@ -0,0 +1,138 @@
+#+TITLE: Org module ownership and load boundaries
+#+AUTHOR: Craig Jennings
+#+DATE: 2026-07-10
+
+* Why this note exists
+
+The Org workflow is spread across sixteen modules with overlapping
+responsibilities. The code works. What's missing is a map: which module owns which
+behavior, what each one adds to a shared variable, and in what order they load.
+
+Future load-order work needs that map. Four modules add capture templates, four
+touch =org-agenda-files=, and five register org-protocol handlers. None of that is
+wrong, but none of it is written down either, so the only way to answer "who sets
+this?" is to grep.
+
+This is a description of what is, not a proposal. Where the map turned up something
+that looks like a defect, it's flagged at the bottom rather than fixed here.
+
+* The modules
+
+Every Org module declares =Layer=, =Category=, and =Load shape= in its commentary
+header. The layer numbers below come from those headers, not from this note.
+
+| Module | Layer | Owns |
+|------------------------------+-------+---------------------------------------------------------|
+| =org-faces-config.el= | 2 | Agenda keyword and priority faces, plus their -dim |
+| | | variants for unfocused windows |
+|------------------------------+-------+---------------------------------------------------------|
+| =org-config.el= | 3 | Base org-mode settings, the org keymap, tag alignment |
+|------------------------------+-------+---------------------------------------------------------|
+| =org-agenda-config.el= | 3 | Agenda views, task tracking, notifications; loads |
+| | | =org-agenda-config-debug= on demand |
+|------------------------------+-------+---------------------------------------------------------|
+| =org-capture-config.el= | 3 | The base capture templates and their dispatch keys |
+|------------------------------+-------+---------------------------------------------------------|
+| =org-refile-config.el= | 3 | Refile targets and the cached target list |
+|------------------------------+-------+---------------------------------------------------------|
+| =org-roam-config.el= | 3 | Roam database, capture, and its agenda-file additions |
+|------------------------------+-------+---------------------------------------------------------|
+| =org-contacts-config.el= | 3 | Contacts storage and its capture template |
+|------------------------------+-------+---------------------------------------------------------|
+| =org-babel-config.el= | 3 | Source-block languages and tempo templates |
+|------------------------------+-------+---------------------------------------------------------|
+| =org-export-config.el= | 3 | Export backends and their defaults |
+|------------------------------+-------+---------------------------------------------------------|
+| =org-spec-links.el= | 3 | Resolves =[[id:]]= links into project spec docs |
+|------------------------------+-------+---------------------------------------------------------|
+| =hugo-config.el= | 3 | ox-hugo blog publishing (=C-; h=) |
+|------------------------------+-------+---------------------------------------------------------|
+| =org-drill-config.el= | 4 | Spaced repetition, its own refile targets |
+|------------------------------+-------+---------------------------------------------------------|
+| =org-noter-config.el= | 4 | PDF and EPUB annotation |
+|------------------------------+-------+---------------------------------------------------------|
+| =org-reveal-config.el= | 4 | reveal.js presentations (=C-; p=) |
+|------------------------------+-------+---------------------------------------------------------|
+| =org-webclipper.el= | 4 | org-protocol web clipping into roam |
+|------------------------------+-------+---------------------------------------------------------|
+| =quick-video-capture.el= | 4 | Video download via an org-protocol bookmark |
+|------------------------------+-------+---------------------------------------------------------|
+
+* Shared variables and who writes to them
+
+This is the part grep answers slowly and a table answers fast. Each of these
+variables is appended to by more than one module.
+
+** =org-capture-templates= — four writers
+
+=org-capture-config.el= establishes the base set. =org-contacts-config.el=,
+=org-webclipper.el=, and =quick-video-capture.el= each append their own. Dispatch
+keys must not collide across all four, which is why
+=tests/test-org-capture-templates-integrity.el= exists: it loads the cleanly
+loadable capture modules, applies their additions, and asserts no two templates
+share a key.
+
+That test is the enforcement mechanism for a boundary no single module can see.
+
+** =org-agenda-files= — four writers
+
+=org-agenda-config.el= sets the base list. =org-roam-config.el= adds roam files.
+=config-utilities.el= manipulates it, and =org-agenda-config-debug.el= reads it for
+diagnostics.
+
+** org-protocol handlers — five registrars
+
+=org-config.el=, =org-capture-config.el=, =org-drill-config.el=,
+=org-webclipper.el=, and =quick-video-capture.el= all touch org-protocol. The
+handlers are keyed by protocol name, so collisions are possible in principle and
+nothing checks for them today.
+
+** =org-refile-targets= — two writers
+
+=org-refile-config.el= owns the general targets. =org-drill-config.el= adds its
+own, because drill files are refile destinations but not agenda files.
+
+* Load order
+
+=init.el= loads the Org stack at lines 130 through 145, in this order: base config,
+faces, agenda, babel, capture, contacts, drill, export, hugo, reveal, refile, roam,
+spec-links, webclipper, noter.
+
+The order is deliberate in two places. =org-config= precedes everything because it
+establishes the keymap the rest bind into. =org-faces-config= precedes
+=org-agenda-config= because the agenda renders with those faces.
+
+One module sits outside that block. =quick-video-capture.el= loads at line 106,
+twenty-four lines before =org-config=, because it is grouped with the media modules
+rather than the Org modules. It nonetheless appends to =org-capture-templates=. It
+works because the append is a plain =add-to-list= against a variable org-mode has
+not yet claimed, but the grouping means a reader looking for "who adds capture
+templates" in the Org block will not find it.
+
+* Load shape
+
+Every Org module declares =Load shape: eager=. Nothing in the Org stack defers.
+
+This is the single largest fact about the Org workflow's startup cost, and it is
+worth stating plainly rather than leaving implicit across sixteen headers. Layer-4
+modules (drill, noter, reveal, webclipper, video capture) are optional features by
+their own classification, and all five load eagerly on every launch.
+
+Whether that matters is a measurement question, not an argument, and this note does
+not make it. It records the fact so the question can be asked.
+
+* Observations for follow-up
+
+These turned up while mapping. None is fixed here.
+
+1. =modules/org-config.el.faces.bak= is tracked in git (25KB). It appears to be a
+ leftover from the 2026-06-14 face-stripping work. A =.bak= file in =modules/=
+ is not loaded, but it is on the load-path's directory and will confuse the next
+ reader.
+2. =org-agenda-config-debug.el= carries no =Layer= / =Category= / =Load shape=
+ header, unlike every other module. It loads conditionally from
+ =org-agenda-config.el:42= when =cj/debug-modules= names it, so its absence from
+ =init.el= is by design, not an orphan.
+3. Nothing checks org-protocol handler collisions the way the capture-template test
+ checks key collisions. Five modules register handlers.
+4. Every Layer-4 Org module loads eagerly despite being classified optional.