1# forge critique: the reviewer is .forge/review from the review's target2# branch, run on the contributor's machine; its findings become comments3# with the model's name and severity, and a signed statement in4# refs/notes/review says what is still open. An approval rule's "+ agent"5# wants a verified one of the tip's tree with nothing blocking open.6. "$(dirname "$0")/../lib.sh"7user dami8user ana9repo dami/greet10G=$FORGE_ROOT/dami/greet.git11clone dami dami/greet "$T/w/seed"12mkdir -p "$T/w/seed/.forge" "$T/w/seed/lib"13# A reviewer that needs no model: in lib/, eval is blocking, TODO a note; an open14# finding whose line no longer says eval is resolved.15cat >"$T/w/seed/.forge/review" <<'EOF'16#!/bin/sh17echo '{"model":"fake-1"}'18[ -z "$FORGE_REVIEW_RULES" ] || echo "rules: $(cat "$FORGE_REVIEW_RULES")" >&219echo "scope $FORGE_REVIEW_SCOPE, skipped: $(cat "$FORGE_REVIEW_SKIPPED" | tr '\n' ' ')" >&220awk '21 /^\+\+\+ b\// { p = substr($0, 7); next }22 /^@@/ { match($0, /\+[0-9]+/); n = substr($0, RSTART + 1, RLENGTH - 1) - 1; next }23 /^\+/ { n++24 if (p !~ /^lib\//) next25 if (index($0, "eval")) printf "{\"severity\":\"blocking\",\"path\":\"%s\",\"line\":%d,\"body\":\"no `eval` here\"}\n", p, n26 else if (index($0, "TODO")) printf "{\"severity\":\"note\",\"path\":\"%s\",\"line\":%d,\"body\":\"a TODO\"}\n", p, n27 next }28 /^ / { n++ }'29node -e '30 const fs = require("fs");31 for (const l of fs.readFileSync(process.env.FORGE_REVIEW_OPEN, "utf8").split("\n").filter(Boolean)) {32 const f = JSON.parse(l);33 const text = fs.existsSync(f.path) ? fs.readFileSync(f.path, "utf8") : "";34 if (!text.includes("eval")) console.log(JSON.stringify({ resolved: f.id }));35 }'36echo "not json, ignored"37EOF38chmod +x "$T/w/seed/.forge/review"39echo "Be strict about shell." >"$T/w/seed/.forge/review.md"40printf '#!/bin/sh\ntrue\n' >"$T/w/seed/.forge/ci"41chmod +x "$T/w/seed/.forge/ci"42printf 'lib/ 1 + agent\n* 1\n' >"$T/w/seed/.forge/approvals"43at "$T/w/seed" commit "seed" README44ok push_as dami "$T/w/seed" origin main45git --git-dir="$G" config forge.requireapproval true4647clone_ssh ana dami/greet "$T/w/a"48cl() { (cd "$T/w/a" && FORGE_USER=ana GIT_SSH_COMMAND="$T/ssh" "$FORGE" "$@"); }49at "$T/w/a" git switch -q -c ana/run50mkdir -p "$T/w/a/lib"51printf '#!/bin/sh\n# TODO: flags\neval "$1"\n' >"$T/w/a/lib/run.sh"52# The review's own reviewer would let anything through; it isn't the one used.53printf '#!/bin/sh\necho "{\\"model\\":\\"lenient\\"}"\n' >"$T/w/a/.forge/review"54at "$T/w/a" git add -A55at "$T/w/a" git commit -q -m "run things"56ok push_as ana "$T/w/a" origin ana/run57build dami/greet5859fails cl critique60has "$T_LOG" 'reviewing everything since main: run things'61has "$T_LOG" 'rules: Be strict about shell\.'62has "$T_LOG" 'scope full, skipped: $'63has "$T_LOG" '#1: 2 finding\(s\), 2 posted; 1 blocking open; signed \(fake-1\)'64c=$G/forge/comments/165has "$c/1" '^agent fake-1$'66has "$c/1" '^severity note$'67has "$c/1" '^path lib/run.sh$'68has "$c/1" '^line 2$'69has "$c/2" '^severity blocking$'70has "$c/2" '^line 3$'71has "$c/2" '^no `eval` here$'72note=$(git --git-dir="$G" notes --ref=review show ana/run)73case $note in *"review dami/greet#1"*"scope full"*"model fake-1"*"findings 274open 1"*'|{"id":"'*'-2","severity":"blocking","path":"lib/run.sh","line":3'*"-----END SSH SIGNATURE-----"*) ;; *) fail "unexpected statement: $note" ;; esac75case $note in *"rules .forge/review.md "*) ;; *) fail "the statement doesn't name the rules: $note" ;; esac7677# The page: the agent's comments say so, and to land says what is open.78build dami/greet79page=$FORGE_WWW/dami/greet/reviews/1/index.html80has "$page" '<b>ana</b> <span class="agent" title="written by an agent review \(forge critique\)">agent fake-1</span> <span class="tag blocking">blocking</span>'81has "$page" '<b>agent review</b><span class="has">: fake-1, signed by ana, 2 findings, <b>1 blocking open</b></span>'82has "$page" '1 approval and an agent review</a> <span class="paths">for <span class="mono">lib/run.sh</span>'8384# Approved, but the agent review has a blocking finding open: refused.85ok ssh_as dami approve dami/greet 186fails_with 'needs 1 approval and an agent review \(\.forge/approvals:1\) for lib/run.sh: approved by dami, no passing agent review' push_as dami "$T/w/a" origin ana/run:main8788# The same tree again: nothing new to review.89fails cl critique90has "$T_LOG" 'this tree was already reviewed, with 1 blocking finding\(s\) open'9192# Fixed: the next run reviews only what changed, and the reviewer resolves it.93printf '#!/bin/sh\n# TODO: flags\nsh -c "$1"\n' >"$T/w/a/lib/run.sh"94at "$T/w/a" git checkout -q origin/main -- .forge/review95at "$T/w/a" git commit -q -am "no eval, and main's reviewer back"96ok push_as ana "$T/w/a" origin ana/run97build dami/greet98ok cl critique99has "$T_LOG" 'reviewing what changed since [0-9a-f]{7}: run things'100has "$T_LOG" '1 blocking finding\(s\) still open from the last agent review'101has "$T_LOG" 'scope incremental'102has "$T_LOG" '#1: 0 finding\(s\), 0 posted; 0 blocking open; signed \(fake-1\)'103note=$(git --git-dir="$G" notes --ref=review show ana/run)104case $note in *"scope incremental"*"open 0"*'|{"resolved":"'*'-2","by":"agent"}'*) ;; *) fail "unexpected statement: $note" ;; esac105ok ssh_as dami approve dami/greet 1106ok push_as dami "$T/w/a" origin ana/run:main107108# Dismissed by hand: --resolve signs a statement without the finding,109# naming who dismissed it; the reviewer doesn't run.110at "$T/w/a" git switch -q -c ana/again origin/main111printf 'eval "$2"\n' >"$T/w/a/lib/again.sh"112at "$T/w/a" git add -A113at "$T/w/a" git commit -q -m "again"114ok push_as ana "$T/w/a" origin ana/again115build dami/greet116fails cl critique ana/again117id=$(git --git-dir="$G" notes --ref=review show ana/again | sed -n 's/^|{"id":"\([^"]*\)","severity":"blocking".*/\1/p')118[ -n "$id" ] || fail "no blocking finding in the statement"119fails_with "no such open finding" cl critique --resolve nope ana/again120ok cl critique --resolve "$id" ana/again121has "$T_LOG" '0 blocking open'122case $(git --git-dir="$G" notes --ref=review show ana/again) in *"scope resolved"*"\"by\":\"ana\""*) ;; *) fail "the dismissal isn't recorded" ;; esac123124# A finding on a file the review doesn't change goes on the whole review.125at "$T/w/a" git switch -q -c ana/third origin/main126echo hi >"$T/w/a/notes.txt"127at "$T/w/a" git add -A128at "$T/w/a" git commit -q -m "third"129ok push_as ana "$T/w/a" origin ana/third130git -C "$T/w/seed" pull -q origin main131cat >"$T/w/seed/.forge/review" <<'EOF'132#!/bin/sh133cat >/dev/null134echo '{"severity":"note","path":"elsewhere.sh","line":4,"body":"about another file"}'135echo '{"severity":"note","path":"notes.txt","line":99,"body":"past the end"}'136EOF137at "$T/w/seed" git commit -q -am "another reviewer"138git --git-dir="$G" config forge.requireapproval false139ok push_as dami "$T/w/seed" origin main140build dami/greet141ok cl critique ana/third142has "$T_LOG" "elsewhere.sh isn't a file the review changes; posted as a comment on the whole review"143has "$T_LOG" 'notes.txt:99 is past the end of the file; posted on the file.s first line'144n=$(ls "$G/forge/comments/3" | sort -n | head -n 1)145lacks "$G/forge/comments/3/$n" '^path '146has "$G/forge/comments/3/$((n + 1))" '^line 1$'