<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dotemacs/modules/calendar-sync.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:57:56+00:00</updated>
<entry>
<title>docs(load-graph): classify domain, integration, and optional modules</title>
<updated>2026-05-24T21:57:56+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T21:57:56+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=cad351ec00c3f78cfb6e203d87c7309a620e485c'/>
<id>urn:sha1:cad351ec00c3f78cfb6e203d87c7309a620e485c</id>
<content type='text'>
Eighth classification batch: 17 domain/integration/optional modules — ai-config, ai-vterm, browser-config, calendar-sync, calibredb-epub-config, chrono-tools, dirvish-config, dwim-shell-config, erc-config, eshell-config, eww-config, flyspell-and-abbrev, games-config, gloss-config, httpd-config, jumper, latex-config. I annotated each header, added a Batch 8 table to the inventory, and extended the validation allowlist. 82 of 102 modules are now classified.

Almost all are eager only by init order and become command/hook/mode-loaded. calendar-sync stays eager when its .local.el is present. One new hidden dependency: calendar-sync guards its C-; g registration with a boundp shim and doesn't require keybindings, so the binding drops standalone.

I deferred elfeed-config rather than annotate it. Its header edit triggers byte-compilation, and the existing tests only pass when the module loads as interpreted source — the compiled cj/elfeed-process-entries inlines an elfeed struct accessor the stubs can't intercept, and the batch test environment has no elfeed package to build real structs. It needs its tests rewritten first, recorded in the inventory and a new todo task.

Also made the header allowlist scoping test durable: it used games-config (now classified) as its unclassified example; switched to a sentinel name plus a duplicate-entry guard.
</content>
</entry>
<entry>
<title>refactor(auth): consolidate the auth-source secret lookup into one helper</title>
<updated>2026-05-23T00:32:32+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-23T00:32:32+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=f6e5885b47e3ab244b293f4e478af7e520180710'/>
<id>urn:sha1:f6e5885b47e3ab244b293f4e478af7e520180710</id>
<content type='text'>
The auth-source-search + funcall-the-secret block was copied four times: calendar-sync--calendar-url, cj/auth-source-secret (ai-config), cj/--auth-source-password (transcription), and cj/slack--get-credential. Each searched authinfo, pulled :secret, and called it when the netrc backend returned a function.

I pulled that into cj/auth-source-secret-value in system-lib (a leaf, so calendar-sync doesn't have to depend on ai-config and drag in the gptel stack). It takes an optional user and returns the secret or nil. The four callers now delegate to it: ai-config layers its required-secret error on top, and the others keep their nil-on-miss behavior. With the direct auth-source-search calls gone, I dropped the now-unused (require 'auth-source) from transcription, slack, and calendar-sync. The helper's autoload covers it.

The transcription tests that exercise the delegated path stay green, and the primitive and the error wrapper get their own tests.
</content>
</entry>
<entry>
<title>feat(calendar-sync): resolve .ics feed URLs from auth-source</title>
<updated>2026-05-21T19:38:56+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-21T19:38:56+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=e78595096c1cb956602796c6b4b692e58458ff99'/>
<id>urn:sha1:e78595096c1cb956602796c6b4b692e58458ff99</id>
<content type='text'>
A calendar's .ics feed URL is a secret token, so I'd rather not keep it in a plaintext config file. A calendar can now name a :secret-host, and calendar-sync--calendar-url looks the URL up in auth-source (~/.authinfo.gpg) at sync time. Inline :url still works and wins when both are set, so existing configs are unaffected.

I added 7 tests covering the explicit-url, string-secret, function-secret, precedence, and no-match paths, and switched the .example template to the :secret-host shape.
</content>
</entry>
<entry>
<title>feat(calendar-sync): dispatch Google calendars through API helper</title>
<updated>2026-05-20T15:58:40+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-20T15:58:40+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=4a6201dd0117df55d164cee969f7c3c8123f6b28'/>
<id>urn:sha1:4a6201dd0117df55d164cee969f7c3c8123f6b28</id>
<content type='text'>
The Python helper from d6a995b could fetch and render on its own, but nothing in Emacs called it. This wires it in. Each entry in calendar-sync-calendars now takes a :fetcher key. 'api routes through the helper, and the default 'ics keeps the existing curl + Elisp parser path. Proton and any plain .ics feed work unchanged because the key defaults to 'ics.

The 'api path reads :account and :calendar-id off the calendar plist, builds the helper command (honoring the past/future window and the calendar-sync-skip-declined toggle), and runs it through make-process. The script writes the org file directly, so the sentinel only handles state bookkeeping and failure reporting, the same as the .ics worker.

I split the old --sync-calendar body into --sync-calendar-ics and turned --sync-calendar into a dispatcher. The command builder and script-path resolution are pure functions, tested directly. The dispatch routing is tested with both leaf syncers stubbed, so no process runs. I added 14 tests across the two new files, and the full suite is green.

Running the 'api path still needs the one-time OAuth bootstrap from docs/calendar-sync-api-setup.org.
</content>
</entry>
<entry>
<title>fix(calendar-sync): drop declined events from synced output</title>
<updated>2026-05-19T22:33:15+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-19T22:33:15+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=8911d161f7ef38f8a1b03fba6316b71da1011174'/>
<id>urn:sha1:8911d161f7ef38f8a1b03fba6316b71da1011174</id>
<content type='text'>
The sync parsed PARTSTAT into a :STATUS: declined property but kept
the event. Meetings I'd declined still landed in dcal.org / gcal.org
and showed on the agenda. I added a pure --filter-declined helper
called inside --parse-ics after event collection, plus the
calendar-sync-skip-declined defvar (default t) so it can be flipped
off without code changes.

The .ics feed and the Calendar API can disagree on PARTSTAT. OOO
auto-declines sometimes only write API-side, so a few declined
events may still slip through. I'm calling this out because the
filter looks absolute from the agenda but isn't.

Tests cover Normal/Boundary/Error (11 cases). Full suite is green.
</content>
</entry>
<entry>
<title>refactor: consolidate runtime state into persist/</title>
<updated>2026-05-16T17:20:44+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-16T17:20:44+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=43022b56569717f28fa16284f7092f2bbe0830ad'/>
<id>urn:sha1:43022b56569717f28fa16284f7092f2bbe0830ad</id>
<content type='text'>
Six previously-scattered runtime state files now live under persist/
in user-emacs-directory:

- theme-file (was .emacs-theme)
- pdf-view-restore-filename (was .pdf-view-restore)
- time-zones--city-list-file (was .time-zones.el)
- calendar-sync--state-file (was data/calendar-sync-state.el)
- prescient-save-file (was var/prescient-save.el)
- org-id-locations-file (was .org-id-locations)

The defaults in each module now expand to persist/&lt;name&gt; instead of
the user-emacs-directory root or ad-hoc subdirs.  Existing files
moved into persist/ alongside this change so the next launch picks
up the state without regenerating.

test-ui-theme-default-theme-file-is-emacs-dotfile renamed to
test-ui-theme-default-theme-file-is-under-persist and updated to
assert the new default path.

lsp-session-file is left at the root for now -- prog-lsp.el has no
(require) reference anywhere, so the use-package block that would
carry the redirect never runs.  Tier 3 follow-up: confirm the module
is dead, then delete it or wire it into the load chain.

The var/ directory is now empty and removed.  data/ retains the
calendar agenda content (dcal/gcal/pcal.org) and the .rest API
examples -- content, not state, stays where it is.
</content>
</entry>
<entry>
<title>fix(calendar-sync): give the no-init .ics worker its module load-path</title>
<updated>2026-05-11T22:17:53+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-11T22:17:53+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=e958410cbc14d2bfa0f97890aafe38031e082aa7'/>
<id>urn:sha1:e958410cbc14d2bfa0f97890aafe38031e082aa7</id>
<content type='text'>
The async .ics-to-Org worker runs `emacs --batch --no-site-file --no-site-lisp' and loads `calendar-sync.el' by absolute path, but that doesn't make its sibling `(require 'cj-org-text-lib)' resolvable, so the conversion died with "Cannot open load file: cj-org-text-lib". `calendar-sync--worker-command' now inserts `-L &lt;module-dir&gt;' before `-l calendar-sync.el', which keeps the worker isolated from `init.el' while letting it load its local module deps. Updated the worker-command test and added a regression test that runs the real no-init worker shape.
</content>
</entry>
<entry>
<title>refactor(cj-org-text): rename to cj-org-text-lib for naming consistency</title>
<updated>2026-05-10T20:25:27+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-10T20:25:27+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=0f9e308745b77ca589e5b25f415121058b9f08ee'/>
<id>urn:sha1:0f9e308745b77ca589e5b25f415121058b9f08ee</id>
<content type='text'>
Same naming-convention fix as the cj-cache rename.  Org-safe text
sanitizers extracted in Phase 3 went into modules/cj-org-text.el,
should have followed the established `-lib' suffix convention.

Rename modules/cj-org-text.el -&gt; modules/cj-org-text-lib.el; update
provide form, file header, and the two (require 'cj-org-text) call
sites in calendar-sync and test-cj-org-text-sanitize.  No behavior
change.
</content>
</entry>
<entry>
<title>refactor(cj-org-text): extract Org-safe text sanitizers from calendar-sync</title>
<updated>2026-05-10T19:34:52+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-10T19:34:52+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=c44a52a7905b605a6537e3ff9bb4fe3afede0485'/>
<id>urn:sha1:c44a52a7905b605a6537e3ff9bb4fe3afede0485</id>
<content type='text'>
Phase 3 of utility-consolidation. Three sanitizers moved from calendar-sync.el into a new cj-org-text.el module so other consumers (web-clipper, AI conversation, mail-to-org capture) can compose Org content from external text without depending on calendar:

- `calendar-sync--sanitize-org-body' -&gt; `cj/org-sanitize-body-text'
- `calendar-sync--sanitize-org-property-value' -&gt; `cj/org-sanitize-property-value'
- `calendar-sync--sanitize-org-heading' -&gt; `cj/org-sanitize-heading'

The helpers stay pure (string in, string out, nil-safe) and have no Org-mode dependency, so they work in batch and in tests without loading Org.

Migrate calendar-sync.el to use the new public names: drop the three local defuns, add `(require \='cj-org-text)', update the six call sites in `calendar-sync--make-event-entry'.

Move the existing 17-test file to `tests/test-cj-org-text-sanitize.el', rename test names to match the new helpers, add 1 nil-input test for `cj/org-sanitize-heading' that wasn't in the original file. Total: 18 Normal/Boundary tests across the three helpers.
</content>
</entry>
<entry>
<title>Keep calendar sync off the UI thread</title>
<updated>2026-05-10T08:09:37+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-10T08:09:37+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=f89b6f22409318ac3124138f7d230c829e6d73c5'/>
<id>urn:sha1:f89b6f22409318ac3124138f7d230c829e6d73c5</id>
<content type='text'>
Move calendar feed conversion into an isolated batch Emacs worker so large parse/write cycles do not freeze interactive editing. Cover the worker command, isolated logging, quoted settings, and sync success/failure paths with focused ERTs.
</content>
</entry>
</feed>
