#!/usr/bin/env bash # check_the_checker for scripts/repo-stats (improvise-1xfb.5.1): the whole pipeline, # code-maat and improvise included, over a fixture repo whose every number is known. # See context/test-evidence/tooling.md. set -uo pipefail here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" STATS="$here/repo-stats" work="$(mktemp -d)" trap 'rm -rf "$work"' EXIT pass=0; fail=0 ok() { printf ' ok %s\n' "$1"; pass=$((pass+1)); } bad() { printf ' FAIL %s (%s)\n' "$1" "$2"; fail=$((fail+1)); } # The improvise under test: on PATH in CI, else this tree's debug build. No build # here: checkers.sh scrubs the linker's environment (context/code/tooling.md ยง7). if command -v improvise >/dev/null; then IMPROVISE="$(command -v improvise)" elif [ -x "$here/../target/debug/improvise" ]; then IMPROVISE="$here/../target/debug/improvise" else echo "repo-stats-test: no improvise; run \`cargo build --bin improvise\` first" >&2 exit 1 fi export IMPROVISE # $1 name, $2 jq expression over $work/out.json, $3 the value it must print is() { local got got="$(jq -c "$2" "$work/out.json" 2>&1)" [ "$got" = "$3" ] && ok "$1" || bad "$1" "got $got, wanted $3" } # $1 name, $2 table, $3 row label, $4 the row's cells after the label, as JSON row() { is "$1" ".tables[\"$2\"][]|select(.[0]==\"$3\")|.[1:]" "$4" } # $1 name, $2 fixed-string the text report must contain says() { grep -Fq -- "$2" "$work/out.txt" && ok "$1" || bad "$1" "no '$2' in the text report" } export GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_GLOBAL=/dev/null export GIT_AUTHOR_NAME=fixture GIT_AUTHOR_EMAIL=f@x GIT_COMMITTER_NAME=fixture GIT_COMMITTER_EMAIL=f@x repo="$work/repo" g() { git -C "$repo" "$@" >/dev/null; } # $1 path, $2 line count, $3 tag. Rewriting with a new tag replaces every line. put() { mkdir -p "$(dirname "$repo/$1")"; seq -f "$3 %g" 1 "$2" > "$repo/$1"; } # $1 date; commits everything staged or not. commit() { g add -A GIT_AUTHOR_DATE="$1T12:00:00Z" GIT_COMMITTER_DATE="$1T12:00:00Z" g commit -q -m "at $1" } git init -q -b main "$repo" # Before the window: must count nowhere. put README.md 50 old; commit 2026-01-01 # Two commits co-change six files, so each pair couples at 100% over 2 revs. # src/ is the root crate's, so it crosses a boundary with both crates/. for f in crates/a/src/lib.rs crates/b/src/lib.rs; do put "$f" 10 x1; done put src/main.rs 5 x1; put crates/a/tests/t.rs 4 x1 put AGENTS.md 3 x1; put .agents/skills/wave/agent-rules.md 3 x1 commit 2026-06-02 for f in crates/a/src/lib.rs crates/b/src/lib.rs; do put "$f" 6 x2; done put src/main.rs 5 x2; put crates/a/tests/t.rs 4 x2 put AGENTS.md 3 x2; put .agents/skills/wave/agent-rules.md 3 x2 commit 2026-06-03 # One file in each remaining area. The repo map is 13 lines. put context/code/x.md 8 x3 mkdir -p "$repo/context/test-evidence" printf '### t_one\ntext\n#### t_two\n## not an entry\n### t_three\n' \ > "$repo/context/test-evidence/y.md" cat > "$repo/context/repo-map.md" <<'EOF' # Map ## Gotchas (read before editing) 1. **One.** short. 2. **Two.** a much longer gotcha, with plenty of words in it to be the longest one and a second line. 3. **Three.** middling length. --- 4. not a gotcha, because the section has ended EOF put context/plan.md 2 x3 put prolog-sessions/kb.pl 5 x3 put scripts/foo.sh 7 x3; put .github/workflows/ci.yml 1 x3 put web/app.js 9 x3; put harness/x.js 2 x3 put Cargo.lock 11 x3 put crates/a/src/grammar.pest 3 x3 put .beads/issues.jsonl 2 x3; put site/index.html 1 x3 put docs/guide.md 2 x3; put .codex/config.toml 1 x3 commit 2026-06-04 g tag before-shrink # A side branch merged with a merge commit: the side commit counts, the merge does not. g checkout -q -b side put notes.txt 4 x5; commit 2026-06-05 g checkout -q main GIT_AUTHOR_DATE=2026-06-06T12:00:00Z GIT_COMMITTER_DATE=2026-06-06T12:00:00Z \ g merge -q --no-ff -m merge side # A shrink in the next ISO week, so one area deletes and one row spans two weeks, # and a rename, which --no-renames counts as a delete and an add. put web/app.js 3 x4 g mv scripts/foo.sh scripts/bar.sh commit 2026-06-09 # $1 output dir, $2 "text" to also write the text report, rest: extra args. run() { local out="$1" text="$2"; shift 2 "$STATS" -C "$repo" --since 2026-06-01 --min-revs 2 --out "$out" "$@" --json \ > "$work/out.json" 2> "$work/err" \ || { bad "repo-stats $* ran" "$(cat "$work/err")"; return 1; } [ "$text" = text ] || return 0 "$STATS" -C "$repo" --since 2026-06-01 --min-revs 2 --out "$out" "$@" \ > "$work/out.txt" 2>&1 \ || { bad "repo-stats $* ran (text)" "$(cat "$work/out.txt")"; return 1; } } if run "$work/stats" text; then is "no gh/main in the fixture, so the ref is HEAD" '.ref' '"HEAD"' is "the merge commit is not counted, the side commit is" '.commits' '5' row "rust src is .rs and .pest outside tests" by-area "rust src" '["45","25","0.556"]' row "rust tests are the tests/ files" by-area "rust tests" '["8","4","0.500"]' row "agent docs and skills take .codex" by-area "agent docs and skills" '["13","6","0.462"]' row "docs/*.md is other docs" by-area "other docs" '["2","0","0.000"]' row "context/code" by-area "context/code" '["8","0","0.000"]' row "context/test-evidence" by-area "context/test-evidence" '["5","0","0.000"]' row "repo-map is its own area" by-area "repo-map" '["13","0","0.000"]' row "other context" by-area "other context" '["2","0","0.000"]' row "kb.pl" by-area "kb.pl" '["5","0","0.000"]' row "scripts/CI takes .github, and a rename is a delete and an add" \ by-area "scripts/CI" '["15","7","0.467"]' row "web and harness, and its deletion ratio" by-area "web and harness" '["14","9","0.643"]' row "a lock file, .beads/ and site/ are generated" by-area "generated" '["14","0","0.000"]' row "the pre-window README counts nowhere" by-area "other" '["4","0","0.000"]' row "prose is the context areas, other docs and agent docs" by-kind prose '["43","6","0.140"]' row "code is rust, scripts/CI and web" by-kind code '["82","45","0.549"]' row "prose to code is prose over code" by-kind ProseToCode '["0.524","0.133",""]' is "weeks are columns, first-seen first" '.tables["area-by-week"][0][1:]' '["2026-W24","2026-W23"]' row "an area is split by ISO week" area-by-week "web and harness" '["3","11"]' is "every day's churn.csv total equals code-maat's abs-churn" \ '[.tables["by-day"][1:][]|.[1]==.[2]]|all' 'true' row "a day's total is every file changed that day" by-day 2026-06-04 '["72","72"]' is "gotchas end with their section" '.docs.repo_map.gotchas' '3' is "the longest gotcha is measured over its lines" \ '.docs.repo_map.longest|[.number,.lines]' '[2,2]' is "test-evidence entries are ### and #### headings" '.docs.test_evidence.entries' '3' is "coupling finds the 15 pairs of six files, and web/app.js with scripts/foo.sh" \ '.coupling|length' '16' is "files of two crates, the root crate's included, cross a boundary" \ '[.coupling[]|select(.["crosses-crate"]=="yes")|[.entity,.coupled]|sort]|sort' \ '[["crates/a/src/lib.rs","crates/b/src/lib.rs"],["crates/a/src/lib.rs","src/main.rs"],["crates/a/tests/t.rs","crates/b/src/lib.rs"],["crates/a/tests/t.rs","src/main.rs"],["crates/b/src/lib.rs","src/main.rs"]]' is "two agent-rule docs are flagged" \ '[.coupling[]|select(.["agent-rules"]=="yes")|[.entity,.coupled]|sort]' \ '[[".agents/skills/wave/agent-rules.md","AGENTS.md"]]' says "the text report flags a crate crossing" "[CROSSES-CRATE]" says "the text report flags agent-rule docs" "[AGENT-RULES]" says "the text report prints improvise's ratio row" "ProseToCode" views="$(grep -c '^## View: ' "$work/stats/repo-stats.improv")" [ "$views" = 10 ] && ok "the model keeps a view per file and the report's four" \ || bad "the model keeps a view per file and the report's four" "$views views" # Sparse, not padded: a padded merge has the same numbers and views, but a # `-` item in every category and per-file views seated on the padding. dashes="$(grep -c '|-|' "$work/stats/repo-stats.improv")" [ "$dashes" = 0 ] && ok "the sparse model has no padding item" \ || bad "the sparse model has no padding item" "$dashes lines name |-|" churn_view="$(awk '/^## View: churn$/,/^$/' "$work/stats/repo-stats.improv")" for seat in "date: row" "agent-rules: none" "coupled: none" "crosses-crate: none"; do grep -qx -- "$seat" <<< "$churn_view" && ok "the churn view seats $seat" \ || bad "the churn view seats $seat" "$(tr '\n' ' ' <<< "$churn_view")" done fi # An --out holding a CSV this script did not write is refused, since the folder # import would merge it. The script's own CSVs, from a rerun, are not foreign. mkdir -p "$work/foreign"; echo "x,y" > "$work/foreign/other.csv" "$STATS" -C "$repo" --since 2026-06-01 --out "$work/foreign" > /dev/null 2>&1 got=$? [ "$got" = 2 ] && ok "a foreign CSV in --out is refused with exit 2" \ || bad "a foreign CSV in --out is refused with exit 2" "exit $got" # The window is scoped to the ref: at the tag, the shrink has not happened. if run "$work/at-tag" json --ref before-shrink; then row "--ref scopes the log" by-area "web and harness" '["11","0","0.000"]' is "--ref names the ref it read" '.ref' '"before-shrink"' fi # A window whose only commits are empty changes no file: exit 0, and no model. empty="$work/empty" git init -q -b main "$empty" repo="$empty"; put README.md 5 old; commit 2026-01-01 GIT_AUTHOR_DATE=2026-06-02T12:00:00Z GIT_COMMITTER_DATE=2026-06-02T12:00:00Z \ g commit -q --allow-empty -m empty if run "$work/empty-out" text; then is "an empty commit is counted" '.commits' '1' is "and builds no tables" '.tables' '{}' lines="$(wc -l < "$work/empty-out/churn.csv" | tr -d ' ')" [ "$lines" = 1 ] && ok "churn.csv is written with its header alone" \ || bad "churn.csv is written with its header alone" "$lines lines" says "the text report says why there are no tables" "No file changed in the window" fi repo="$work/repo" # improvise refusing the pipeline is exit 3, whatever the numbers were. The # third stub imports cleanly and refuses only the scripts. mkdir -p "$work/bin" printf '#!/usr/bin/env bash\necho "Parse error: no" >&2\nexit 1\n' > "$work/bin/refuses" printf '#!/usr/bin/env bash\necho "warning: Skipped 3 of 3 records" >&2\nexit 0\n' \ > "$work/bin/warns" printf '#!/usr/bin/env bash\n[ "$1" = script ] || exit 0\necho "Parse error: no" >&2\nexit 1\n' \ > "$work/bin/refuses-a-script" chmod +x "$work/bin/refuses" "$work/bin/warns" "$work/bin/refuses-a-script" for stub in refuses warns refuses-a-script; do IMPROVISE="$work/bin/$stub" "$STATS" -C "$repo" --since 2026-06-01 --out "$work/$stub" \ > /dev/null 2>&1 got=$? [ "$got" = 3 ] && ok "an improvise that $stub fails the pipeline with exit 3" \ || bad "an improvise that $stub fails the pipeline with exit 3" "exit $got" done printf '\nrepo-stats: %d passed, %d failed\n' "$pass" "$fail" [ "$fail" = 0 ]