From 362974244ebc6b30027d7344c400da641bbf405c Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 7 Oct 2026 21:08:33 +0200 Subject: [PATCH] Hold a retired word's plural to the word in what the tools say (hq ADR 0244) The pattern ended at the word, so control planes and flavors passed where control plane and flavor fail. It now takes s or es on the last part; a test holds it, and merge-check runs that test before the check. --- checks/words/main.go | 7 +++++-- checks/words/main_test.go | 26 ++++++++++++++++++++++++++ merge-check.sh | 4 ++-- 3 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 checks/words/main_test.go diff --git a/checks/words/main.go b/checks/words/main.go index 3cc58c9..345e86b 100644 --- a/checks/words/main.go +++ b/checks/words/main.go @@ -4,7 +4,8 @@ // own code that is not a test (a tool's description, the notes and errors it answers with) and every // description in its manifest. Comments are not read: an agent never sees them. // -// A word is matched whole and in any case, a space in it matching any run of white space. A vendor word is +// A word is matched whole and in any case, a space in it matching any run of white space, and its plural +// (`s` or `es` on its last part) is matched as the word. A vendor word is // never on the list (the glossary does not retire one for the tools), so a wrapped program's own objects — // an identity provider's users, a media manager's releases — are never findings. // @@ -123,7 +124,9 @@ func pattern(word string) *regexp.Regexp { for i, p := range parts { parts[i] = regexp.QuoteMeta(p) } - return regexp.MustCompile(`(?i)(?:^|[^\w-])(` + strings.Join(parts, `\s+`) + `)(?:$|[^\w-])`) + // The plural is the word too: `s` or `es` on its last part, so "control planes" is found as "control + // plane" is. Anything else joined on is another word. + return regexp.MustCompile(`(?i)(?:^|[^\w-])(` + strings.Join(parts, `\s+`) + `(?:e?s)?)(?:$|[^\w-])`) } func check(words []string, texts []text) []string { diff --git a/checks/words/main_test.go b/checks/words/main_test.go new file mode 100644 index 0000000..949d746 --- /dev/null +++ b/checks/words/main_test.go @@ -0,0 +1,26 @@ +package main + +import "testing" + +// A retired word's plural is the word; anything else joined on is another word. +func TestPatternMatchesTheWordAndItsPlural(t *testing.T) { + cases := []struct { + word, text string + want bool + }{ + {"control plane", "ask the control plane", true}, + {"control plane", "two control planes", true}, + {"control plane", "two control\n\tplanes", true}, + {"substrate", "the substrates", true}, + {"flavor", "flavors of a module", true}, + {"flavor", "flavored", false}, + {"node tools", "node toolsets", false}, + {"mesh-console", "the mesh-consoles", true}, + {"mesh-console", "mesh-console-x", false}, + } + for _, c := range cases { + if got := pattern(c.word).MatchString(c.text); got != c.want { + t.Errorf("pattern(%q) on %q = %v, want %v", c.word, c.text, got, c.want) + } + } +} diff --git a/merge-check.sh b/merge-check.sh index 1a1b713..00d094d 100755 --- a/merge-check.sh +++ b/merge-check.sh @@ -18,10 +18,10 @@ # 3. the words (novox/hq ADR 0244): no word the glossary retired for the tools, as copied in # retired-words, in any text a module's tools can show an agent — every string in its own code that is # not a test, and every description in its manifest. Run over every module, not only the touched ones, -# so a word newly retired is found everywhere it stands. +# so a word newly retired is found everywhere it stands. The check's own test runs first. set -eu -(cd checks/words && go run . ../..) +(cd checks/words && go test -count=1 . && go run . ../..) if [ -n "${MESH_GATE:-}" ]; then checked=$("$MESH_GATE" module check modules/*/module.json) || { printf '%s\n' "$checked"; exit 1; }