<feed xmlns='http://www.w3.org/2005/Atom'>
<title>rulesets/languages/elisp/claude, 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-07-24T17:28:18+00:00</updated>
<entry>
<title>fix(hooks): the secret scan no longer passes when git fails</title>
<updated>2026-07-24T17:28:18+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-24T17:28:18+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=f0c1bc40708615d5b423922c08c1f27e6cf96259'/>
<id>urn:sha1:f0c1bc40708615d5b423922c08c1f27e6cf96259</id>
<content type='text'>
Every bundle built its scan input as `git diff --cached ... | grep ... || true`. The `|| true` has to stay, since grep exits 1 when it matches nothing and that is the ordinary case. But with no pipefail it also swallowed a failure of git itself, so an empty result made the scan search nothing, find nothing, and report clean with a real credential staged. A gate that passes without having looked.

.emacs.d found it in elisp. It was in all five: bash, elisp, go, python and typescript, eleven sites once each bundle's staged-file list is counted. Two of those bundles are ones I wrote yesterday by copying bash, so I propagated it while closing an unrelated gap in the same file. Each site now reads the diff on its own and aborts if git fails, leaving the greps their `|| true`.

Verified per bundle on three axes: refuses when the diff cannot be read, still blocks a real staged secret, still passes a clean commit.

The cross-bundle test suite had its own version of the same disease. Its VARIANTS list read "elisp bash go" while python and typescript also shipped hooks, so every "in every variant" assertion had quietly skipped two bundles since the day they were added. VARIANTS is now discovered from the tree. Two fail-closed assertions join it, and .emacs.d's elisp suite is adopted here beside the canonical hook, because a test living in the consuming project cannot fail when the canonical regresses.

Also removes the validate-el auto-test cap, Craig's call. Above 20 matching test files the runner skipped everything and exited 0 with no output. The premise was speed and it did not hold: a whole family runs in about a second. The cap was also hiding a real cross-test pollution bug that only surfaces when a family runs in one process.
</content>
</entry>
<entry>
<title>docs(elisp): mark coverage-summary.el as a local-only helper</title>
<updated>2026-07-19T01:08:49+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-19T01:08:49+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=2cb7c1b3957193f74b1ecd1b5e0f437e13092eb1'/>
<id>urn:sha1:2cb7c1b3957193f74b1ecd1b5e0f437e13092eb1</id>
<content type='text'>
The elisp bundle installs coverage-summary.el into .claude/scripts/, which is gitignored in code projects, so CI can't run make coverage-summary against it. That's intentional (Craig, 2026-06-28): it stays a developer-run check, not a CI gate. Stated the local-only status in the script's commentary header and in elisp-testing.md's coverage section so the gitignored install reads as by-design rather than a gap.
</content>
</entry>
<entry>
<title>fix(elisp): validate-el.sh prefers source over stale bytecode</title>
<updated>2026-07-14T04:39:07+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-14T04:39:07+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=7e61dc0fa44e9b782f2a551e3813bb11eed20af7'/>
<id>urn:sha1:7e61dc0fa44e9b782f2a551e3813bb11eed20af7</id>
<content type='text'>
Both emacs invocations in the hook now set load-prefer-newer, matching the project-side Makefile and early-init guards. Without it, the PostToolUse hook after a pull that changed a module ran tests against the module's stale .elc and every test failed void-function on the newly-added symbols. From .emacs.d's 2026-07-13 handoff, repro-verified there; inbox/2026-07-13-1712-from-.emacs.d.
</content>
</entry>
<entry>
<title>fix(elisp): cd to tests/ before the Phase 2 test load</title>
<updated>2026-06-24T03:16:03+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-24T03:16:03+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=e5aab199cd4c83f357ff5190139ddf2994ac28a3'/>
<id>urn:sha1:e5aab199cd4c83f357ff5190139ddf2994ac28a3</id>
<content type='text'>
Phase 2's plain `-l` load ran emacs from PROJECT_ROOT, so test files that resolve test-bootstrap.el with `(expand-file-name "test-bootstrap.el")` against default-directory couldn't find it and failed with "Cannot open load file". Phase 1 escaped this because byte-compile-file rebinds default-directory to the source file's dir. The cd restores the tests/ working directory the tests/Makefile already runs from.

Restores b2e9038, dropped when a consuming project's .claude refreshed to the rulesets canonical, which never carried the fix.

Claude-Session: https://claude.ai/code/session_017PtX1nt1rtYVATuzmzBS4f
</content>
</entry>
<entry>
<title>fix(elisp): exclude generated package files from coverage source scan</title>
<updated>2026-06-22T03:57:34+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-22T03:57:34+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=fb867366f4760da94f732c296601f7c324de0418'/>
<id>urn:sha1:fb867366f4760da94f732c296601f7c324de0418</id>
<content type='text'>
cj/coverage-summary--source-files scanned SOURCE-DIR for *.el and treated every match as testable source. Build tools write NAME-autoloads.el and NAME-pkg.el into that dir, and undercover never instruments them, so the missing-file detection counted each as untested at 0% and dragged the file-weighted project number down. emacs-wttrin read 72.9% instead of 97.2% after eask wrote its autoloads file during a coverage run.

seq-remove the generated files before returning. A genuinely untested source is still flagged. The new test asserts both.
</content>
</entry>
<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>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>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>
</feed>
