<feed xmlns='http://www.w3.org/2005/Atom'>
<title>rulesets/languages/elisp, branch main</title>
<subtitle>Claude Code skills, rules, and language bundles
</subtitle>
<id>https://git.cjennings.net/rulesets/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/rulesets/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/'/>
<updated>2026-06-13T18:08:25+00:00</updated>
<entry>
<title>fix(elisp): byte-compile cross-project .el edits against their own modules</title>
<updated>2026-06-13T18:08:25+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-13T18:08:25+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=9e4c58029785c418ad6dbdbff3988a8582388c34'/>
<id>urn:sha1:9e4c58029785c418ad6dbdbff3988a8582388c34</id>
<content type='text'>
The validate-el hook only put the current project's roots on the load path, so editing an .el file from another project failed Phase 1 byte-compile on free-variable warnings: the file's own sibling modules weren't reachable. I added the edited file's directory and its parent to the load path. For in-project edits both are redundant (already covered by PROJECT_ROOT and its modules dir). They only do work when the file sits outside the current project root.

I left Phase 2's test runner alone. It discovers tests by stem under PROJECT_ROOT/tests, so a cross-project file's tests aren't found regardless of load path.
</content>
</entry>
<entry>
<title>docs(elisp): name eask as the build tool, not eldev</title>
<updated>2026-06-09T19:15:52+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-09T19:15:52+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=4daecc8c3957f4e6355c9591bb5fccd481933b58'/>
<id>urn:sha1:4daecc8c3957f4e6355c9591bb5fccd481933b58</id>
<content type='text'>
The real elisp packages drive eask in their Makefiles, but the bundle listed eldev as a build tool and left eask out, pointing a fresh session at the wrong tool.
</content>
</entry>
<entry>
<title>fix(elisp-hook): show a compact test summary in the terminal on failure</title>
<updated>2026-06-07T15:27:17+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-07T15:27:17+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=d733bb29763d6e936b460dcf136a491c156eb888'/>
<id>urn:sha1:d733bb29763d6e936b460dcf136a491c156eb888</id>
<content type='text'>
validate-el.sh dumped the full ERT batch output to the terminal on a red test, every backtrace frame, flooding the pane. It now prints a short summary there: the run tally plus the failing test names and their file:line. Claude still gets the full backtrace through additionalContext. fail_json takes an optional fourth argument for the terminal echo. Paren and byte-compile failures stay short, so they still print in full.
</content>
</entry>
<entry>
<title>feat(elisp): add coverage-summary to the Elisp bundle with missing-file detection</title>
<updated>2026-05-31T16:43:03+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-31T16:43:03+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=b46619cd17ed4e36f2e59c1b600078521b2049ef'/>
<id>urn:sha1:b46619cd17ed4e36f2e59c1b600078521b2049ef</id>
<content type='text'>
A line-weighted coverage total has a blind spot: a module no test loads never shows up in the SimpleCov report, so it can't drag the number down. The suite looks healthier than it is. This adds a summary that counts every source file on disk against the report and treats an absent file as 0%, weighting the project number by file instead of by line so untested modules stay visible.

The script ships at languages/elisp/claude/scripts/coverage-summary.el, self-contained on stock Emacs (just the built-in json). It parses the undercover SimpleCov shape directly rather than depending on the editor's coverage engine, so it runs anywhere the bundle lands. I proved it against a real 103-file report: 93 tracked, 27 untested modules surfaced, project number 66.4%.

Delivery follows the bundle convention. The script lives under the gitignored .claude/ footprint and gets auto-fixed on drift by sync-language-bundle.sh, which I made generic for any claude/scripts/* rather than coverage-specific. The Makefile targets ship as a project-owned fragment (languages/elisp/coverage-makefile.txt) that install-lang.sh seeds at the project root and sync drops into .ai/inbox/ when that convention exists. The bundle never edits the project's own Makefile.

Tests: 12 ERT for the kernel (Normal/Boundary/Error per function), wired into make test via a new languages/*/tests/ discovery path, plus bats for the sync auto-fix and the inbox-drop guards.

This is the Elisp pilot. The pattern is proven, so fanning out to Python, Go, and TypeScript is now a follow-up. Each one needs only its own parser and fragment. The plumbing is already generic.
</content>
</entry>
<entry>
<title>fix(elisp): add themes/ to the validate-el.sh load path</title>
<updated>2026-05-25T20:39:05+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-25T20:39:05+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=e1b5bc7628899445cb317e2f27aed9586c303ae6'/>
<id>urn:sha1:e1b5bc7628899445cb317e2f27aed9586c303ae6</id>
<content type='text'>
The PostToolUse hook byte-compiles each saved .el with -L for the project root, modules/, and tests/, but not themes/. A theme file that requires a sibling under themes/ then fails byte-compile with "Cannot open load file", a false VALIDATION FAILED even though the file loads fine at runtime. I added -L themes/ to both the byte-compile and test-runner blocks.

Adding a load-path entry for a directory that doesn't exist is harmless to Emacs, so it stays unconditional, matching how modules/ and tests/ are already added without an existence guard.
</content>
</entry>
<entry>
<title>fix(elisp): gitignore the full Claude tooling footprint</title>
<updated>2026-05-25T19:50:31+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-25T19:50:31+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=d27d07e8e4b004926ea960c118fa99caa979caa0'/>
<id>urn:sha1:d27d07e8e4b004926ea960c118fa99caa979caa0</id>
<content type='text'>
The bundle tracked .claude/rules, CLAUDE.md, and githooks/, ignoring only the personal overrides. For a code project, especially a third-party package checkout, the whole Claude footprint should stay local: install and sync deliver it, so it shouldn't land in the project's history. gitignore-add.txt now ignores .claude/, CLAUDE.md, and githooks/ next to the elisp build artifacts.

I also added install-lang.bats, which the bundle had no test for. It covers the landed footprint and the gitignore set.
</content>
</entry>
<entry>
<title>docs(languages): tighten elisp coding and testing rules</title>
<updated>2026-05-22T20:07:32+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-22T20:07:32+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=2305b9532fd8a6384d80e90e0cb93e17e3f8022f'/>
<id>urn:sha1:2305b9532fd8a6384d80e90e0cb93e17e3f8022f</id>
<content type='text'>
Two audit fixes. elisp.md's "prefer Write over Edits" advice was tool-specific. It's now framed around intent: edit cohesively, then run paren and byte-compile checks immediately, whatever the editing mechanism. elisp-testing.md gains batch-mode reproducibility (emacs --batch as source of truth, no interactive state, no blocking prompts), state isolation (temp user-emacs-directory, explicit load-path, declared deps only), and byte-compile/native-comp warning handling, with native-comp gated on availability and kept opt-in.
</content>
</entry>
<entry>
<title>feat(hooks): add global hooks — PreCompact priorities + git/gh confirm modals</title>
<updated>2026-04-19T22:06:10+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-19T22:06:10+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=4957c60c9ee985628ad59344e593d20a18ca8fdb'/>
<id>urn:sha1:4957c60c9ee985628ad59344e593d20a18ca8fdb</id>
<content type='text'>
Three new machine-wide hooks installed via `make install-hooks`:

- `precompact-priorities.sh` (PreCompact) — injects a priority block into
  the compaction prompt so the generated summary retains information most
  expensive to reconstruct: unanswered questions, root causes with
  file:line, subagent findings as primary evidence, exact numbers/IDs,
  A-vs-B decisions, open TODOs, classified-data handling.

- `git-commit-confirm.py` (PreToolUse/Bash) — gates `git commit` behind a
  confirmation modal showing parsed message, staged files, diff stats,
  author. Parses both HEREDOC and `-m`/`--message` forms.

- `gh-pr-create-confirm.py` (PreToolUse/Bash) — gates `gh pr create`
  behind a modal showing title, base ← head, reviewers, labels,
  assignees, milestone, draft flag, body (HEREDOC or quoted).

Makefile: adds `install-hooks` / `uninstall-hooks` targets and extends
`list` with a Hooks section. Install prints the settings.json snippet
(in `hooks/settings-snippet.json`) to merge into `~/.claude/settings.json`.

Also: `languages/elisp/claude/hooks/validate-el.sh` now emits JSON with
`hookSpecificOutput.additionalContext` on failure (via new `fail_json()`
helper) so Claude sees a structured error in context, in addition to
the existing stderr output and exit 2.

Patterns synthesized clean-room from fcakyon/claude-codex-settings
(Apache-2.0). Each hook is original content.
</content>
</entry>
<entry>
<title>feat(rules): port key testing principles from quality-engineer prompt</title>
<updated>2026-04-19T18:16:07+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-19T18:16:07+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=e50c732d7138c18749b96b57004a3e23f31bbaef'/>
<id>urn:sha1:e50c732d7138c18749b96b57004a3e23f31bbaef</id>
<content type='text'>
Additions to claude-rules/testing.md:
- Testing pyramid proportions (70-80% unit / 15-25% integration / 5-10% e2e)
- Integration Tests section: docstring must name 'Components integrated:'
  and mark real vs mocked; when-to-write heuristics
- Signs of Overmocking: 'would the test pass if the function body was
  NotImplementedError?' plus three more sharp questions
- Testing Code That Uses Frameworks: test your integration, not the
  framework itself
- Test Real Code, Not Copies: never inline prod code into tests
- Error Behavior, Not Error Text: test type + key values, not exact prose
- If Tests Are Hard to Write, Refactor the Code: hard-to-test is a code
  signal, not a test signal; extract focused helpers
- Anti-patterns list extended

Addition to languages/elisp/claude/rules/elisp-testing.md:
- Interactive vs Internal split pattern: cj/foo wraps cj/--foo; test the
  internal directly, skip UI mocks

Source: ~/.emacs.d/ai-prompts/quality-engineer.org (personal reference,
kept as an extended prompt separate from these rules).
</content>
</entry>
<entry>
<title>chore: remove project-specific references</title>
<updated>2026-04-19T18:09:45+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-19T18:09:45+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=2d026369b616e51199579ff039cc34be4d5c2ef9'/>
<id>urn:sha1:2d026369b616e51199579ff039cc34be4d5c2ef9</id>
<content type='text'>
- elisp-testing.md: generalized testutil description. The specific files
  testutil-general.el / testutil-filesystem.el / testutil-org.el only
  exist in one project; bundle should describe the pattern, not name
  specific files.
- README.org: install examples use ~/code/ path to match actual layout.
</content>
</entry>
</feed>
