<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-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>feat(languages): ship the missing python and typescript hooks</title>
<updated>2026-07-24T01:48:44+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-24T01:48:44+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=c238afbd4150ef53737f16e7dd84a565d2838ecc'/>
<id>urn:sha1:c238afbd4150ef53737f16e7dd84a565d2838ecc</id>
<content type='text'>
The python and typescript bundles carried rules and a coverage script but no pre-commit hook, so any project installing one got no credential scan on commit. Both now ship all four components the README documents: the shared secret scan, a validate-on-edit hook, settings wiring, and a seed CLAUDE.md. Verified against a real repo: a commit carrying an AWS key is refused.

install-lang now warns when a bundle is missing a documented component. That's the half that keeps this from recurring. Whoever adds the sixth bundle will forget something too, and today the installer prints success either way.

Two things fell out of the build. node --check is unusable on TypeScript: it ignores --experimental-strip-types, so it rejects valid TS and accepts broken TS. The hook uses tsc filtered to syntactic diagnostics instead. And completing the bundles means every pair now collides on settings.json and pre-commit, so no two compose without FORCE=1. The earlier "bundles already compose" reading rested on these two being incomplete. Filed for a real decision; clock-panel is the project that wants it.

Also stamps :LAST_REVIEWED: at task creation, with a lint checker to catch misses. Writing a task is reviewing it, so leaving the stamp off pushed every fresh task to the top of the next review batch to be re-derived by someone with less context than its author had. Tonight's sweep sent the staleness count from 13 to 22 while the list got more accurate.
</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(githooks): match fixed-case secret tokens case-sensitively</title>
<updated>2026-07-16T16:19:45+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-16T16:19:45+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=794a8bd606006cc21351be098a30cbbfe55750e4'/>
<id>urn:sha1:794a8bd606006cc21351be098a30cbbfe55750e4</id>
<content type='text'>
The secret scan ran every pattern through grep -iE, so AKIA[0-9A-Z]{16} matched any mixed-case 20-char run. Random base64 in an embedded image blob hits that about 6% of the time per 100KB, enough to block a real commit and force --no-verify. takuzu hit it on a PNG sprite. AWS keys are uppercase, sk- keys lowercase, and PEM headers fixed, so those three now match case-sensitively. Only the keyword=value patterns still need -i.

I measured both causes first. Across ~10MB of random base64 the old pattern produced 7 matches and would have false-positived 6 of 100 sprite-sized blobs. Case-sensitive matching produced none.

takuzu's report also proposed skipping any added line containing ";base64,". I left that out. It guards against an uppercase AKIA run surviving the case fix, which never occurred in the sample and runs about one in a million per blob. The cost is real: minified bundles put a whole file on one line, so a data URI and a live key can share it, and skipping the line hides the key. A test covers that.

The three variants share one scan block, so elisp, bash, and go all carry the change. Splitting one grep into two meant a line matching both passes got reported twice, which reads as two separate leaks. awk dedupes it.
</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>test(elisp): cover coverage-summary source-file scan and under-dir filtering</title>
<updated>2026-06-22T03:59:29+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-22T03:59:29+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=0751b3c0f4e56061e79aa408493f77510e08f053'/>
<id>urn:sha1:0751b3c0f4e56061e79aa408493f77510e08f053</id>
<content type='text'>
Adds direct tests for cj/coverage-summary--source-files (non-recursive, subdirectories out of scope) and --under-dir (report entries outside the source dir dropped, survivors re-keyed relative to project root). Both were previously exercised only indirectly through the missing-file tests.
</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>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>
</feed>
