<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dotemacs/modules/org-roam-config.el, branch load-graph-classify-end</title>
<subtitle>My Emacs configuration
</subtitle>
<id>https://git.cjennings.net/dotemacs/atom?h=load-graph-classify-end</id>
<link rel='self' href='https://git.cjennings.net/dotemacs/atom?h=load-graph-classify-end'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/'/>
<updated>2026-05-24T21:34:16+00:00</updated>
<entry>
<title>docs(load-graph): classify Org modules</title>
<updated>2026-05-24T21:34:16+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T21:34:16+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=02baa68063f02cc571789c03b1101c28d139200d'/>
<id>urn:sha1:02baa68063f02cc571789c03b1101c28d139200d</id>
<content type='text'>
Seventh classification batch: the thirteen Org modules — config, agenda, babel, capture, contacts, drill, export, noter, refile, reveal, roam, webclipper, hugo. I annotated each header, added a Batch 7 table to the inventory, and extended the validation allowlist. 65 of 102 modules are now classified.

The daily workflows (config, agenda, capture, refile, roam) keep their eager reason 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 recorded as the side effects the spec already tracks for cache-lifecycle work. No new hidden dependencies.
</content>
</entry>
<entry>
<title>fix(org-roam): guard move-branch-to-roam against data loss</title>
<updated>2026-05-24T19:33:21+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T19:33:21+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=5c0fa15d1d5bbe48f85f21e98bb6e91e9b9bcd85'/>
<id>urn:sha1:5c0fa15d1d5bbe48f85f21e98bb6e91e9b9bcd85</id>
<content type='text'>
cj/move-org-branch-to-roam cut the subtree from the source buffer before writing the new roam file, so a failure in the demote/format/write/db-sync steps left the subtree gone from the source and not persisted anywhere — a destructive operation with no rollback.

Reordered so the node file is written and verified on disk before org-cut-subtree runs; a failed write now aborts with the source untouched. Added a no-clobber guard (refuse an existing target file) and a confirmation prompt for large subtrees (&gt;= cj/move-org-branch-confirm-lines, 30) or buffers with unsaved changes. The source buffer is deliberately left modified and undoable rather than auto-saved, so the move stays reversible. New test drives the write-failure-preserves-source invariant via an unwritable roam dir; the existing creates-roam-file test gained the confirm mock.
</content>
</entry>
<entry>
<title>refactor(org): give org-log-done a single home</title>
<updated>2026-05-23T01:20:25+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-23T01:20:25+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=5f8e1bc757fab620f5cf9b5fbc42b6a0030f5e53'/>
<id>urn:sha1:5f8e1bc757fab620f5cf9b5fbc42b6a0030f5e53</id>
<content type='text'>
`org-log-done` was set in two places: `cj/org-todo-settings` in org-config.el set it nil, and org-roam-config.el's `:config` set it to 'time. Whichever module loaded last won, so the effective value was load-order-dependent and fragile.

I set it once in `cj/org-todo-settings` and dropped the org-roam-config setter, leaving a comment at the old site so it doesn't creep back. The value is 'time rather than nil because the dated-completion workflow wants a CLOSED timestamp stamped on every TODO-&gt;DONE.
</content>
</entry>
<entry>
<title>fix(org-roam): always save the daily after a journal task-copy</title>
<updated>2026-05-23T00:43:00+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-23T00:43:00+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=f07ce74d835dfc81e1e529240526829464ad8b21'/>
<id>urn:sha1:f07ce74d835dfc81e1e529240526829464ad8b21</id>
<content type='text'>
The save lived inside the `unless` branch that only ran when the completed task needed an `org-refile` into a different file. When the task was already in today's daily, the copy left the buffer modified but unsaved. A crash before the next manual save lost it, and shutdown prompted about the unsaved journal buffer.

I pulled the save out of the refile branch into a `cj/--org-roam-save-daily` helper that runs on both paths and only writes when the buffer is modified. Extracting it also makes the save logic testable without driving the org-roam capture machinery.
</content>
</entry>
<entry>
<title>refactor(org-workflow): four hygiene fixes from the module-by-module re-review</title>
<updated>2026-05-16T08:45:29+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-16T08:45:29+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=a005c7636f30b710e27a6812ca989506d5df7531'/>
<id>urn:sha1:a005c7636f30b710e27a6812ca989506d5df7531</id>
<content type='text'>
- org-roam-config.el: extract `cj/--org-roam-should-copy-completed-task-p'
  and gate the `org-after-todo-state-change-hook' on it.  Skips
  fileless buffers (org-capture, indirect, temp Org) where
  `buffer-file-name' is nil and the downstream copy used to crash.
  Same gcal.org skip preserved.  Five existing tests updated to
  bind `buffer-file-name' inside `run-hooks' so the positive-case
  hook still fires.

- org-webclipper.el: drop the redundant
  `org-protocol-protocol-alist' registration inside
  `cj/webclipper-ensure-initialized'.  The
  `with-eval-after-load 'org-protocol' block at the bottom of the
  module is the single registration site now; comment in the
  initializer explains why.  Split the matching test into two:
  one for template registration (the initializer's actual job) and
  one for protocol registration (which now fires from the
  after-load block when `org-protocol' provides).

- org-webclipper.el: validate `:url' and `:title' in
  `cj/org-protocol-webclip'.  `:url' must be a non-empty string;
  `:title' must be a string when provided.  Signals `user-error'
  with the unexpected value instead of silently setting the
  globals to nil and failing downstream in the capture handler.

- mu4e-org-contacts-integration.el: declare `contacts-file' (via
  `eval-when-compile (defvar ...)') and `cj/get-all-contact-emails'
  (via `declare-function') near the top of the file.  Byte-compile
  in isolation no longer warns about free variables / unknown
  functions; the cross-module dependency is explicit at the top.
</content>
</entry>
<entry>
<title>refactor(org-roam-config): indirect node-tags accessor for testability</title>
<updated>2026-05-15T07:32:04+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-15T07:32:04+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=2f06d6e98f3b2d8ae9f8506262f59f0249770e34'/>
<id>urn:sha1:2f06d6e98f3b2d8ae9f8506262f59f0249770e34</id>
<content type='text'>
`cj/org-roam-filter-by-tag' called `org-roam-node-tags' directly.
That accessor is generated by `cl-defstruct' and ships with a
compiler-macro that inlines the call to an `aref' against the
`cl-struct-org-roam-node-tags' tag variable at byte-compile time.

In tests, `cl-letf' on `(symbol-function 'org-roam-node-tags)' sets
the function cell but the byte-compiled call site never consults it
-- it executes the inlined `aref' instead.  When org-roam isn't
loaded (legitimate for a tag-filter unit test), the inlined code
fails with `void-variable cl-struct-org-roam-node-tags'.

Wrap the accessor in `cj/--org-roam-node-tags' that calls through
`funcall' with a quoted symbol.  Quoted symbols skip the
compiler-macro (which only fires on direct call forms), so the
funcall resolves the function cell at runtime and picks up the
test's `cl-letf' stub.  Production behavior is unchanged; tests
no longer need org-roam loaded.
</content>
</entry>
<entry>
<title>fix(org-roam-config): save journal buffer after copying DONE task</title>
<updated>2026-05-14T12:18:11+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-14T12:18:11+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=266a660cb8db190e0418c5f7062af95e1a31270f'/>
<id>urn:sha1:266a660cb8db190e0418c5f7062af95e1a31270f</id>
<content type='text'>
`cj/org-roam-copy-todo-to-today' tried to save the target journal
buffer via `org-after-refile-insert-hook' bound to `#'save-buffer',
but that value is the wrong shape (single function instead of a
hook list), and the only other save mechanism -- the `:after' advice
on `org-refile' that calls `org-save-all-org-buffers' -- doesn't
attach until `:defer .5' elapses, so the very first DONE transition
after startup leaves the journal unsaved.

Drop the broken hook binding and save the target buffer explicitly
after the refile call. New ERT test asserts `buffer-modified-p' on
the journal buffer is nil after the function returns.
</content>
</entry>
<entry>
<title>fix(org-roam): bind dailies keys after keymap exists</title>
<updated>2026-01-24T18:25:41+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-01-24T18:25:41+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=60d527bb4fb2961deb6620a34027a9d6a9a2d0a0'/>
<id>urn:sha1:60d527bb4fb2961deb6620a34027a9d6a9a2d0a0</id>
<content type='text'>
</content>
</entry>
<entry>
<title>feat(calendar-sync): Add automatic timezone detection and chronological sorting</title>
<updated>2025-11-17T00:09:17+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2025-11-17T00:09:17+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=d842e08de5600c90c2f9de93ca81aea32be9775a'/>
<id>urn:sha1:d842e08de5600c90c2f9de93ca81aea32be9775a</id>
<content type='text'>
Implemented calendar-sync.el as a complete replacement for org-gcal, featuring:

**Core Functionality:**
- One-way sync from Google Calendar to Org (via .ics URL)
- UTC to local timezone conversion for all event timestamps
- Chronological event sorting (past → present → future)
- Non-blocking sync using curl (works reliably in daemon mode)

**Automatic Timezone Detection:**
- Detects timezone changes when traveling between timezones
- Tracks timezone offset in seconds (-21600 for CST, -28800 for PST, etc.)
- Triggers automatic re-sync when timezone changes detected
- Shows informative messages: "Timezone change detected (UTC-6 → UTC-8)"

**State Persistence:**
- Saves sync state to ~/.emacs.d/data/calendar-sync-state.el
- Persists timezone and last sync time across Emacs sessions
- Enables detection even after closing Emacs before traveling

**User Features:**
- Interactive commands: calendar-sync-now, calendar-sync-start/stop
- Keybindings: C-; g s (sync), C-; g a (start auto-sync), C-; g x (stop)
- Optional auto-sync every 15 minutes (disabled by default)
- Clear status messages for all operations

**Code Quality:**
- Comprehensive test coverage: 51 ERT tests (100% passing)
- Refactored UTC conversion into separate function
- Clean separation of concerns (parsing, conversion, formatting, sorting)
- Well-documented with timezone behavior guide and changelog

**Migration:**
- Removed org-gcal-config.el (archived in modules/archived/)
- Updated init.el to use calendar-sync
- Moved gcal.org to .emacs.d/data/ for machine-independent syncing
- Removed org-gcal appointment capture template

Files modified: modules/calendar-sync.el:442, tests/test-calendar-sync.el:577
Files created: data/calendar-sync-state.el, tests/testutil-calendar-sync.el
Documentation: docs/calendar-sync-timezones.md, docs/calendar-sync-changelog.md
</content>
</entry>
<entry>
<title>wip:org-roam: first pass at consult-org-roam, but disabled</title>
<updated>2025-11-01T18:11:02+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2025-11-01T18:11:02+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=e9213ddb9fdaafd1b75d0d71857b24c2a06697a8'/>
<id>urn:sha1:e9213ddb9fdaafd1b75d0d71857b24c2a06697a8</id>
<content type='text'>
</content>
</entry>
</feed>
