diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | docs/design/2026-06-23-install-lang-claude-md-gap.org | 31 | ||||
| -rw-r--r-- | languages/default-CLAUDE.md | 64 | ||||
| -rwxr-xr-x | scripts/install-lang.sh | 19 | ||||
| -rwxr-xr-x | scripts/lint.sh | 3 | ||||
| -rw-r--r-- | scripts/tests/install-lang.bats | 29 | ||||
| -rw-r--r-- | todo.org | 6 |
7 files changed, 150 insertions, 4 deletions
@@ -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" @@ -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] |
