Fix commit: 3617983 — fix(count-guard): scan shell scripts so the guard polices its own prose
I diagnosed the issue against the real repo and verified the fix end-to-end. The solution is written to ~/solution.md (also reproduced below).
Repo: get-h3/sdk-typescript
Fix commit: 3617983 — fix(count-guard): scan shell scripts so the guard polices its own prose
Guard: scripts/check-test-count.sh · Canonical source: scripts/test-count.txt
sh scripts/check-test-count.sh printed PASS while its own header comment advertised a retired suite size:
# suite=149 this repo's own vitest suite ...
but scripts/test-count.txt declared suite=165. No sweep could see it.
is_scanned() omitted *.sh. The inventory loop in checks (d) retired-literal sweep and (e) canonical-claim sweep skipped any file not in *.md | *.ts | *.json | *.yml | *.yaml | Makefile. scripts/check-test-count.sh is a .sh file, so it was skipped before grep/awk ever saw it.suite=149 and retired battery narration (144-test suite, Tests (141), 43 tests). Restating a canonical value is exactly what drifts.(1) hid the guard from (2)’s own sweep, so the stale literal shipped.
*.sh to the scanned set is_scanned() {
case "$1" in
- *.md | *.ts | *.json | *.yml | *.yaml) return 0 ;;
+ *.md | *.ts | *.json | *.yml | *.yaml | *.sh) return 0 ;;
Makefile | */Makefile) return 0 ;;
*) return 1 ;;
esac
}
-# Canonical inputs — scripts/test-count.txt:
-# battery=46 the get-h3/shim compliance battery (`h3-test`): every "N/N
-# compliant" / "N tests, 6 categories" claim in this repo is
-# about this number.
-# suite=149 this repo's own vitest suite: one `it(`/`test(` case per line
-# in the files vitest.config.ts includes (`src/**/*.test.ts`).
+# Canonical inputs — scripts/test-count.txt is the only place that declares the
+# numeric battery= and suite= values; never restate those values here:
+# battery= the get-h3/shim compliance battery (`h3-test`): every "N/N
+# compliant" / "N tests, 6 categories" claim in this repo is
+# about this value.
+# suite= this repo's own vitest suite: one `it(`/`test(` case per line
+# in the files vitest.config.ts includes (`src/**/*.test.ts`).
-# The stale-count class re-offended here repeatedly (CONTRIBUTING.md advertised
-# a 144-test suite and a "Tests (141)" line long after vitest ran 149; the
-# diagnostics trail described the battery as 43 tests after it reached 46)
+# The stale-count class re-offended here repeatedly: living contributor and
+# diagnostics prose kept quoting retired suite and battery sizes
...
-# advertise a RETIRED battery count (43/44/45 in
-# count-shaped forms, plus any "N/44"-style fraction
+# advertise a retired battery count in count-shaped
+# forms, including any "N/<retired>"-style fraction
Era-correct lines that must remain use the documented inline marker count-ok-historical.
scripts/test-count.txt:
- suite=165
+ suite=167
CONTRIBUTING.md:
-├── src/__tests__/ # 7 test files, 165 tests ...
+├── src/__tests__/ # 7 test files, 167 tests ...
-# vitest — 165 tests across 7 test files
+# vitest — 167 tests across 7 test files
-npm test # Tests (165)
+npm test # Tests (167)
-- [ ] `npm test` passes (165 tests)
+- [ ] `npm test` passes (167 tests)
src/__tests__/test-count-guard.test.ts:
+ it("self-scans the guard's own shell prose under real repo defaults", () => {
+ const result = runGuard();
+ expect(result.stderr).toBe("");
+ expect(result.status).toBe(0);
+ expect(result.stdout).toContain(
+ "no stale count literals in current-state surfaces",
+ );
+ });
+
+ it("flags a stale count in a shell script", () => {
+ const { root, canon } = scratchTree(100);
+ writeFileSync(
+ join(root, "self-check.sh"),
+ `# stale battery narration: ${retiredBattery()} tests\n`,
+ "utf8",
+ );
+ const result = runGuard(scratchEnv(root, canon));
+ expect(result.status).toBe(1);
+ expect(result.stdout).toContain("self-check.sh:1");
+ expect(result.stderr).toContain("stale count literal");
+ });
(retiredBattery() is assembled from digit fragments so the test source carries no retired literal.)
All commands were actually run on a clean clone.
4.1 Real-repo defaults pass (exit 0):
$ sh scripts/check-test-count.sh
check-test-count: suite agrees (167 vitest cases across 7 files)
check-test-count: no stale count literals in current-state surfaces
check-test-count: PASS — canonical battery=46, suite=167; current-state prose agrees
$ echo $?
0
4.2 Behavior change proven — pre-fix guard blind, fixed guard catches it. On the pre-fix tree (3617983^, canon suite=165, header still suite=149):
$ cd /tmp/sdk-prefix && sh scripts/check-test-count.sh # PRE-FIX
check-test-count: PASS — canonical battery=46, suite=165; current-state prose agrees
$ echo $? # 0 <-- blind PASS (the bug)
$ H3_SDK_SCAN_ROOT=/tmp/sdk-prefix \
H3_SDK_COUNT_FILE=/tmp/sdk-prefix/scripts/test-count.txt \
sh scripts/check-test-count.sh # FIXED guard, same tree
scripts/check-test-count.sh:8:# a 144-test suite and a "Tests (141)" line long after vitest ran 149; the
scripts/check-test-count.sh:45: suite claim 141 is not a canonical count (165/46): ...
FAIL: 6 stale count literal(s) above.
$ echo $? # 1 <-- names file:line
4.3 Hermetic scan root with a stale count planted in a .sh (100-case suite, suite=100, planted self-check.sh quoting 45 tests):
$ sh /tmp/pre-fix-guard.sh # PRE-FIX -> PASS, exit 0 (blind)
$ sh scripts/check-test-count.sh # FIXED
self-check.sh:1:# stale narration planted in a shell script: 45 tests
FAIL: 1 stale count literal(s) above.
$ echo $? # 1 <-- names self-check.sh:1
4.4 Gates:
$ npx vitest run
Test Files 7 passed (7)
Tests 167 passed (167)
$ npx tsc --noEmit # exit 0
The narrow canonical-claim sweep still does not recognize the restated-literal form suite=<N> / battery=<N> by itself. That shape is currently prevented by removal from the header rather than detected. Keep canonical numbers only in scripts/test-count.txt; a follow-up should teach check (e) to flag suite=\d+ / battery=\d+ prose literals outside the canonical file.
# Evidence - Problem class: shell-count-guard-does-not-scan-its-own-source - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-19T09:28:38.372Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "A repo-maintained count/prose guard script (scripts/check-test-count.sh) drifted out of date in its OWN header comment (it documented the canonical suite input as suite=149 while scripts/test-count.txt read suite=165) and no sweep could catch it: its is_scanned() allow-list covered only (*.md | *.ts | *.json | *.yml | *.yaml | Makefile), so shell scripts were invisible to every check the guard performs on the tree it lives in. Rule: the guard that polices prose must include its own file type in the scanned set, and must never restate canonical numbers - restating them is what drifts.", "environment": "linux/posix-sh repo guard (get-h3/sdk-typescript)", "language": "shell", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "shell-count-guard-does-not-scan-its-own-source", "provider": "openrouter", "solved_at": "2026-09-19T09:28:38.373Z", "version": "2026-09-19"}I diagnosed the issue against the real repo and verified the fix end-to-end. The solution is written to ~/solution.md (also reproduced below).
Repo: get-h3/sdk-typescript
Fix commit: 3617983 — fix(count-guard): scan shell scripts so the guard polices its own prose
Guard: scripts/check-test-count.sh · Canonical source: scripts/test-count.txt
sh scripts/check-test-count.sh printed PASS while its own header comment advertised a retired suite size:
# suite=149 this repo's own vitest suite ...
but scripts/test-count.txt declared suite=165. No sweep could see it.
is_scanned() omitted *.sh. The inventory loop in checks (d) retired-literal sweep and (e) canonical-claim sweep skipped any file not in *.md | *.ts | *.json | *.yml | *.yaml | Makefile. scripts/check-test-count.sh is a .sh file, so it was skipped before grep/awk ever saw it.suite=149 and retired battery narration (144-test suite, Tests (141), 43 tests). Restating a canonical value is exactly what drifts.(1) hid the guard from (2)’s own sweep, so the stale literal shipped.
*.sh to the scanned set is_scanned() {
case "$1" in
- *.md | *.ts | *.json | *.yml | *.yaml) return 0 ;;
+ *.md | *.ts | *.json | *.yml | *.yaml | *.sh) return 0 ;;
Makefile | */Makefile) return 0 ;;
*) return 1 ;;
esac
}
-# Canonical inputs — scripts/test-count.txt:
-# battery=46 the get-h3/shim compliance battery (`h3-test`): every "N/N
-# compliant" / "N tests, 6 categories" claim in this repo is
-# about this number.
-# suite=149 this repo's own vitest suite: one `it(`/`test(` case per line
-# in the files vitest.config.ts includes (`src/**/*.test.ts`).
+# Canonical inputs — scripts/test-count.txt is the only place that declares the
+# numeric battery= and suite= values; never restate those values here:
+# battery= the get-h3/shim compliance battery (`h3-test`): every "N/N
+# compliant" / "N tests, 6 categories" claim in this repo is
+# about this value.
+# suite= this repo's own vitest suite: one `it(`/`test(` case per line
+# in the files vitest.config.ts includes (`src/**/*.test.ts`).
-# The stale-count class re-offended here repeatedly (CONTRIBUTING.md advertised
-# a 144-test suite and a "Tests (141)" line long after vitest ran 149; the
-# diagnostics trail described the battery as 43 tests after it reached 46)
+# The stale-count class re-offended here repeatedly: living contributor and
+# diagnostics prose kept quoting retired suite and battery sizes
...
-# advertise a RETIRED battery count (43/44/45 in
-# count-shaped forms, plus any "N/44"-style fraction
+# advertise a retired battery count in count-shaped
+# forms, including any "N/<retired>"-style fraction
Era-correct lines that must remain use the documented inline marker count-ok-historical.
scripts/test-count.txt:
- suite=165
+ suite=167
CONTRIBUTING.md:
-├── src/__tests__/ # 7 test files, 165 tests ...
+├── src/__tests__/ # 7 test files, 167 tests ...
-# vitest — 165 tests across 7 test files
+# vitest — 167 tests across 7 test files
-npm test # Tests (165)
+npm test # Tests (167)
-- [ ] `npm test` passes (165 tests)
+- [ ] `npm test` passes (167 tests)
src/__tests__/test-count-guard.test.ts:
+ it("self-scans the guard's own shell prose under real repo defaults", () => {
+ const result = runGuard();
+ expect(result.stderr).toBe("");
+ expect(result.status).toBe(0);
+ expect(result.stdout).toContain(
+ "no stale count literals in current-state surfaces",
+ );
+ });
+
+ it("flags a stale count in a shell script", () => {
+ const { root, canon } = scratchTree(100);
+ writeFileSync(
+ join(root, "self-check.sh"),
+ `# stale battery narration: ${retiredBattery()} tests\n`,
+ "utf8",
+ );
+ const result = runGuard(scratchEnv(root, canon));
+ expect(result.status).toBe(1);
+ expect(result.stdout).toContain("self-check.sh:1");
+ expect(result.stderr).toContain("stale count literal");
+ });
(retiredBattery() is assembled from digit fragments so the test source carries no retired literal.)
All commands were actually run on a clean clone.
4.1 Real-repo defaults pass (exit 0):
$ sh scripts/check-test-count.sh
check-test-count: suite agrees (167 vitest cases across 7 files)
check-test-count: no stale count literals in current-state surfaces
check-test-count: PASS — canonical battery=46, suite=167; current-state prose agrees
$ echo $?
0
4.2 Behavior change proven — pre-fix guard blind, fixed guard catches it. On the pre-fix tree (3617983^, canon suite=165, header still suite=149):
$ cd /tmp/sdk-prefix && sh scripts/check-test-count.sh # PRE-FIX
check-test-count: PASS — canonical battery=46, suite=165; current-state prose agrees
$ echo $? # 0 <-- blind PASS (the bug)
$ H3_SDK_SCAN_ROOT=/tmp/sdk-prefix \
H3_SDK_COUNT_FILE=/tmp/sdk-prefix/scripts/test-count.txt \
sh scripts/check-test-count.sh # FIXED guard, same tree
scripts/check-test-count.sh:8:# a 144-test suite and a "Tests (141)" line long after vitest ran 149; the
scripts/check-test-count.sh:45: suite claim 141 is not a canonical count (165/46): ...
FAIL: 6 stale count literal(s) above.
$ echo $? # 1 <-- names file:line
4.3 Hermetic scan root with a stale count planted in a .sh (100-case suite, suite=100, planted self-check.sh quoting 45 tests):
$ sh /tmp/pre-fix-guard.sh # PRE-FIX -> PASS, exit 0 (blind)
$ sh scripts/check-test-count.sh # FIXED
self-check.sh:1:# stale narration planted in a shell script: 45 tests
FAIL: 1 stale count literal(s) above.
$ echo $? # 1 <-- names self-check.sh:1
4.4 Gates:
$ npx vitest run
Test Files 7 passed (7)
Tests 167 passed (167)
$ npx tsc --noEmit # exit 0
The narrow canonical-claim sweep still does not recognize the restated-literal form suite=<N> / battery=<N> by itself. That shape is currently prevented by removal from the header rather than detected. Keep canonical numbers only in scripts/test-count.txt; a follow-up should teach check (e) to flag suite=\d+ / battery=\d+ prose literals outside the canonical file.
# Evidence - Problem class: shell-count-guard-does-not-scan-its-own-source - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-19T09:28:38.372Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "A repo-maintained count/prose guard script (scripts/check-test-count.sh) drifted out of date in its OWN header comment (it documented the canonical suite input as suite=149 while scripts/test-count.txt read suite=165) and no sweep could catch it: its is_scanned() allow-list covered only (*.md | *.ts | *.json | *.yml | *.yaml | Makefile), so shell scripts were invisible to every check the guard performs on the tree it lives in. Rule: the guard that polices prose must include its own file type in the scanned set, and must never restate canonical numbers - restating them is what drifts.", "environment": "linux/posix-sh repo guard (get-h3/sdk-typescript)", "language": "shell", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "shell-count-guard-does-not-scan-its-own-source", "provider": "openrouter", "solved_at": "2026-09-19T09:28:38.373Z", "version": "2026-09-19"}