From 126fdbc86157d66895029ed70744a7e31a6bbfaf Mon Sep 17 00:00:00 2001 From: Claude Opus 5 Date: Thu, 30 Jul 2026 01:09:22 +0600 Subject: [PATCH] harness: soften two gates that would have taught --no-verify The test-coupling gate failed comment-only and gofmt-only .go diffs, which ship no behaviour and owe no test. Bypassing it would have cost every gate at once, so it now compares added and removed lines with comments, blanks and whitespace runs stripped: equal sets mean nothing happened. A new .go file is never exempt. The exported-doc-comment check is now a warning. No gate can tell "// Load loads." from a useful sentence, so as a hard failure its cheapest satisfaction was exactly the noise conventions.md calls worse than nothing. Package comments and ADR citations in code stay hard. Also fills in state.md's verified-against line and drops a stray blank line left in roadmap.md by an earlier gate test. --- HARNESS.md | 9 ++++++--- docs/conventions.md | 7 +++++-- docs/roadmap.md | 1 - docs/state.md | 2 +- scripts/verify.sh | 18 ++++++++++++++++-- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/HARNESS.md b/HARNESS.md index adda5d4..a2c8276 100644 --- a/HARNESS.md +++ b/HARNESS.md @@ -111,8 +111,10 @@ plus one line in `cmd/atelier/wire.go`. `verify.sh` fails on a feature importing feature package without a `doc.go` — the first because sibling imports make an agent's read set compound, the second because a four-line `doc.go` turns orientation into a fifteen-line read (ADR-0027). -**Documentation is gated too.** Every package carries a package comment, every exported identifier a doc -comment, and a decision cited in code (`// Path shape: ADR-0008.`) must name an ADR that exists. The point +**Documentation is gated too.** Every package carries a package comment, and a decision cited in code +(`// Path shape: ADR-0008.`) must name an ADR that exists. Doc comments on exported identifiers are +warned rather than failed — presence is checkable, usefulness is not, and a hard gate there would buy +`// Load loads.` The point is a codebase still navigable by `go doc` and `git log` alone, years from now, with no agent available — `verify.sh` enforces presence, and only you can enforce that the comment says something. @@ -140,7 +142,8 @@ that describe it, in the same change.** - `verify.sh` fails when `CLAUDE.md`, `scripts/` or `.claude/` changes without `HARNESS.md` changing. This file is the current description of the machine, not a snapshot of its design. - `verify.sh` fails when a `.go` file changes without `docs/state.md` changing, and when `cmd/` or - `internal/` code changes without a `_test.go` changing — behaviour ships with a test. + `internal/` code changes without a `_test.go` changing — behaviour ships with a test. A comment-only or + `gofmt`-only diff is exempt: it ships no behaviour, and failing it would only teach you `--no-verify`. - The gate is not optional. `scripts/hooks/pre-commit` runs it on every commit; enable once per clone with `git config core.hooksPath scripts/hooks`. `--no-verify` bypasses it, and the commit body should say why. diff --git a/docs/conventions.md b/docs/conventions.md index ff20f98..6f5ba6f 100644 --- a/docs/conventions.md +++ b/docs/conventions.md @@ -47,11 +47,14 @@ wants splitting. ## Documentation Written for a maintainer working alone, years from now, with no agent to explain anything. `verify.sh` -enforces presence; only a human can enforce that it says something. +fails on a missing package comment and on a citation naming an ADR that does not exist; a missing doc +comment on an exported identifier is a warning, because no gate can tell `// Load loads.` from a useful +sentence, and a gate whose cheapest satisfaction is noise buys noise. - **Every package has a package comment.** What it owns, what it does not, and which packages may import it. For `internal/ext/*` that is the four-line `doc.go` shape in `extensions.md`. -- **Every exported identifier has a doc comment.** The exported surface of a 2000-line core is small, and +- **Every exported identifier has a doc comment** (warned, not gated). The exported surface of a + 2000-line core is small, and `go doc ./...` is the only navigation tool that still works when nothing else does. A comment that restates the name (`// Load loads.`) is worse than none: say what it returns on absence, what it costs, what it assumes. diff --git a/docs/roadmap.md b/docs/roadmap.md index 3436201..0104448 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -70,4 +70,3 @@ Bengali tokenisation for search is the genuinely novel problem here — solve it and deliberately, not as a side effect of adding search. The low-bandwidth, low-carbon ethos is coherent with Gemini output, PWA offline, and no-JS defaults; let that coherence break ties when two designs are otherwise equal. - diff --git a/docs/state.md b/docs/state.md index 2b35786..cd89a5c 100644 --- a/docs/state.md +++ b/docs/state.md @@ -1,6 +1,6 @@ # State -**Verified against:** `54c14a2` on 2026-07-30 — update this line every change. +**Verified against:** `1c19727` on 2026-07-30 — update this line every change. If this file disagrees with the code, the code is right and this file is a bug. ## Inventory diff --git a/scripts/verify.sh b/scripts/verify.sh index dca5a46..57bd4a7 100755 --- a/scripts/verify.sh +++ b/scripts/verify.sh @@ -51,7 +51,21 @@ if [ -d .git ] && command -v git >/dev/null 2>&1; then fi if echo "$changed" | grep -qE '^(cmd|internal)/.*\.go$' && ! echo "$changed" | grep -qE '_test\.go$'; then - bad "cmd/ or internal/ .go changed but no _test.go did — behaviour ships with a test (conventions.md)" + # A comment-only or gofmt-only diff ships no behaviour, so it owes no test. Compare the added and + # removed lines with comments, blanks and indentation stripped: equal sets mean nothing happened. + gochanged=$(echo "$changed" | grep -E '^(cmd|internal)/.*\.go$') + godiff=$(git diff HEAD -- $gochanged 2>/dev/null) + # strip the marker, comments, blanks, and *all* whitespace runs — gofmt realignment then compares + # equal, while any real edit still differs. + strip() { grep -E "^[$1]" | grep -vE '^(\+\+\+|---)' | sed "s/^[$1]//" | + grep -vE '^[[:space:]]*(//|/\*|\*/|\* )' | tr -s ' \t' ' ' | + sed 's/^ //; s/ $//' | grep -v '^$' | sort; } + newgo=$(git ls-files --others --exclude-standard -- $gochanged 2>/dev/null) + if [ -z "$newgo" ] && [ "$(echo "$godiff" | strip '+')" = "$(echo "$godiff" | strip '-')" ]; then + pass "code/test coupling (comment- or format-only)" + else + bad "cmd/ or internal/ .go changed but no _test.go did — behaviour ships with a test (conventions.md)" + fi else pass "code/test coupling" fi @@ -267,7 +281,7 @@ undocumented=$(echo "$gofiles" | grep -v '_test\.go$' | xargs awk ' /^(func|type|var|const) [A-Z]/ { if (prev !~ /^\/\//) printf "%s:%d ", FILENAME, FNR } { prev = $0 }' 2>/dev/null) if [ -n "$undocumented" ]; then - bad "exported identifier without a doc comment: $undocumented" + note "exported identifier without a doc comment: $undocumented" else pass "exported identifiers documented" fi