aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--docs/design/2026-06-23-install-lang-claude-md-gap.org31
-rw-r--r--languages/default-CLAUDE.md64
-rwxr-xr-xscripts/install-lang.sh19
-rwxr-xr-xscripts/lint.sh3
-rw-r--r--scripts/tests/install-lang.bats29
-rw-r--r--todo.org6
7 files changed, 150 insertions, 4 deletions
diff --git a/Makefile b/Makefile
index 450dc1a..294e99c 100644
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,7 @@ HOOKS := $(wildcard hooks/*.sh hooks/*.py)
OPTIN_HOOKS := hooks/destructive-bash-confirm.py
DEFAULT_HOOKS := $(filter-out $(OPTIN_HOOKS),$(HOOKS))
CLAUDE_CONFIG := $(wildcard .claude/*.json) $(wildcard .claude/.*.json) $(wildcard .claude/*.sh)
-LANGUAGES := $(notdir $(wildcard languages/*))
+LANGUAGES := $(notdir $(patsubst %/,%,$(wildcard languages/*/)))
TEAMS := $(notdir $(wildcard teams/*))
PDFTOOLS_VENV ?= $(HOME)/.local/venvs/pdftools
diff --git a/docs/design/2026-06-23-install-lang-claude-md-gap.org b/docs/design/2026-06-23-install-lang-claude-md-gap.org
new file mode 100644
index 0000000..cf16256
--- /dev/null
+++ b/docs/design/2026-06-23-install-lang-claude-md-gap.org
@@ -0,0 +1,31 @@
+#+TITLE: install-lang CLAUDE.md gap — non-elisp projects get a wrong or missing CLAUDE.md
+#+DATE: 2026-06-23
+
+Surfaced while running the archangel .ai/ conversion you sent (the 2026-06-20 handoff). archangel is a bash project — 437 =.sh= files; the only =.el=/=.py= in the tree are under =work/x86_64/airootfs/= (archiso staging, not source). Per your handoff I installed both elisp and python bundles. The result exposed two coupled issues that block CLAUDE.md consistency across projects.
+
+* The two findings
+
+1. *Only the elisp bundle ships a CLAUDE.md template.* =languages/elisp/CLAUDE.md= exists; =languages/python/=, =go/=, =typescript/= ship none. =install-lang.sh= guards on =[ -f "$SRC/CLAUDE.md" ]=, so a bundle without a template silently contributes nothing — no line printed, no file seeded.
+
+2. *No shell/bash bundle exists* (only elisp, go, python, typescript). archangel and archsetup are bash projects with no bundle that fits.
+
+* The consequence
+
+- Install python (or go/ts) alone → project gets *no* CLAUDE.md.
+- Install elisp + anything → project gets the elisp stub, whose first line is "Elisp project." Because install-lang seeds CLAUDE.md only on first install and never overwrites without FORCE=1, install order doesn't matter — the elisp template is the only one available, so it always wins.
+- Net: archangel, a bash project, ended up with a CLAUDE.md headed "Elisp project." An inaccurate CLAUDE.md is worse than none — it mislabels the project for every future session.
+
+* Proposals (rulesets' call)
+
+1. *Add a shell/bash language bundle.* This is the real gap for archangel/archsetup and any other shell-heavy project.
+2. *Give every bundle its own CLAUDE.md template*, or ship a language-neutral default so install-lang always seeds an accurate (or at least non-misleading) header. A stub that says "<LANG> project — customize this" is only safe when the bundle actually matches the language.
+3. *Consider the multi-bundle case* — when a project installs more than one bundle, the CLAUDE.md "Project" line shouldn't hardcode a single language picked by which template happened to exist.
+
+* Companion files to reconcile
+
+- =scripts/install-lang.sh= — the seed-on-first-install / no-overwrite logic (sections 3 and 3b) is correct; the gap is the missing templates and missing bash bundle, not this logic.
+- =languages/elisp/CLAUDE.md= — the only template today; pattern to replicate per language.
+
+* What archangel did locally (stopgap)
+
+Installed both bundles as you asked; the generic =.claude/rules/= and gitignore hygiene are the real gain there. I flagged the elisp-stub mismatch to Craig and offered to hand-write archangel's CLAUDE.md as a bash ISO-build project. That local fix doesn't address the cross-project pattern — hence this note.
diff --git a/languages/default-CLAUDE.md b/languages/default-CLAUDE.md
new file mode 100644
index 0000000..a5b6925
--- /dev/null
+++ b/languages/default-CLAUDE.md
@@ -0,0 +1,64 @@
+# CLAUDE.md
+
+## Project
+
+Describe this project: what it is, its layout, and its conventions. This
+default was seeded by `install-lang` because the installed bundle ships no
+language-specific CLAUDE.md — it deliberately names no language, so replace
+this section with an accurate description rather than inheriting a wrong one.
+
+**Typical layout (edit to match):**
+- entry points — the file(s) that run first
+- source directories — where the real code lives
+- tests — beside the code, or under a `tests/` tree
+
+## Build & Test Commands
+
+If the project has a Makefile, document its targets here. A common shape:
+
+```bash
+make test # run the test suite
+make lint # run the linter / formatter check
+make build # build the project
+```
+
+Otherwise, document the direct commands a contributor runs to test and build.
+
+## Language Rules
+
+Shared rules live in `.claude/rules/` (installed from `claude-rules/`):
+- `commits.md` — author identity, no AI attribution, message format
+- `testing.md` — TDD discipline and test-quality standards
+- `verification.md` — verify-before-claim-done discipline
+
+If a language bundle was installed, its own rule files (code style, testing
+conventions) sit alongside these in `.claude/rules/`.
+
+## Git Workflow
+
+Commit conventions: see `.claude/rules/commits.md`.
+
+If a `githooks/` pre-commit hook was installed, activate it on a fresh clone
+with `git config core.hooksPath githooks`.
+
+## Problem-Solving Approach
+
+Investigate before fixing. When diagnosing a bug:
+1. Read the relevant code and trace what actually happens
+2. Identify the root cause, not a surface symptom
+3. Write a failing test that captures the correct behavior
+4. Fix, then re-run tests
+
+## Testing Discipline
+
+TDD is the default: write a failing test before any implementation. If you
+can't write the test, you don't yet understand the change. Details in
+`.claude/rules/testing.md`.
+
+## What Not to Do
+
+- Don't add features beyond what was asked
+- Don't refactor surrounding code when fixing a bug
+- Don't add comments to code you didn't change
+- Don't create abstractions for one-time operations
+- Don't commit credentials or API keys
diff --git a/scripts/install-lang.sh b/scripts/install-lang.sh
index 0fc9ea8..2f38fcd 100755
--- a/scripts/install-lang.sh
+++ b/scripts/install-lang.sh
@@ -66,13 +66,26 @@ if [ -d "$SRC/githooks" ]; then
fi
fi
-# 3. CLAUDE.md — seed on first install, don't overwrite unless FORCE=1
+# 3. CLAUDE.md — seed on first install, don't overwrite unless FORCE=1.
+# Prefer the bundle's own template; fall back to the language-neutral
+# default so a bundle that ships none still seeds an accurate, non-
+# mislabeling header instead of nothing. The default names no language,
+# so multi-bundle and wrong-bundle installs don't inherit a false header.
+CLAUDE_SRC=""
+CLAUDE_KIND=""
if [ -f "$SRC/CLAUDE.md" ]; then
+ CLAUDE_SRC="$SRC/CLAUDE.md"
+ CLAUDE_KIND="$LANG"
+elif [ -f "$REPO_ROOT/languages/default-CLAUDE.md" ]; then
+ CLAUDE_SRC="$REPO_ROOT/languages/default-CLAUDE.md"
+ CLAUDE_KIND="language-neutral default"
+fi
+if [ -n "$CLAUDE_SRC" ]; then
if [ -f "$PROJECT/CLAUDE.md" ] && [ "$FORCE" != "1" ]; then
echo " [skip] CLAUDE.md already exists (use FORCE=1 to overwrite)"
else
- cp "$SRC/CLAUDE.md" "$PROJECT/CLAUDE.md"
- echo " [ok] CLAUDE.md installed"
+ cp "$CLAUDE_SRC" "$PROJECT/CLAUDE.md"
+ echo " [ok] CLAUDE.md installed ($CLAUDE_KIND)"
fi
fi
diff --git a/scripts/lint.sh b/scripts/lint.sh
index ae30aa5..61a27a1 100755
--- a/scripts/lint.sh
+++ b/scripts/lint.sh
@@ -99,6 +99,9 @@ for claude_md in languages/*/CLAUDE.md; do
check_md_heading "$claude_md"
done
+# Language-neutral default CLAUDE.md (install-lang's fallback when a bundle ships none)
+[ -f languages/default-CLAUDE.md ] && check_md_heading languages/default-CLAUDE.md
+
# Hook scripts
for h in languages/*/claude/hooks/*.sh languages/*/githooks/*; do
[ -f "$h" ] || continue
diff --git a/scripts/tests/install-lang.bats b/scripts/tests/install-lang.bats
index ecfbe01..f790fbf 100644
--- a/scripts/tests/install-lang.bats
+++ b/scripts/tests/install-lang.bats
@@ -79,6 +79,35 @@ teardown() {
grep -qxF "coverage/" "$PROJECT/.gitignore"
}
+@test "install-lang python: seeds the language-neutral default CLAUDE.md when the bundle ships none" {
+ run bash "$INSTALL_LANG" python "$PROJECT"
+
+ [ "$status" -eq 0 ]
+ [ -f "$PROJECT/CLAUDE.md" ]
+ # The default names no language, so it can't mislabel a python (or bash, or
+ # multi-bundle) project the way inheriting elisp's "Elisp project" header did.
+ ! grep -qi "Python project" "$PROJECT/CLAUDE.md"
+ ! grep -qi "Elisp project" "$PROJECT/CLAUDE.md"
+ grep -qF "names no language" "$PROJECT/CLAUDE.md"
+ [[ "$output" == *"language-neutral default"* ]]
+}
+
+@test "install-lang elisp: seeds the bundle's own CLAUDE.md, not the default" {
+ run bash "$INSTALL_LANG" elisp "$PROJECT"
+
+ [ "$status" -eq 0 ]
+ grep -qF "Elisp project." "$PROJECT/CLAUDE.md"
+ [[ "$output" == *"CLAUDE.md installed (elisp)"* ]]
+}
+
+@test "install-lang python: does not overwrite an existing CLAUDE.md without FORCE" {
+ echo "MY OWN CLAUDE" > "$PROJECT/CLAUDE.md"
+ run bash "$INSTALL_LANG" python "$PROJECT"
+
+ [ "$status" -eq 0 ]
+ grep -qxF "MY OWN CLAUDE" "$PROJECT/CLAUDE.md"
+}
+
@test "install-lang go: full bundle lands (rules, hook, settings, githook, CLAUDE.md, coverage)" {
run bash "$INSTALL_LANG" go "$PROJECT"
diff --git a/todo.org b/todo.org
index 0312f32..c1b76b0 100644
--- a/todo.org
+++ b/todo.org
@@ -34,6 +34,12 @@ Tags are assigned and refreshed by =task-audit=; =task-review= keeps them honest
* Rulesets Open Work
+** TODO [#C] Bash/shell language bundle :feature:
+:PROPERTIES:
+:CREATED: [2026-06-23 Tue]
+:END:
+No =languages/= bundle fits a shell-heavy project. archangel (437 =.sh= files) and archsetup are bash projects with nothing that matches; installing elisp/python gives them the wrong language rules. Build a =languages/bash/= bundle on the elisp/go pattern: =claude/rules/bash.md= (style — =set -euo pipefail=, quoting, =[[ ]]=, trap/cleanup) + =bash-testing.md= (bats conventions), a PostToolUse validate hook (=shellcheck= on edited =.sh=), a =githooks/pre-commit= running shellcheck on staged shell files, =settings.json= wiring, =gitignore-add.txt=, and its own =CLAUDE.md= headed "Bash/shell project." Urgency dropped 2026-06-23: install-lang now seeds the language-neutral default CLAUDE.md when a bundle ships none, so a bash project no longer gets a mislabeled "Elisp project" header — the bundle is now the accurate-rules win, not a mislabel fix. From archangel 2026-06-23 ([[file:docs/design/2026-06-23-install-lang-claude-md-gap.org][handoff]]).
+
** TODO [#B] Anki deck name from #+TITLE :bug:
:PROPERTIES:
:CREATED: [2026-06-22 Mon]