<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dotemacs/.gitignore, branch main</title>
<subtitle>My Emacs configuration
</subtitle>
<id>https://git.cjennings.net/dotemacs/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/dotemacs/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/'/>
<updated>2026-06-03T01:57:24+00:00</updated>
<entry>
<title>chore: gitignore slime-history.eld</title>
<updated>2026-06-03T01:57:24+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-03T01:57:24+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=f49c45e6839c2425a10ce369a285382344e4999a'/>
<id>urn:sha1:f49c45e6839c2425a10ce369a285382344e4999a</id>
<content type='text'>
</content>
</entry>
<entry>
<title>feat(signal): add Signal client foundation on a signel fork</title>
<updated>2026-05-27T01:24:58+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-27T01:24:58+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=049325f438817c0b4f4a443f71b8821b0bfd357a'/>
<id>urn:sha1:049325f438817c0b4f4a443f71b8821b0bfd357a</id>
<content type='text'>
I'm building a Signal client in Emacs on signal-cli (linked as a secondary device) with a fork of the signel package as the front end. signel is on MELPA but effectively abandoned, and the behavior I want needs internal edits, so owning a fork beats advising a dead package. Full rationale and the rejected alternatives are in docs/design/signal-client.org.

This lands the signal-cli-independent foundation: contact-list parsing for a completing-read picker, and the predicate that suppresses a notification for the chat being actively viewed. Both are pure and unit-tested without a linked account. cj/signal--parse-contacts was corrected against a live account (signal-cli 0.14 puts givenName/familyName at the top level, not under profile), and verified across all 94 real contacts.

The use-package wiring loads the fork from ~/code/signel, sources the account from a gitignored signal-config.local.el (a phone number is an identifier, not a credential, and this keeps it off the mirror without a GPG prompt), and turns off auto-open so an incoming message can't steal a window. Verified live: signel-start spawns the jsonRpc process, loads the account, and receives over the channel.

The fork edits (notify routing, the upstream input-clobber bug) and the contact-picker command are still to come.
</content>
</entry>
<entry>
<title>chore: drop the stray butterfly PNG backup and ignore .bak files</title>
<updated>2026-05-26T23:07:13+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-26T23:07:13+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=43c59b8b0b8b4ee4cec7a7c6b5dc17292e4fc051'/>
<id>urn:sha1:43c59b8b0b8b4ee4cec7a7c6b5dc17292e4fc051</id>
<content type='text'>
assets/M-x_butterfly.png.bak was a tracked duplicate of the banner PNG, an accidental backup that got committed. I removed it and added *.bak to .gitignore so editor and image backups stay out of the tree.
</content>
</entry>
<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>
</feed>
