aboutsummaryrefslogtreecommitdiff
path: root/.claude/rules
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-12 18:14:19 -0500
committerCraig Jennings <c@cjennings.net>2026-06-12 18:14:19 -0500
commitb39633d9d482b6510e5626e0c5441ad211508a6e (patch)
treecbec839665b1fdb942629c3586711f57a4d50829 /.claude/rules
parentcf17681bcf1ed458e4972e3853be13e48ecde6b1 (diff)
downloadchime-b39633d9d482b6510e5626e0c5441ad211508a6e.tar.gz
chime-b39633d9d482b6510e5626e0c5441ad211508a6e.zip
chore: refresh .claude rules and hooks to rulesets canonical
The startup language-bundle sync repaired drift in these rulesets-owned files. Committing them at canonical so future syncs stay no-ops.
Diffstat (limited to '.claude/rules')
-rw-r--r--.claude/rules/elisp-testing.md50
-rw-r--r--.claude/rules/elisp.md2
2 files changed, 51 insertions, 1 deletions
diff --git a/.claude/rules/elisp-testing.md b/.claude/rules/elisp-testing.md
index b5def78..7c3a9ef 100644
--- a/.claude/rules/elisp-testing.md
+++ b/.claude/rules/elisp-testing.md
@@ -37,6 +37,12 @@ Every non-trivial function needs at least:
Missing a category is a test gap. If three cases look near-identical, parametrize with a loop or `dolist` rather than copy-pasting.
+### Measuring it — `make coverage-summary`
+
+The bundle ships a coverage summary at `.claude/scripts/coverage-summary.el` and a Makefile fragment (`coverage-makefile.txt`) with `coverage` and `coverage-summary` targets. After `make coverage` writes an undercover SimpleCov report, `make coverage-summary` prints a per-file table and a unit-weighted project number.
+
+The number to watch is the missing-file count. A module no test loads never appears in the SimpleCov report, so a line-weighted total skips it silently — the suite looks healthier than it is. The summary counts every `modules/*.el` on disk that's absent from the report as 0%, so an untested module drags the project number down where you can see it. Copy the fragment's targets into your own Makefile to adopt it; the bundle never edits your Makefile.
+
## TDD Workflow
Write the failing test first. A failing test proves you understand the change. Assume the bug is in production code until the test proves otherwise — never fix the test before proving the test is wrong.
@@ -99,6 +105,50 @@ make test-name TEST=pattern # Match by test name pattern
A PostToolUse hook runs matching tests automatically after edits to a module, when the match count is small enough to be fast.
+## Batch-Mode Reproducibility
+
+Tests must pass under `emacs --batch` — the headless, scriptable path that CI and the `make` targets use. `--batch` is the source of truth, not an interactive session.
+
+- Don't depend on interactive-session state: window configuration, frame parameters, `this-command`, minibuffer activity, or anything a running editor accumulates. A test that passes in a live Emacs but fails (or hangs) under `--batch` is broken.
+- Don't block on a prompt. `--batch` has no one to answer `y-or-n-p` or `read-string`, so an unmocked prompt either errors or stalls the run. Test the internal directly (see *Interactive vs Internal* above) or `cl-letf` the prompt.
+- Keep tests deterministic: no reliance on test execution order, wall-clock time (mock `current-time`), or environment that differs between the developer's machine and CI.
+
+## Isolating Emacs State
+
+A test must not read or mutate the developer's real Emacs config. Bind a throwaway environment so the run is hermetic regardless of who runs it.
+
+- Bind `user-emacs-directory` (and, when relevant, `user-init-file`) to a temp directory so package state, `custom-file` writes, caches, and auto-save files land in the sandbox rather than the developer's `~/.emacs.d`.
+- Control `load-path` explicitly. Add only the project's own directories; don't lean on whatever happens to be installed in the developer's session.
+- Depend only on the project's declared dependencies. A test that passes because some unrelated package is installed on this machine will fail on a clean checkout or in CI.
+
+```elisp
+(ert-deftest test-foo-writes-to-sandbox ()
+ "Normal: writes under an isolated user-emacs-directory."
+ (let* ((sandbox (make-temp-file "elisp-test-" t))
+ (user-emacs-directory (file-name-as-directory sandbox)))
+ (unwind-protect
+ (progn
+ (cj/--foo)
+ (should (file-exists-p (expand-file-name "foo.cache" user-emacs-directory))))
+ (delete-directory sandbox t))))
+```
+
+## Byte-Compile and Native-Comp Warnings
+
+A clean compile is part of green. Byte-compile warnings (free variables, wrong argument counts, unused lexical bindings, obsolete-function calls) flag real defects, so treat them as failures rather than noise.
+
+This can be enforced in the test run by binding `byte-compile-error-on-warn` to `t` and compiling the modules under test, optionally extending to native compilation where `native-comp-async-report-warnings-errors` is available.
+
+Keep the native-comp half conditional. Native compilation exists only on builds with the `native-compile` feature (Emacs 28+ compiled with it); older or non-native builds lack `native-comp-*` variables and `native-compile` entirely. Gate on the feature so the suite still runs everywhere:
+
+```elisp
+(when (and (fboundp 'native-comp-available-p) (native-comp-available-p))
+ ;; native-comp-specific checks here
+ )
+```
+
+Make the warnings-as-errors gate opt-in or version-aware rather than absolute — a warning that's clean on the project's pinned Emacs may differ across versions, and a hard failure on every build penalizes contributors on a different Emacs than the maintainer's.
+
## Anti-Patterns
- Hardcoded timestamps — generate relative to `current-time` or mock
diff --git a/.claude/rules/elisp.md b/.claude/rules/elisp.md
index e641058..ea9bdc2 100644
--- a/.claude/rules/elisp.md
+++ b/.claude/rules/elisp.md
@@ -72,4 +72,4 @@ Then `(require 'foo-config)` in `init.el` (or a config aggregator).
- A PostToolUse hook runs `check-parens` and `byte-compile-file` on every `.el` save
- If it blocks, read the error — don't retry blindly
-- Prefer Write over repeated Edits for nontrivial new code; incremental edits accumulate subtle paren mismatches
+- Edit cohesively, then verify parens/byte-compile right away. For nontrivial Elisp, land a function as one complete, coherent change rather than dribbling it in over many tiny partial edits — incremental fragments accumulate subtle paren mismatches. Run the paren-balance and byte-compile checks immediately after editing, whatever editing mechanism the environment uses.