aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-28 09:16:39 -0500
committerCraig Jennings <c@cjennings.net>2026-05-28 09:16:39 -0500
commit5c0c7a6f213609f5be8258f07b763201ad182876 (patch)
tree9d7c0d54cde859607fb1910b57395f4408900906
parentbdf755d33aa6a207a538c85f18e38cc03f14e529 (diff)
downloadrulesets-5c0c7a6f213609f5be8258f07b763201ad182876.tar.gz
rulesets-5c0c7a6f213609f5be8258f07b763201ad182876.zip
fix(sync-check): ignore generated python and elisp artifacts
Pre-commit caught a false-positive on its first real-world run. The .ai/scripts/__pycache__ directory created by make test (pytest writes .pyc and .pytest_cache, emacs writes .elc) was flagged as drift against the canonical's clean tree. Added matching --exclude patterns to the diff -rq check and the --fix rsync calls. Patterns: __pycache__, *.pyc, *.pyo, .pytest_cache, *.elc. The --fix excludes prevent rsync's --delete from wiping the mirror's __pycache__ when the canonical has none. Four new bats tests cover the exclusions. All 12 pass.
-rw-r--r--claude-templates/Makefile39
-rwxr-xr-xscripts/sync-check.sh28
-rw-r--r--scripts/tests/sync-check.bats30
-rw-r--r--todo.org3
4 files changed, 56 insertions, 44 deletions
diff --git a/claude-templates/Makefile b/claude-templates/Makefile
deleted file mode 100644
index e097cc2..0000000
--- a/claude-templates/Makefile
+++ /dev/null
@@ -1,39 +0,0 @@
-.DEFAULT_GOAL := help
-
-PREFIX ?= $(HOME)/.local/bin
-SRC := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
-
-.PHONY: help install uninstall list test-scripts
-
-help:
- @printf 'claude-templates — session launcher + project seed\n\n'
- @printf 'Targets:\n'
- @printf ' make install symlink bin/ai into %s\n' "$(PREFIX)"
- @printf ' make uninstall remove the symlink\n'
- @printf ' make list show installed symlink\n'
- @printf ' make test-scripts run pytest + ERT suites under .ai/scripts/tests/\n'
-
-install:
- @mkdir -p $(PREFIX)
- @chmod +x $(SRC)/bin/ai
- @ln -sfn $(SRC)/bin/ai $(PREFIX)/ai
- @printf 'installed %s/ai -> %s/bin/ai\n' "$(PREFIX)" "$(SRC)"
-
-uninstall:
- @if [ -L $(PREFIX)/ai ]; then \
- rm $(PREFIX)/ai; \
- printf 'removed %s/ai\n' "$(PREFIX)"; \
- else \
- printf '%s/ai is not a symlink; nothing to remove\n' "$(PREFIX)"; \
- fi
-
-list:
- @ls -la $(PREFIX)/ai 2>/dev/null || printf 'not installed\n'
-
-test-scripts:
- @cd $(SRC)/.ai/scripts/tests && python -m pytest
- @set -e; for f in $(SRC)/.ai/scripts/tests/test-*.el; do \
- [ -e "$$f" ] || continue; \
- echo "ert: $$(basename "$$f")"; \
- emacs --batch -q -L $(SRC)/.ai/scripts -l ert -l "$$f" -f ert-run-tests-batch-and-exit; \
- done
diff --git a/scripts/sync-check.sh b/scripts/sync-check.sh
index 8fb9ab0..cc7bc62 100755
--- a/scripts/sync-check.sh
+++ b/scripts/sync-check.sh
@@ -35,12 +35,23 @@ fi
paths=(protocols.org workflows scripts)
+# Generated artifacts that should never count as drift. These appear on the
+# mirror side when tests run (pytest writes .pyc and .pytest_cache; emacs
+# writes .elc) but aren't part of the canonical source.
+exclude_args=(
+ --exclude='__pycache__'
+ --exclude='*.pyc'
+ --exclude='*.pyo'
+ --exclude='.pytest_cache'
+ --exclude='*.elc'
+)
+
check_drift() {
local drift=0
for relpath in "${paths[@]}"; do
- if ! diff -rq "$canonical/$relpath" "$mirror/$relpath" >/dev/null 2>&1; then
+ if ! diff -rq "${exclude_args[@]}" "$canonical/$relpath" "$mirror/$relpath" >/dev/null 2>&1; then
echo "drift: claude-templates/.ai/$relpath ↔ .ai/$relpath" >&2
- diff -rq "$canonical/$relpath" "$mirror/$relpath" 2>&1 | head -20 >&2
+ diff -rq "${exclude_args[@]}" "$canonical/$relpath" "$mirror/$relpath" 2>&1 | head -20 >&2
drift=1
fi
done
@@ -54,9 +65,18 @@ fi
if [ "${1:-}" = "--fix" ]; then
echo "" >&2
echo "sync-check --fix: syncing canonical → mirror..." >&2
+ # Same exclude patterns as the diff so generated files in the mirror
+ # aren't wiped by --delete when canonical has no counterpart.
+ rsync_excludes=(
+ --exclude='__pycache__'
+ --exclude='*.pyc'
+ --exclude='*.pyo'
+ --exclude='.pytest_cache'
+ --exclude='*.elc'
+ )
rsync -a "$canonical/protocols.org" "$mirror/protocols.org"
- rsync -a --delete "$canonical/workflows/" "$mirror/workflows/"
- rsync -a --delete "$canonical/scripts/" "$mirror/scripts/"
+ rsync -a --delete "${rsync_excludes[@]}" "$canonical/workflows/" "$mirror/workflows/"
+ rsync -a --delete "${rsync_excludes[@]}" "$canonical/scripts/" "$mirror/scripts/"
if check_drift; then
echo "sync-check --fix: resolved." >&2
echo "Re-stage the synced files and retry the commit." >&2
diff --git a/scripts/tests/sync-check.bats b/scripts/tests/sync-check.bats
index df775b3..17c9189 100644
--- a/scripts/tests/sync-check.bats
+++ b/scripts/tests/sync-check.bats
@@ -84,3 +84,33 @@ setup() {
[ "$status" -eq 2 ]
[[ "$output" == *"not inside a git checkout"* ]]
}
+
+@test "generated Python artifacts in mirror don't count as drift" {
+ mkdir -p .ai/scripts/__pycache__
+ touch .ai/scripts/__pycache__/foo.cpython-314.pyc
+ touch .ai/scripts/some.pyc
+ run scripts/sync-check.sh
+ [ "$status" -eq 0 ]
+}
+
+@test "compiled elisp artifacts in mirror don't count as drift" {
+ touch .ai/scripts/foo.elc
+ run scripts/sync-check.sh
+ [ "$status" -eq 0 ]
+}
+
+@test "pytest cache in mirror doesn't count as drift" {
+ mkdir -p .ai/scripts/.pytest_cache
+ touch .ai/scripts/.pytest_cache/CACHEDIR.TAG
+ run scripts/sync-check.sh
+ [ "$status" -eq 0 ]
+}
+
+@test "--fix preserves generated artifacts in mirror" {
+ mkdir -p .ai/scripts/__pycache__
+ touch .ai/scripts/__pycache__/foo.cpython-314.pyc
+ echo "drift" > .ai/protocols.org
+ run scripts/sync-check.sh --fix
+ [ "$status" -eq 0 ]
+ [ -f .ai/scripts/__pycache__/foo.cpython-314.pyc ]
+}
diff --git a/todo.org b/todo.org
index 0b76681..9df8049 100644
--- a/todo.org
+++ b/todo.org
@@ -1125,7 +1125,8 @@ The four canonical rules (=commits=, =testing=, =verification=, =subagents=) are
The Elisp pair is the most suspicious — three repos using essentially the same rules. Audit: diff these across the projects, check for drift, then decide whether to canonicalize them under =~/code/rulesets/claude-rules/languages/<lang>/= and symlink, or leave them as project-local.
-** TODO [#C] Consolidate =claude-templates/Makefile= after fold :chore:quick:solo:
+** DONE [#C] Consolidate =claude-templates/Makefile= after fold :chore:quick:solo:
+CLOSED: [2026-05-28 Thu]
:PROPERTIES:
:LAST_REVIEWED: 2026-05-28
:END: