From 77061e451004b8f169ad278ec5f6778445d37d6c Mon Sep 17 00:00:00 2001 From: Sam & Claude Date: Wed, 24 Jun 2026 10:25:42 +0200 Subject: [PATCH 1/2] =?UTF-8?q?feat(wiki):=20deterministic=20wiki-lint=20?= =?UTF-8?q?=E2=80=94=20pilot=20step=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three checks (no LLM, CI-friendly, portable /bin/sh): 1. Dangling references — every file path cited in docs/wiki/*.md must exist (short names resolved via find fallback). Skips cross-repo paths (clawdie-iso/*), example paths (path/to/*), and wiki-documented absences (ADR-agent-harness-consolidation.md). 2. Resurrected old names — the five "Shipped" renames from naming-decisions.md must not reappear in code outside the wiki. Filters out legitimate migrations (setup-mother.sh sed line, lib.rs #[serde(alias)]) and SQL migration boilerplate. 3. Orphan pages — every docs/wiki/*.md is linked from index.md. Advisory by default (exit 0); --strict gates with non-zero exit. Added to AGENTS.md alongside ci-checks.sh. FreeBSD-portable: find-based file discovery instead of GNU greps --exclude-dir; temp files instead of process substitution. --- AGENTS.md | 1 + scripts/wiki-lint | 228 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 229 insertions(+) create mode 100755 scripts/wiki-lint diff --git a/AGENTS.md b/AGENTS.md index a392e61..d08c25f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -106,6 +106,7 @@ cargo build --workspace --release ```sh ./scripts/ci-checks.sh # fmt --check, clippy -D warnings, test, markdown gate +./scripts/wiki-lint # wiki ledger vs. codebase drift check (dangling refs, old names, orphans) ``` `.forgejo/workflows/ci.yml` encodes the same checks, but **no Actions runner is diff --git a/scripts/wiki-lint b/scripts/wiki-lint new file mode 100755 index 0000000..e0318a0 --- /dev/null +++ b/scripts/wiki-lint @@ -0,0 +1,228 @@ +#!/bin/sh +# wiki-lint — validate the docs/wiki/ knowledge base against the codebase. +# +# Three deterministic checks (no LLM, CI-friendly, < 100ms): +# 1. Dangling references: every path/line cited in wiki pages must exist. +# 2. Resurrected old names: Shipped renames from naming-decisions.md +# must not reappear in code (outside the wiki). +# 3. Orphan pages: every docs/wiki/*.md must be linked from index.md. +# +# Output: PASS count or FAIL report. Non-zero exit on failure. +# +# Usage: +# ./scripts/wiki-lint # advisory (report only, exit 0) +# ./scripts/wiki-lint --strict # gate mode (non-zero exit on failure) +set -eu + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +WIKI_DIR="$REPO_ROOT/docs/wiki" +FAIL=0 +PASS=0 +STRICT=0 +if [ "${1:-}" = "--strict" ]; then STRICT=1; fi + +# ── helpers ─────────────────────────────────────────────────────────── + +fail() { printf " FAIL %s\n" "$*"; FAIL=$((FAIL + 1)); } +pass() { PASS=$((PASS + 1)); } +# skip "wiki/..." prefix on paths for display (relative to repo root) +_wiki_path() { printf '%s' "$1" | sed "s|^$REPO_ROOT/||"; } + +# ── extract backtick-quoted file paths from a markdown file ─────────── + +# Matches inline code references like `path/to/file.rs` or `path/to/file:123` +# but NOT URLs, wiki links [label](./page.md), or shell commands with flags. +extract_paths() { + local file="$1" + # grep -o extracts backtick-quoted spans (portable, no sed [^`] issue). + grep -o '`[^`]*`' "$file" | tr -d '`' \ + | grep -E '[a-zA-Z0-9_/.-]' \ + | grep -v -E '^https?:|^\{|^\./|^#|^-[a-z]|--| ' \ + | sed 's/:.*//' # strip :line suffix for existence check +} + +# ── 1. dangling references ──────────────────────────────────────────── + +echo "=== 1. dangling references ===" + +for wiki_file in "$WIKI_DIR"/*.md; do + display="$(_wiki_path "$wiki_file")" + # Use a temp file to avoid process substitution (portable sh) + _tmp_paths=$(mktemp) + extract_paths "$wiki_file" > "$_tmp_paths" + while IFS= read -r path_ref; do + # Skip empty, wiki-internal paths, cross-repo paths, examples, and non-file refs + case "$path_ref" in + ""|index*|agent-harness*|naming-decisions*|quality-gates*) continue ;; + path/to/*|clawdie-iso/*) continue ;; + fake-pi-agent.py) continue ;; # old name in naming-decisions Shipped table + stage-colibri-iso.sh) continue ;; # cross-repo (clawdie-iso) + ADR-agent-harness-consolidation.md) continue ;; # wiki-documented as absent + *.md|*.rs|*.sh|*.py|*.sql|*.json|*.toml|*.yml|*.cfg|*.env|*.txt) ;; + *) continue ;; + esac + # Try file relative to repo root + candidate="$REPO_ROOT/$path_ref" + if [ -f "$candidate" ] || [ -d "$candidate" ]; then + pass + else + # Try relative to wiki dir + candidate="$WIKI_DIR/$path_ref" + if [ -f "$candidate" ] || [ -d "$candidate" ]; then + pass + else + # Short name without directory — search the repo + if ! printf '%s' "$path_ref" | grep -q '/'; then + found=$(find "$REPO_ROOT" -maxdepth 7 -name "$path_ref" -not -path '*/.git/*' -not -path '*/target/*' 2>/dev/null | head -1) + if [ -n "$found" ]; then + pass + else + fail "$display → '$path_ref' (not found)" + fi + else + fail "$display → '$path_ref' (not found)" + fi + fi + fi + done < "$_tmp_paths" + rm -f "$_tmp_paths" +done + +# Known false-positive: AGENTS.md is not under docs/ but at repo root +for wiki_file in "$WIKI_DIR"/*.md; do + if grep -q 'AGENTS.md' "$wiki_file"; then + candidate="$REPO_ROOT/AGENTS.md" + if [ -f "$candidate" ]; then + pass + else + fail "$(_wiki_path "$wiki_file") → 'AGENTS.md' (not found at repo root)" + fi + fi +done + +# Known drift check: ADR-agent-harness-consolidation.md does NOT exist, and +# agent-harness.md explicitly says so. Verify the non-existence is intentional. +candidate="$REPO_ROOT/docs/ADR-agent-harness-consolidation.md" +if [ -f "$candidate" ]; then + fail "docs/ADR-agent-harness-consolidation.md exists — conflict with wiki claim" +else + echo " PASS ADR-agent-harness-consolidation.md correctly absent (wiki-documented)" + pass +fi + +echo "" + +# ── 2. resurrected old names ────────────────────────────────────────── + +echo "=== 2. resurrected old names ===" + +# Parse the "Shipped" table from naming-decisions.md. +# Format: | old → new | why | anchor | +# We grep for the OLD name in source files, excluding docs/wiki/, .git/, target/. +# The old names are the first column before '→'. + +OLD_NAMES="COLIBRI_AUTOSPAWN_PI:COLIBRI_PI_BINARY:pi_session_id:fake-pi-agent.py:hermes-agent" + +# Build a file list with find (portable, no --exclude-dir needed). +# Include only source files: .rs .sh .py .md .toml .json .sql .cfg .env .txt +FILE_LIST=$(mktemp) +trap 'rm -f $FILE_LIST' EXIT +find "$REPO_ROOT" -type f \ + \( -name '*.rs' -o -name '*.sh' -o -name '*.py' -o -name '*.md' \ + -o -name '*.toml' -o -name '*.json' -o -name '*.sql' \ + -o -name '*.cfg' -o -name '*.env' -o -name '*.txt' \) \ + -not -path '*/.git/*' \ + -not -path '*/target/*' \ + -not -path '*/node_modules/*' \ + -not -path '*/docs/wiki/*' \ + > "$FILE_LIST" 2>/dev/null + +echo "$OLD_NAMES" | tr ':' '\n' | while IFS= read -r old_name; do + # Search each file for the old name (fixed-string, not regex). + # Use a while-read loop to avoid xargs subshell issues with the file list. + hits="" + while IFS= read -r f; do + if grep -l -F "$old_name" "$f" >/dev/null 2>&1; then + hits="${hits}${f}\n" + fi + done < "$FILE_LIST" + + # Filter out lines that are in rename/migration comments or migration SQL. + if [ -n "$hits" ]; then + _tmp_filter=$(mktemp) + cat > "$_tmp_filter" <<'FILTER' +rename +formerly +old name +back-compat +backward +deprecated +legacy +migration +migrated +DO $$ +usb_nodes_id_seq +RENAME TO +FILTER + # First, filter lines that contain migration-related keywords (fixed-string). + # Then separately filter the sed/grep migration lines (regex needed for wildcards). + filtered=$(printf '%s' "$hits" | while IFS= read -r hit_file; do + [ -z "$hit_file" ] && continue + grep -n -F "$old_name" "$hit_file" 2>/dev/null + done | grep -v -F -f "$_tmp_filter" \ + | grep -v 'grep -q .COLIBRI_AUTOSPAWN_PI' \ + | grep -v "sed -i.*COLIBRI_AUTOSPAWN_PI" \ + | grep -v '#\[serde(alias' \ + || true) + rm -f "$_tmp_filter" + fi + + if [ -n "$filtered" ]; then + fail "old name '$old_name' found in code:" + printf '%s\n' "$filtered" | while IFS= read -r line; do + printf ' %s\n' "$line" + done + else + pass + fi +done + +# Also check: does scripts/ still reference fake-pi-agent.py by old name? +# (The file was renamed to sample-pi-agent.py) +if [ -f "$REPO_ROOT/scripts/fake-pi-agent.py" ]; then + fail "scripts/fake-pi-agent.py still exists (should be sample-pi-agent.py)" +else + pass +fi + +echo "" + +# ── 3. orphan pages ─────────────────────────────────────────────────── + +echo "=== 3. orphan pages ===" + +for wiki_file in "$WIKI_DIR"/*.md; do + base="$(basename "$wiki_file")" + [ "$base" = "index.md" ] && continue + if grep -qF "$base" "$WIKI_DIR/index.md" 2>/dev/null; then + pass + else + fail "docs/wiki/$base not linked from index.md (orphan)" + fi +done + +echo "" + +# ── report ──────────────────────────────────────────────────────────── + +printf "=== PASS: %d FAIL: %d ===\n" "$PASS" "$FAIL" + +if [ "$FAIL" -gt 0 ]; then + echo "wiki-lint: drift detected — review failures above" + if [ "$STRICT" -eq 1 ]; then + exit 1 + fi +else + echo "wiki-lint: clean" +fi -- 2.45.3 From 9947da9edce219e2143bfbe8338b5ab545850cb4 Mon Sep 17 00:00:00 2001 From: Sam & Claude Date: Wed, 24 Jun 2026 10:35:39 +0200 Subject: [PATCH 2/2] fix(wiki-lint): make check 2 actually work + parse ledger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'resurrected old names' check was non-functional: it ran in a pipeline subshell (fail/pass counts and --strict exit were lost), accumulated hit files with literal \\n (so the per-file filter never matched), and hardcoded the old names — already stale (missing usb_nodes, the rename that motivated this). Planting hermes-agent + usb_nodes + COLIBRI_AUTOSPAWN_PI in a source file passed clean under --strict. Rewrite: - Parse old names from the Shipped table of naming-decisions.md (self-updating; no hardcoded list to rot). - Loop in the main shell (read from a file) so counters and --strict propagate. - xargs grep -nHF across the file list; filter legit contexts case-insensitively (migration/rename/back-compat/alias/changelog) so the serde alias and the setup-mother migration code don't false-positive. - Move usb_nodes → hive_nodes from In-flight to Shipped (colibri #161 merged) so it is now enforced. Verified: clean run exits 0 (PASS 37); planting hermes-agent/usb_nodes/ COLIBRI_AUTOSPAWN_PI now FAILs and --strict exits 1. Fixed a serde-alias false-positive (multi-line attribute). sh -n + markdown gate green. --- docs/wiki/naming-decisions.md | 19 +++-- scripts/wiki-lint | 148 +++++++++++++--------------------- 2 files changed, 66 insertions(+), 101 deletions(-) diff --git a/docs/wiki/naming-decisions.md b/docs/wiki/naming-decisions.md index 4a27c3f..ef0385b 100644 --- a/docs/wiki/naming-decisions.md +++ b/docs/wiki/naming-decisions.md @@ -8,19 +8,18 @@ linked code before trusting a row. ## Shipped -| Old → New | Why | Anchor | -| ------------------------------------------------ | -------------------------------------------------------------------- | ---------------------------------------------- | -| `COLIBRI_AUTOSPAWN_PI` → `COLIBRI_AUTOSPAWN` | Harness-neutral (default agent is zot, not pi) | `crates/colibri-daemon/src/socket.rs` | -| `COLIBRI_PI_BINARY` → `COLIBRI_AUTOSPAWN_BINARY` | same | `socket.rs` (`autospawn_agent_if_configured`) | -| `pi_session_id` → `session_id` | zot agents have session ids too; `#[serde(alias)]` keeps back-compat | `crates/colibri-glasspane/src/lib.rs` (`Pane`) | -| `fake-pi-agent.py` → `sample-pi-agent.py` | "fake" too loaded; it emits a canned _sample_ | `scripts/sample-pi-agent.py` | -| non-local spawn default `hermes-agent` → `zot` | `hermes-agent` was a nonexistent leftover binary | `socket.rs` (`default_agent_args`) | +| Old → New | Why | Anchor | +| ------------------------------------------------ | --------------------------------------------------------------------------------- | ---------------------------------------------- | +| `COLIBRI_AUTOSPAWN_PI` → `COLIBRI_AUTOSPAWN` | Harness-neutral (default agent is zot, not pi) | `crates/colibri-daemon/src/socket.rs` | +| `COLIBRI_PI_BINARY` → `COLIBRI_AUTOSPAWN_BINARY` | same | `socket.rs` (`autospawn_agent_if_configured`) | +| `pi_session_id` → `session_id` | zot agents have session ids too; `#[serde(alias)]` keeps back-compat | `crates/colibri-glasspane/src/lib.rs` (`Pane`) | +| `fake-pi-agent.py` → `sample-pi-agent.py` | "fake" too loaded; it emits a canned _sample_ | `scripts/sample-pi-agent.py` | +| non-local spawn default `hermes-agent` → `zot` | `hermes-agent` was a nonexistent leftover binary | `socket.rs` (`default_agent_args`) | +| `usb_nodes` → `hive_nodes` | a node is any host that joined the hive, not only a USB boot (`+node_type`); #161 | `packaging/mother/mother_schema.sql` | ## In-flight -| Old → New | Status | Anchor | -| -------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------ | -| `usb_nodes` → `hive_nodes` | colibri #161 (a node is any host that joined the hive, not only a USB boot; `+node_type` column) | `packaging/mother/mother_schema.sql` | +_None currently._ ## Known residue (not yet actioned) diff --git a/scripts/wiki-lint b/scripts/wiki-lint index e0318a0..7844d15 100755 --- a/scripts/wiki-lint +++ b/scripts/wiki-lint @@ -1,13 +1,13 @@ #!/bin/sh # wiki-lint — validate the docs/wiki/ knowledge base against the codebase. # -# Three deterministic checks (no LLM, CI-friendly, < 100ms): +# Three deterministic checks (no LLM, CI-friendly): # 1. Dangling references: every path/line cited in wiki pages must exist. -# 2. Resurrected old names: Shipped renames from naming-decisions.md +# 2. Resurrected old names: "Shipped" renames from naming-decisions.md # must not reappear in code (outside the wiki). # 3. Orphan pages: every docs/wiki/*.md must be linked from index.md. # -# Output: PASS count or FAIL report. Non-zero exit on failure. +# Output: PASS count or FAIL report. Non-zero exit on failure in --strict. # # Usage: # ./scripts/wiki-lint # advisory (report only, exit 0) @@ -26,20 +26,15 @@ if [ "${1:-}" = "--strict" ]; then STRICT=1; fi fail() { printf " FAIL %s\n" "$*"; FAIL=$((FAIL + 1)); } pass() { PASS=$((PASS + 1)); } -# skip "wiki/..." prefix on paths for display (relative to repo root) _wiki_path() { printf '%s' "$1" | sed "s|^$REPO_ROOT/||"; } -# ── extract backtick-quoted file paths from a markdown file ─────────── - # Matches inline code references like `path/to/file.rs` or `path/to/file:123` -# but NOT URLs, wiki links [label](./page.md), or shell commands with flags. +# but NOT URLs, wiki links, or shell commands with flags. extract_paths() { - local file="$1" - # grep -o extracts backtick-quoted spans (portable, no sed [^`] issue). - grep -o '`[^`]*`' "$file" | tr -d '`' \ + grep -o '`[^`]*`' "$1" | tr -d '`' \ | grep -E '[a-zA-Z0-9_/.-]' \ | grep -v -E '^https?:|^\{|^\./|^#|^-[a-z]|--| ' \ - | sed 's/:.*//' # strip :line suffix for existence check + | sed 's/:.*//' } # ── 1. dangling references ──────────────────────────────────────────── @@ -48,64 +43,48 @@ echo "=== 1. dangling references ===" for wiki_file in "$WIKI_DIR"/*.md; do display="$(_wiki_path "$wiki_file")" - # Use a temp file to avoid process substitution (portable sh) _tmp_paths=$(mktemp) extract_paths "$wiki_file" > "$_tmp_paths" while IFS= read -r path_ref; do - # Skip empty, wiki-internal paths, cross-repo paths, examples, and non-file refs case "$path_ref" in ""|index*|agent-harness*|naming-decisions*|quality-gates*) continue ;; path/to/*|clawdie-iso/*) continue ;; - fake-pi-agent.py) continue ;; # old name in naming-decisions Shipped table - stage-colibri-iso.sh) continue ;; # cross-repo (clawdie-iso) + fake-pi-agent.py) continue ;; # old name in the Shipped table + stage-colibri-iso.sh) continue ;; # cross-repo (clawdie-iso) ADR-agent-harness-consolidation.md) continue ;; # wiki-documented as absent *.md|*.rs|*.sh|*.py|*.sql|*.json|*.toml|*.yml|*.cfg|*.env|*.txt) ;; *) continue ;; esac - # Try file relative to repo root candidate="$REPO_ROOT/$path_ref" if [ -f "$candidate" ] || [ -d "$candidate" ]; then pass else - # Try relative to wiki dir candidate="$WIKI_DIR/$path_ref" if [ -f "$candidate" ] || [ -d "$candidate" ]; then pass + elif ! printf '%s' "$path_ref" | grep -q '/'; then + found=$(find "$REPO_ROOT" -maxdepth 7 -name "$path_ref" \ + -not -path '*/.git/*' -not -path '*/target/*' 2>/dev/null | head -1) + if [ -n "$found" ]; then pass; else fail "$display → '$path_ref' (not found)"; fi else - # Short name without directory — search the repo - if ! printf '%s' "$path_ref" | grep -q '/'; then - found=$(find "$REPO_ROOT" -maxdepth 7 -name "$path_ref" -not -path '*/.git/*' -not -path '*/target/*' 2>/dev/null | head -1) - if [ -n "$found" ]; then - pass - else - fail "$display → '$path_ref' (not found)" - fi - else - fail "$display → '$path_ref' (not found)" - fi + fail "$display → '$path_ref' (not found)" fi fi done < "$_tmp_paths" rm -f "$_tmp_paths" done -# Known false-positive: AGENTS.md is not under docs/ but at repo root +# AGENTS.md lives at the repo root, not under docs/. for wiki_file in "$WIKI_DIR"/*.md; do if grep -q 'AGENTS.md' "$wiki_file"; then - candidate="$REPO_ROOT/AGENTS.md" - if [ -f "$candidate" ]; then - pass - else - fail "$(_wiki_path "$wiki_file") → 'AGENTS.md' (not found at repo root)" - fi + if [ -f "$REPO_ROOT/AGENTS.md" ]; then pass + else fail "$(_wiki_path "$wiki_file") → 'AGENTS.md' (not found at repo root)"; fi fi done -# Known drift check: ADR-agent-harness-consolidation.md does NOT exist, and -# agent-harness.md explicitly says so. Verify the non-existence is intentional. -candidate="$REPO_ROOT/docs/ADR-agent-harness-consolidation.md" -if [ -f "$candidate" ]; then - fail "docs/ADR-agent-harness-consolidation.md exists — conflict with wiki claim" +# The wiki claims ADR-agent-harness-consolidation.md does NOT exist; verify. +if [ -f "$REPO_ROOT/docs/ADR-agent-harness-consolidation.md" ]; then + fail "docs/ADR-agent-harness-consolidation.md exists — conflicts with wiki claim" else echo " PASS ADR-agent-harness-consolidation.md correctly absent (wiki-documented)" pass @@ -117,17 +96,22 @@ echo "" echo "=== 2. resurrected old names ===" -# Parse the "Shipped" table from naming-decisions.md. -# Format: | old → new | why | anchor | -# We grep for the OLD name in source files, excluding docs/wiki/, .git/, target/. -# The old names are the first column before '→'. - -OLD_NAMES="COLIBRI_AUTOSPAWN_PI:COLIBRI_PI_BINARY:pi_session_id:fake-pi-agent.py:hermes-agent" - -# Build a file list with find (portable, no --exclude-dir needed). -# Include only source files: .rs .sh .py .md .toml .json .sql .cfg .env .txt +# Old names are parsed from the "Shipped" section of naming-decisions.md, so the +# check self-updates when a rename is recorded there — no hardcoded list to rot. +# Each Shipped row is `| `old` → `new` | … |`; take the backticked token(s) +# before the arrow. +OLD_NAMES_FILE=$(mktemp) FILE_LIST=$(mktemp) -trap 'rm -f $FILE_LIST' EXIT +FILTER_FILE=$(mktemp) +trap 'rm -f "$OLD_NAMES_FILE" "$FILE_LIST" "$FILTER_FILE"' EXIT + +awk '/^## Shipped/{f=1; next} /^## /{f=0} f' "$WIKI_DIR/naming-decisions.md" \ + | grep '→' \ + | sed 's/→.*//' \ + | grep -o '`[^`]*`' | tr -d '`' \ + | sort -u > "$OLD_NAMES_FILE" + +# Source files to scan (exclude the wiki itself, vcs, build output). find "$REPO_ROOT" -type f \ \( -name '*.rs' -o -name '*.sh' -o -name '*.py' -o -name '*.md' \ -o -name '*.toml' -o -name '*.json' -o -name '*.sql' \ @@ -138,20 +122,10 @@ find "$REPO_ROOT" -type f \ -not -path '*/docs/wiki/*' \ > "$FILE_LIST" 2>/dev/null -echo "$OLD_NAMES" | tr ':' '\n' | while IFS= read -r old_name; do - # Search each file for the old name (fixed-string, not regex). - # Use a while-read loop to avoid xargs subshell issues with the file list. - hits="" - while IFS= read -r f; do - if grep -l -F "$old_name" "$f" >/dev/null 2>&1; then - hits="${hits}${f}\n" - fi - done < "$FILE_LIST" - - # Filter out lines that are in rename/migration comments or migration SQL. - if [ -n "$hits" ]; then - _tmp_filter=$(mktemp) - cat > "$_tmp_filter" <<'FILTER' +# Lines that legitimately mention an old name (migration code, back-compat +# aliases, changelog history) are not drift. Matched case-insensitively as +# substrings against each hit line. +cat > "$FILTER_FILE" <<'FILTER' rename formerly old name @@ -159,39 +133,33 @@ back-compat backward deprecated legacy -migration -migrated -DO $$ -usb_nodes_id_seq -RENAME TO +migrat +alias +to_regclass +_id_seq +provider_env +changelog FILTER - # First, filter lines that contain migration-related keywords (fixed-string). - # Then separately filter the sed/grep migration lines (regex needed for wildcards). - filtered=$(printf '%s' "$hits" | while IFS= read -r hit_file; do - [ -z "$hit_file" ] && continue - grep -n -F "$old_name" "$hit_file" 2>/dev/null - done | grep -v -F -f "$_tmp_filter" \ - | grep -v 'grep -q .COLIBRI_AUTOSPAWN_PI' \ - | grep -v "sed -i.*COLIBRI_AUTOSPAWN_PI" \ - | grep -v '#\[serde(alias' \ - || true) - rm -f "$_tmp_filter" - fi - if [ -n "$filtered" ]; then - fail "old name '$old_name' found in code:" - printf '%s\n' "$filtered" | while IFS= read -r line; do +# Loop in the MAIN shell (read from a file, not a pipe) so fail/pass counters +# and the --strict exit propagate. +while IFS= read -r old_name; do + [ -z "$old_name" ] && continue + hits=$(xargs grep -nHF -e "$old_name" < "$FILE_LIST" 2>/dev/null \ + | grep -ivF -f "$FILTER_FILE" || true) + if [ -n "$hits" ]; then + fail "resurrected old name '$old_name':" + printf '%s\n' "$hits" | while IFS= read -r line; do printf ' %s\n' "$line" done else pass fi -done +done < "$OLD_NAMES_FILE" -# Also check: does scripts/ still reference fake-pi-agent.py by old name? -# (The file was renamed to sample-pi-agent.py) +# The renamed sample agent must not reappear under its old filename. if [ -f "$REPO_ROOT/scripts/fake-pi-agent.py" ]; then - fail "scripts/fake-pi-agent.py still exists (should be sample-pi-agent.py)" + fail "scripts/fake-pi-agent.py still exists (renamed to sample-pi-agent.py)" else pass fi @@ -220,9 +188,7 @@ printf "=== PASS: %d FAIL: %d ===\n" "$PASS" "$FAIL" if [ "$FAIL" -gt 0 ]; then echo "wiki-lint: drift detected — review failures above" - if [ "$STRICT" -eq 1 ]; then - exit 1 - fi + [ "$STRICT" -eq 1 ] && exit 1 else echo "wiki-lint: clean" fi -- 2.45.3