<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dotemacs/modules/calendar-sync.el, branch load-graph-classify-start</title>
<subtitle>My Emacs configuration
</subtitle>
<id>https://git.cjennings.net/dotemacs/atom?h=load-graph-classify-start</id>
<link rel='self' href='https://git.cjennings.net/dotemacs/atom?h=load-graph-classify-start'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/'/>
<updated>2026-05-23T00:32:32+00:00</updated>
<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>
<entry>
<title>Make calendar sync startup safe without config</title>
<updated>2026-05-04T05:00:08+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-04T05:00:00+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=cc594fdd28f2b047be25b6f016c7f47d23e741ec'/>
<id>urn:sha1:cc594fdd28f2b047be25b6f016c7f47d23e741ec</id>
<content type='text'>
</content>
</entry>
</feed>
