<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dotemacs/.gitignore, 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-20T00:46:23+00:00</updated>
<entry>
<title>feat(calendar-sync): add Python helper for Google Calendar API sync</title>
<updated>2026-05-20T00:46:23+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-20T00:46:23+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=d6a995b9090ca35190e59e765d5c14daf887e9d8'/>
<id>urn:sha1:d6a995b9090ca35190e59e765d5c14daf887e9d8</id>
<content type='text'>
Google's .ics export drops per-occurrence response statuses on recurring events. When OOO auto-declines a meeting, the master event keeps PARTSTAT=ACCEPTED and declined instances inherit it. The .ics path can't filter the declines out. The API path expands recurrences server-side via singleEvents=True, and each occurrence carries its own attendees[].self.responseStatus.

scripts/calendar_sync_api.py fetches events and renders them as org entries. OAuth is one-time per account. The refresh token lives at ~/.config/calendar-sync/token-&lt;account&gt;.json under 0600. Output matches the existing .ics shape: heading sanitization, LOCATION/ORGANIZER/STATUS/URL property drawer, HTML-stripped descriptions, org timestamps with weekday abbreviations.

I wrote 30 stdlib-unittest tests against fixture JSON, covering rendering, filtering, timestamp formatting, and HTML cleanup. I left auth and HTTP uncovered — they're thin wrappers around the Google client libraries, best checked by running the script once after OAuth setup.

docs/calendar-sync-api-setup.org walks through the Google Cloud OAuth client setup and the per-account auth bootstrap. .gitignore picks up Python bytecode now that the project has a Python helper.

The Elisp dispatch (:fetcher 'api routing in calendar-sync.el) lands in a follow-up commit.
</content>
</entry>
<entry>
<title>chore: stop gitignoring /todo.org</title>
<updated>2026-05-13T19:30:31+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-13T19:30:31+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=e7ab89b109881231bc5465b09122966191b43d8c'/>
<id>urn:sha1:e7ab89b109881231bc5465b09122966191b43d8c</id>
<content type='text'>
</content>
</entry>
<entry>
<title>chore: gitignore .emacs-theme runtime state</title>
<updated>2026-05-08T11:37:06+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-08T11:37:06+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=afa82a2eaa30fcd383abc71a4d2da86d0786a2fd'/>
<id>urn:sha1:afa82a2eaa30fcd383abc71a4d2da86d0786a2fd</id>
<content type='text'>
</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>
<entry>
<title>chore: gitignore Emacs backup, auto-save, and lock files</title>
<updated>2026-05-04T01:15:42+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-04T01:15:42+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=ff8071ff274342f270de32ac3a8b282b6f75adaa'/>
<id>urn:sha1:ff8071ff274342f270de32ac3a8b282b6f75adaa</id>
<content type='text'>
</content>
</entry>
<entry>
<title>feat(coverage): wire make coverage target + simplecov pipeline</title>
<updated>2026-04-23T00:58:00+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-23T00:58:00+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=a97266c0e89ef8560824789063512d2613849fc9'/>
<id>urn:sha1:a97266c0e89ef8560824789063512d2613849fc9</id>
<content type='text'>
Completes the coverage v1 pipeline by adding the Makefile target, the undercover driver script, the exclusion list, and the .gitignore entry. Uses simplecov JSON rather than LCOV as the collection format.

The LCOV vs simplecov choice: Undercover's :merge-report t option only supports simplecov. Since the pipeline runs tests per-file (matching test-unit's isolation pattern) and accumulates coverage across runs, merge-report is required. LCOV is better-supported by external coverage viewers, but for a primarily interactive workflow the on-disk format is an internal detail.

Other moves in this commit:

- Renamed cj/--coverage-parse-lcov to cj/--coverage-parse-simplecov and rewrote its tests for the JSON schema. Same signature, same semantics (file to set of covered lines), different parser.
- Renamed the backend protocol's :lcov-path key to :report-path, format-neutral and matching the renamed cj/--coverage-elisp-report-path function.
- The coverage target deletes modules/*.elc before running so undercover can instrument the .el sources. Without this, byte-compiled versions shadow the instrumentation and only a handful of pre-loaded modules end up with coverage data.
- Excluded tests/test-all-comp-errors.el from make coverage runs. That test byte-compiles every module, which fails under undercover's instrumentation. Excluded only from coverage. Normal make test still runs it.
- Updated docs/design/coverage.org to reflect the simplecov pivot with a historical note on why we moved off LCOV.

Verified end-to-end: make coverage produces .coverage/simplecov.json with 2717 of 4559 executable lines hit across 44 tracked modules.
</content>
</entry>
<entry>
<title>chore: rename /docs/ → /.ai/ in gitignore and CLAUDE.md</title>
<updated>2026-04-20T09:17:56+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-20T09:17:56+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=7361813dccf3d234cb6f84185926a5411dcc30a2'/>
<id>urn:sha1:7361813dccf3d234cb6f84185926a5411dcc30a2</id>
<content type='text'>
.gitignore: rename the private-tooling entry from /docs/ to /.ai/.
CLAUDE.md: expand Layout to list /.ai/ (gitignored) and docs/
(reserved for real project documentation, if/when created).
</content>
</entry>
<entry>
<title>chore: gitignore *.eln (native-compiled artifacts from validate-el.sh)</title>
<updated>2026-04-19T18:01:26+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-19T18:01:26+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=403aba33301f65a45d9ffe0af84400c8888368b4'/>
<id>urn:sha1:403aba33301f65a45d9ffe0af84400c8888368b4</id>
<content type='text'>
</content>
</entry>
<entry>
<title>chore: track CLAUDE.md and .claude/ bundle, narrow gitignore</title>
<updated>2026-04-19T17:14:54+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-19T17:14:54+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=1172e4ef1e2d5dbcf2274820ca7759e3aa54b6c6'/>
<id>urn:sha1:1172e4ef1e2d5dbcf2274820ca7759e3aa54b6c6</id>
<content type='text'>
Swap blanket /CLAUDE.md + /.claude/ gitignore rules for personal-only
overrides (.claude/settings.local.json, .claude/.cache/). Add the
tracked bundle content from ~/code/rulesets install-elisp:

  - CLAUDE.md                    (project instructions)
  - .claude/rules/elisp.md
  - .claude/rules/elisp-testing.md
  - .claude/rules/verification.md
  - .claude/hooks/validate-el.sh (portable via $CLAUDE_PROJECT_DIR)
  - .claude/settings.json        (allowlist + hook wiring)

Hooks now use $CLAUDE_PROJECT_DIR with script-relative fallback, so a
fresh clone of this repo works on any machine without path edits.

Project-local skills under .claude/skills/ were stale DeepSat-flavored
copies; deleted and replaced with symlinks into ~/.claude/skills/ via
the rulesets repo's global install.
</content>
</entry>
<entry>
<title>chore: gitignore and untrack data/ directory</title>
<updated>2026-04-05T11:22:19+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-05T11:22:19+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=a03d433d6145ea394747833428091ecbba7bc55c'/>
<id>urn:sha1:a03d433d6145ea394747833428091ecbba7bc55c</id>
<content type='text'>
</content>
</entry>
</feed>
