aboutsummaryrefslogtreecommitdiff
path: root/.claude/rules
diff options
context:
space:
mode:
Diffstat (limited to '.claude/rules')
l---------.claude/rules/commits.md1
-rw-r--r--.claude/rules/elisp-testing.md107
-rw-r--r--.claude/rules/elisp.md75
l---------.claude/rules/testing.md1
l---------.claude/rules/verification.md1
5 files changed, 0 insertions, 185 deletions
diff --git a/.claude/rules/commits.md b/.claude/rules/commits.md
deleted file mode 120000
index 3e746ed..0000000
--- a/.claude/rules/commits.md
+++ /dev/null
@@ -1 +0,0 @@
-/home/cjennings/code/rulesets/claude-rules/commits.md \ No newline at end of file
diff --git a/.claude/rules/elisp-testing.md b/.claude/rules/elisp-testing.md
deleted file mode 100644
index b5def78..0000000
--- a/.claude/rules/elisp-testing.md
+++ /dev/null
@@ -1,107 +0,0 @@
-# Elisp Testing Rules
-
-Applies to: `**/tests/*.el`
-
-Implements the core principles from `testing.md`. All rules there apply here —
-this file covers Elisp-specific patterns.
-
-## Framework: ERT
-
-Use `ert-deftest` for all tests. One test = one scenario.
-
-## File Layout
-
-- `tests/test-<module>.el` — tests for `<module>.el`
-- `tests/test-<module>--<helper>.el` — tests for a specific private helper (matches `<module>--<helper>` function naming)
-- `tests/testutil-<module>.el` — fixtures and mocks scoped to one module
-- `tests/testutil-*.el` — cross-module helpers (shared fixtures, generic mocks, filesystem helpers); name them for what they help with
-
-Tests must `(require 'module-name)` before the testutil file that stubs its internals, unless documented otherwise. Order matters — a testutil that defines a stub can be shadowed by a later `require` of the real module.
-
-## Test Naming
-
-```elisp
-(ert-deftest test-<module>-<function>-<scenario> ()
- "Normal/Boundary/Error: brief description."
- ...)
-```
-
-Put the category (Normal, Boundary, Error) in the docstring so the category is grep-able.
-
-## Required Coverage
-
-Every non-trivial function needs at least:
-- One **Normal** case (happy path)
-- One **Boundary** case (empty, nil, min, max, unicode, long string)
-- One **Error** case (invalid input, missing resource, failure mode)
-
-Missing a category is a test gap. If three cases look near-identical, parametrize with a loop or `dolist` rather than copy-pasting.
-
-## 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.
-
-For untested code, write a **characterization test** that captures current behavior before you change anything. It becomes the safety net for the refactor.
-
-## Interactive vs Internal — Split for Testability
-
-When a function mixes business logic with user interaction, split it:
-
-- **Internal** (`cj/--foo`) — pure logic. All parameters explicit. No prompts,
- no UI. Deterministic and trivially testable.
-- **Interactive wrapper** (`cj/foo`) — thin layer that reads user input and
- delegates to the internal.
-
-```elisp
-(defun cj/--move-buffer-and-file (dir &optional ok-if-exists)
- "Move the current buffer's file into DIR. Overwrite if OK-IF-EXISTS."
- ...)
-
-(defun cj/move-buffer-and-file ()
- "Interactive wrapper: prompt for DIR, delegate."
- (interactive)
- (let ((dir (read-directory-name "Move to: ")))
- (cj/--move-buffer-and-file dir)))
-```
-
-Test the internal directly with parameter values — no `cl-letf` on
-`read-directory-name`, `yes-or-no-p`, etc. The wrapper gets a smoke test or
-nothing — Emacs already tests its own prompts. The internal also becomes
-reusable by other Elisp code without triggering UI.
-
-## Mocking
-
-Mock at boundaries:
-- Shell: `cl-letf` on `shell-command`, `shell-command-to-string`, `call-process`
-- File I/O when tests shouldn't touch disk
-- Network: URL retrievers, HTTP clients
-- Time: `cl-letf` on `current-time`, `format-time-string`
-
-Never mock:
-- The code under test
-- Core Emacs primitives (buffer ops, string ops, lists)
-- Your own domain logic — restructure it to be testable instead
-
-## Idioms
-
-- `cl-letf` for scoped overrides (self-cleaning)
-- `with-temp-buffer` for buffer manipulation tests
-- `make-temp-file` with `.el` suffix for on-disk fixtures
-- Tests must run in any order; no shared mutable state
-
-## Running Tests
-
-```bash
-make test # All
-make test-file FILE=tests/test-foo.el # One file
-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.
-
-## Anti-Patterns
-
-- Hardcoded timestamps — generate relative to `current-time` or mock
-- Testing implementation details (private storage structure) instead of behavior
-- Mocking the thing you're testing
-- Skipping a failing test without an issue to track it
diff --git a/.claude/rules/elisp.md b/.claude/rules/elisp.md
deleted file mode 100644
index e641058..0000000
--- a/.claude/rules/elisp.md
+++ /dev/null
@@ -1,75 +0,0 @@
-# Elisp / Emacs Rules
-
-Applies to: `**/*.el`
-
-## Style
-
-- 2-space indent, no tabs
-- Hyphen-case for identifiers: `cj/do-thing`, not `cj/doThing`
-- Naming prefixes:
- - `cj/name` — user-facing functions and commands (bound to keys, called from init)
- - `cj/--name` — private helpers (double-dash signals "internal")
- - `<module>/name` — module-scoped where appropriate (e.g., `calendar-sync/parse-ics`)
-- File header: `;;; foo-config.el --- brief description -*- lexical-binding: t -*-`
-- `(provide 'foo-config)` at the bottom of every module
-- `lexical-binding: t` is mandatory — no file without it
-
-## Function Design
-
-- Keep functions under 15 lines where possible
-- One responsibility per function
-- Extract helpers instead of nesting deeply — 5+ levels of nesting is a refactor signal
-- Prefer named helpers over lambdas for anything nontrivial
-- No premature abstraction — three similar lines beats a clever macro
-
-Small functions are the single strongest defense against paren errors. Deeply nested code is where AI and humans both fail.
-
-## Requires and Loading
-
-- Every `(require 'foo)` must correspond to a loadable file on the load-path
-- Byte-compile warnings about free variables usually indicate a missing `require` or a typo in a symbol name — read them
-- Use `use-package` for external (MELPA/ELPA) packages
-- Use plain `(require 'foo-config)` for internal modules
-- For optional features, `(when (require 'foo nil t) ...)` degrades gracefully if absent
-
-## Lexical-Binding Traps
-
-- `(boundp 'x)` where `x` is a lexical variable always returns nil. Bind with `defvar` at top level if you need `boundp` to work, or use the value directly.
-- `setq` on an undeclared free variable is a warning — use `let` for locals or `defvar` for module-level state
-- Closures capture by reference. Avoid capturing mutating loop variables in nested defuns.
-
-## Regex Gotchas
-
-- `\s` is NOT whitespace in Emacs regex. Use `[ \t]` or `\\s-` (syntax class).
-- `^` in `string-match` matches after `\n` OR at position 0 — use `(= (match-beginning 0) start)` for positional checks when that matters.
-- `replace-regexp-in-string` interprets backslashes in the replacement. Pass `t t` (FIXEDCASE LITERAL) when the replacement contains literal backslashes.
-
-## Keybindings
-
-- `keymap-global-set` for global; `keymap-set KEYMAP ...` for mode-local
-- Group module-specific bindings inside the module's file
-- Autoload cookies (`;;;###autoload`) don't activate through plain `(require ...)` — use the form directly, not an autoloaded wrapper
-
-## Module Template
-
-```elisp
-;;; foo-config.el --- Foo feature configuration -*- lexical-binding: t -*-
-
-;;; Commentary:
-;; One-line description.
-
-;;; Code:
-
-;; ... code ...
-
-(provide 'foo-config)
-;;; foo-config.el ends here
-```
-
-Then `(require 'foo-config)` in `init.el` (or a config aggregator).
-
-## Editing Workflow
-
-- 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
diff --git a/.claude/rules/testing.md b/.claude/rules/testing.md
deleted file mode 120000
index 23c3a14..0000000
--- a/.claude/rules/testing.md
+++ /dev/null
@@ -1 +0,0 @@
-/home/cjennings/code/rulesets/claude-rules/testing.md \ No newline at end of file
diff --git a/.claude/rules/verification.md b/.claude/rules/verification.md
deleted file mode 120000
index ac32768..0000000
--- a/.claude/rules/verification.md
+++ /dev/null
@@ -1 +0,0 @@
-/home/cjennings/code/rulesets/claude-rules/verification.md \ No newline at end of file