aboutsummaryrefslogtreecommitdiff
path: root/scripts/tests/install-lang-collision.bats
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-07-23 20:48:44 -0500
committerCraig Jennings <c@cjennings.net>2026-07-23 20:48:44 -0500
commitc238afbd4150ef53737f16e7dd84a565d2838ecc (patch)
tree588a25dc318cb854c57d8146639d400674baed18 /scripts/tests/install-lang-collision.bats
parent10ea44b6de3be1872f7f0bd4501ccf3878105bc4 (diff)
downloadrulesets-c238afbd4150ef53737f16e7dd84a565d2838ecc.tar.gz
rulesets-c238afbd4150ef53737f16e7dd84a565d2838ecc.zip
feat(languages): ship the missing python and typescript hooks
The python and typescript bundles carried rules and a coverage script but no pre-commit hook, so any project installing one got no credential scan on commit. Both now ship all four components the README documents: the shared secret scan, a validate-on-edit hook, settings wiring, and a seed CLAUDE.md. Verified against a real repo: a commit carrying an AWS key is refused. install-lang now warns when a bundle is missing a documented component. That's the half that keeps this from recurring. Whoever adds the sixth bundle will forget something too, and today the installer prints success either way. Two things fell out of the build. node --check is unusable on TypeScript: it ignores --experimental-strip-types, so it rejects valid TS and accepts broken TS. The hook uses tsc filtered to syntactic diagnostics instead. And completing the bundles means every pair now collides on settings.json and pre-commit, so no two compose without FORCE=1. The earlier "bundles already compose" reading rested on these two being incomplete. Filed for a real decision; clock-panel is the project that wants it. Also stamps :LAST_REVIEWED: at task creation, with a lint checker to catch misses. Writing a task is reviewing it, so leaving the stamp off pushed every fresh task to the top of the next review batch to be re-derived by someone with less context than its author had. Tonight's sweep sent the staleness count from 13 to 22 while the list got more accurate.
Diffstat (limited to 'scripts/tests/install-lang-collision.bats')
-rw-r--r--scripts/tests/install-lang-collision.bats53
1 files changed, 42 insertions, 11 deletions
diff --git a/scripts/tests/install-lang-collision.bats b/scripts/tests/install-lang-collision.bats
index 36abb5b..e03e136 100644
--- a/scripts/tests/install-lang-collision.bats
+++ b/scripts/tests/install-lang-collision.bats
@@ -5,10 +5,14 @@
# (appended, deduped) and CLAUDE.md is seed-only, so both compose across
# bundles. Three do not:
#
-# claude/settings.json elisp, bash, go — cp -rT, silently overwritten
-# githooks/pre-commit elisp, bash, go — cp -rT, silently overwritten
+# claude/settings.json all 5 bundles — cp -rT, silently overwritten
+# githooks/pre-commit all 5 bundles — cp -rT, silently overwritten
# coverage-makefile.txt 4 bundles — [skip]ped, fragment dropped
#
+# The first two read "elisp, bash, go" until 2026-07-23, when python and
+# typescript gained the components they had been missing. The consequence is
+# that no two shipping bundles compose any more; see the polyglot task.
+#
# Installing a second bundle used to replace the first's settings.json and
# pre-commit while printing [ok], so a project could lose its paren check or
# secret scan and read the output as success. The guard refuses instead, naming
@@ -68,8 +72,11 @@ teardown() {
}
@test "install-lang: bundles colliding only on coverage-makefile.txt are refused too" {
- # python and typescript ship no settings.json or githooks, but both ship a
- # coverage fragment. The second one's used to be silently dropped.
+ # Named for the pre-2026-07-23 reason: python and typescript shipped no
+ # settings.json or githooks, so the coverage fragment was their only overlap
+ # and the second one's used to be silently dropped. They now overlap on all
+ # three, so this exercises the multi-file refusal path — the coverage
+ # fragment must still be named among them.
bash "$INSTALL" python "$PROJ"
run bash "$INSTALL" typescript "$PROJ"
[ "$status" -ne 0 ] || { echo "typescript installed over python's coverage fragment"; return 1; }
@@ -77,17 +84,41 @@ teardown() {
}
@test "install-lang: two bundles that share no overwritten file install together" {
- # bash ships settings.json + githooks and no coverage fragment; python ships
- # only the coverage fragment. Nothing overlaps, so the guard must stay out of
- # the way. This is the path where a false refusal would be easiest to
- # introduce: the bundle IS detected, and only the empty file-list stops it.
+ # The guard must stay out of the way when nothing overlaps. This is the path
+ # where a false refusal would be easiest to introduce: the bundle IS
+ # detected, and only the empty file-list stops it.
+ #
+ # This used to be tested with bash + python, which composed because python
+ # shipped no settings.json and no githooks. That was the incomplete-bundle
+ # bug (fixed 2026-07-23), not a design property — so no pair of *shipping*
+ # bundles is non-colliding any more, and the case needs a synthetic bundle.
+ # See the polyglot task in todo.org: whether every pair now colliding is
+ # acceptable is an open question, but the guard's own no-false-refusal
+ # behavior is not, and stays pinned here.
+ fake="${BATS_TEST_DIRNAME}/../../languages/zz-test-rulesonly"
+ mkdir -p "$fake/claude/rules"
+ printf '# rule\n' > "$fake/claude/rules/zz-testing.md"
+
bash "$INSTALL" bash "$PROJ"
- run bash "$INSTALL" python "$PROJ"
+ run bash "$INSTALL" zz-test-rulesonly "$PROJ"
+ rm -rf "$fake"
+
[ "$status" -eq 0 ] || { echo "guard falsely refused a non-colliding pair: $output"; return 1; }
- [ -f "$PROJ/coverage-makefile.txt" ] || { echo "python's coverage fragment did not land"; return 1; }
# bash's config survives untouched.
grep -q 'validate-bash.sh' "$PROJ/.claude/settings.json"
- [ -f "$PROJ/.claude/rules/bash.md" ] && [ -f "$PROJ/.claude/rules/python-testing.md" ]
+ [ -f "$PROJ/.claude/rules/bash.md" ] && [ -f "$PROJ/.claude/rules/zz-testing.md" ]
+}
+
+@test "install-lang: completing python made it collide with bash (documents the tradeoff)" {
+ # Pins the consequence of the 2026-07-23 bundle completion so it can't drift
+ # back unnoticed: python now ships settings.json + githooks/pre-commit, so
+ # bash + python is a genuine overwrite conflict and the guard refuses it.
+ # Whether that's the right trade is Craig's open call; that it IS the current
+ # behavior is what this test records.
+ bash "$INSTALL" bash "$PROJ"
+ run bash "$INSTALL" python "$PROJ"
+ [ "$status" -ne 0 ]
+ [[ "$output" == *"collision"* ]]
}
# ---- The escape hatch ----