<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dotemacs/docs, 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-24T08:54:09+00:00</updated>
<entry>
<title>docs(ai-kb): fold in review 6 and resolve the build-time decisions</title>
<updated>2026-05-24T08:54:09+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T08:54:09+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=6ebd0314c880c4f9fc15936618ad412cef6ea309'/>
<id>urn:sha1:6ebd0314c880c4f9fc15936618ad412cef6ea309</id>
<content type='text'>
The latest design review was a UX and performance pass, and I folded its findings into the spec and the implementation tasks. The important one: human Emacs edits now use the same write path as agent writes. An ai-kb minor mode runs index, full lint, and commit under flock on after-save, so a hand edit can't quietly skip the safety gate. The rest: the generated index.org is now invisible to backlink and orphan logic (excluded from the scan, referenced as plain text rather than id-links), a required :SUMMARY: property feeds the index and query without inference, query gains lexical ranking with recency only as a tie-break, the switch installs a full org-roam profile rather than a two-variable swap, and the browsing surface (dashboard, find, search, show, backlinks, map) is named.

I also answered the six build-time decisions: concrete raw and curation limits, performance budgets for the perf fixtures, the lexical scoring weights, org-roam-graph as the first map implementation, the after-save failure UX (the save always lands, the commit is gated, and a failure shows without trapping the buffer), and the after-save recursion guard. The numeric limits and budgets are starting points to calibrate. The rest are firm. Step 1 stays buildable.
</content>
</entry>
<entry>
<title>docs(design): incorporate ai-kb review 5</title>
<updated>2026-05-24T08:30:26+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T08:30:26+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=51c8c2fe8069740ce79fdb60431da71a23d9a7f9'/>
<id>urn:sha1:51c8c2fe8069740ce79fdb60431da71a23d9a7f9</id>
<content type='text'>
Review 5 was implementation-hardening, all of it sound, so I folded in all six findings. The important one: the commit gate now runs the full ai-kb lint over the change (index freshness, duplicate IDs, broken links, and a secret scan of nodes and raw/), not just org-lint on the edited node. If the write path is the safety boundary, gating only on single-node syntax would let a stale index or a leaked secret through.

The rest, all adopted: an explicit org-lint fatal-check list so a future org-lint change can't silently move the gate, observable push failures surfaced through a state-file log and ai-kb doctor and a startup nudge so the KB can't go quietly local-only, a testable ai-kb query contract with text and --json output, and ID-first durable pointers since filenames change in curation but IDs don't. I also split the build plan into Step 1a (the safe write path) and 1b (query, curate, sync, push timer, workflow), since remember depends on index and lint and the adapter depends on remember.
</content>
</entry>
<entry>
<title>docs(design): resolve ai-kb open decisions and refresh provisioning</title>
<updated>2026-05-24T08:16:48+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T08:16:48+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=b96592c2df6ab02c45c500a5e457affe0e2c990f'/>
<id>urn:sha1:b96592c2df6ab02c45c500a5e457affe0e2c990f</id>
<content type='text'>
I resolved the four open decisions and baked the answers in: the store lives at ~/.local/share/ai-kb (XDG), the ai-kb CLI is a shell wrapper that calls emacs --batch for the org-lint and sync steps, the push runs off a background systemd --user timer rather than firing on every write (remember only commits locally), and curation is node-count-triggered with the workflow living in the rulesets .ai/workflows/ directory.

I also refreshed the provisioning and Step 1 sections to match, since the push timer was a new piece: make ai-kb-init now installs and enables the ai-kb-push timer and service units, and doctor checks for them. Open decisions is empty now and the spec is fully decided.
</content>
</entry>
<entry>
<title>docs(design): fold ai-kb reviews 3-4 into the spec</title>
<updated>2026-05-24T08:08:48+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T08:08:48+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=c87631f0c200556d99b2ccbcd838cdf6877c7014'/>
<id>urn:sha1:c87631f0c200556d99b2ccbcd838cdf6877c7014</id>
<content type='text'>
Reviews 3 (Codex, via Nexus/GraphRAG/Letta research) and 4 pushed on the write loop and the access layer rather than scope. I folded both in. The write path is now a real protocol: fetch and fast-forward before writing, org-lint the node, regenerate the index, commit locally always, and treat the push as best-effort and non-blocking so a failed push never errors or hangs the agent. That's the exact gpg-agent failure we hit earlier today. The index is regenerated from node properties by a script rather than hand-maintained, so it can't drift from the nodes.

The access layer became an agent-neutral contract that lives in the repo, fronted by a minimal ai-kb CLI (doctor, query, remember, lint, curate, sync) with destructive operations human-only. That earns its place on Claude-only grounds: it's the clean home for the safe-write protocol and the lint and index steps. Cross-agent use is not a near-term goal, so Codex and Ollama adapters are deferred to vNext. The contract stays neutral in shape, so they're additive later. Added provenance fields, the T1/T2/T3 tier names, and the review dispositions. The spec is now Ready.
</content>
</entry>
<entry>
<title>docs(design): add ai-kb spec — global org-roam memory store for the agent</title>
<updated>2026-05-24T07:32:56+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T07:32:56+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=1faa6e7538d458a9e65c6e97fbf566363686e6c8'/>
<id>urn:sha1:1faa6e7538d458a9e65c6e97fbf566363686e6c8</id>
<content type='text'>
ai-kb is a global, durable, cross-project memory store for Claude Code: org-roam nodes holding lessons, principles, my preferences, and reusable procedures, distinct from the per-project memory files (which shrink to an index pointing into it). The spec covers the two-layer model (a git-versioned file store the agent reads/writes, and an Emacs switch command so I can browse it with backlinks), the sync model, the routing and proactive-write rules, the node format, and the startup retrieval contract.

It folds in two reviews. The scope decision: v1 is the memory store, not a full Karpathy LLM Wiki. The heavy machinery (compiled wiki layer, source hashes, formal ingest pipeline, embedding search) is deferred to vNext, each with a reason. Storage is a dedicated private git repo at an XDG path rather than Syncthing or the public emacs-config repo, which would leak personal notes. Two Karpathy ideas earned their way into v1 because they pay off now: capturing the raw source when a node is compiled from external material, and an org-lint validity check on every write so malformed org never reaches the index. Review dispositions and the open decisions are recorded in the spec.
</content>
</entry>
<entry>
<title>docs(mail): document compose-buffer cleanup settings</title>
<updated>2026-05-23T09:10:58+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-23T09:10:58+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=c7fa570bbed7a40c3531fda0eb410419873bb1a6'/>
<id>urn:sha1:c7fa570bbed7a40c3531fda0eb410419873bb1a6</id>
<content type='text'>
I added docs/mu4e-org-msg-compose-buffer-cleanup.org explaining the one setting that controls whether mail compose buffers close on exit (message-kill-buffer-on-exit), why org-msg needs no setting of its own (it reads the variable, never sets it), and the trap that a stray setter in org-msg's :config silently wins over the mu4e one.
</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>docs(design): keep local gptel-magit design draft as .local.org</title>
<updated>2026-05-17T21:00:57+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-17T21:00:57+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=3aa577665329200c90c17db2c949acb45514c29b'/>
<id>urn:sha1:3aa577665329200c90c17db2c949acb45514c29b</id>
<content type='text'>
Two drafts of `docs/design/gptel-git-tools-magit-backend.org` existed at the same path: a 592-line local copy and the 192-line upstream version that just landed in main. I renamed the local draft to `.local.org` so the upstream version can sit at the canonical path. I'll reconcile the two in a follow-up.
</content>
</entry>
<entry>
<title>docs(design): MCP-into-gptel + gh-as-gptel-tool specs + MCP phases</title>
<updated>2026-05-17T06:16:03+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-17T06:16:03+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=2b61aa82afa39a0ff1b165fa9ff09d55d21bfabf'/>
<id>urn:sha1:2b61aa82afa39a0ff1b165fa9ff09d55d21bfabf</id>
<content type='text'>
Two new design docs in docs/design/ covering the next two GPTel
work items, plus matching task scaffolding in todo.org.

mcp-el-gptel-integration.org wires mcp.el into the config so GPTel
gets access to the nine MCP servers Claude Code already uses
(linear, notion, figma, slack-deepsat, drawio, google-calendar,
google-docs-personal, google-docs-work, google-keep). The design
covers async startup, the write-confirmation policy, a
server-enablement defcustom, a doctor with live-auth-check, the
audit buffer, and the mcp.el compatibility layer. The spec is at
revision 3 after two code-review passes flagged a critical
confirmation gap (gptel-confirm-tool-calls nil at ai-config.el:386
silently ignored per-tool :confirm slots) and several incorrect
mcp.el API assumptions. Both are addressed.

gptel-gh-tool.org wraps the gh CLI as a hybrid surface: 14 typed
read wrappers plus one general write tool gated by :confirm t.
Host/repo resolution is command-aware: --repo HOST/OWNER/REPO for
repo commands, --hostname only for api and auth status. The runner
enforces an irreversible-command blocklist, a 64KB in-flight output
cap, and a debug-record plus last-error-buffer story. The spec is
at revision 2 after a code-review pass corrected gh flag
assumptions and reframed the safety story around per-tool confirm.

todo.org gains a link to the MCP spec under the parent task plus
nine TODO sub-tasks (one per implementation phase), and a new
gh-tool TODO with the same spec-link shape.
</content>
</entry>
<entry>
<title>fix(coverage): include gptel-tools in instrumentation glob</title>
<updated>2026-05-16T15:29:50+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-16T15:29:50+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=faf716de7ad4b69486e42de591588d8b750bbdb9'/>
<id>urn:sha1:faf716de7ad4b69486e42de591588d8b750bbdb9</id>
<content type='text'>
Undercover now instruments gptel-tools/*.el alongside modules/*.el,
so the new git_status / git_log / git_diff / web_fetch tools (and
their successors) report coverage instead of reading as zero.

The matching pre-coverage clean step deletes gptel-tools/*.elc so
stale byte-compiled artifacts don't shadow the .el sources.  If
Emacs loads the .elc first, undercover's source instrumentation
never runs.

docs/design/coverage.org gains an Elisp-coverage-producer subsection
documenting the glob, the :merge-report dependence (SimpleCov merges
cross-process reports, LCOV does not), and the missing-artifact
failure mode.
</content>
</entry>
</feed>
