golangLAKEHOUSE/scripts/scrum_review.sh
root 740eb0d00c scrum_review: switch curl to stdin so large diffs don't blow argv
Phase 4-bundle review (128KB diff) hit "Argument list too long" when
curl --data was passed the body as a literal arg. Pipe via stdin
with --data-binary @- instead. Lifts the practical bundle size from
~30KB to whatever fits in process memory.

Caught while running the harness scrum on golangLAKEHOUSE today —
the bigger Phase A+B harness diff (4566 lines) tripped it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 02:46:52 -05:00

123 lines
4.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# Cross-lineage scrum review driver.
#
# Per feedback_cross_lineage_review.md: Opus + Kimi + Qwen3-coder
# review the same diff via /v1/chat. Convergent findings (≥2
# reviewers) = high-signal real bugs; single-reviewer = lineage
# catch.
#
# Usage:
# ./scripts/scrum_review.sh <bundle.diff> <bundle_label>
#
# Outputs verbatim verdicts to:
# reports/scrum/_evidence/<DATE>/verdicts/<bundle>_<reviewer>.md
set -euo pipefail
cd "$(dirname "$0")/.."
DIFF_FILE="${1:-}"
BUNDLE_LABEL="${2:-}"
DATE="${SCRUM_DATE:-$(date +%Y-%m-%d)}"
GATEWAY="${LH_GATEWAY:-http://127.0.0.1:3110}"
OUT_DIR="reports/scrum/_evidence/${DATE}/verdicts"
if [ -z "$DIFF_FILE" ] || [ ! -f "$DIFF_FILE" ]; then
echo "usage: $0 <diff_file> <bundle_label>"
echo "example: $0 reports/scrum/_evidence/2026-04-30/diffs/bundle_A_config_refactor.diff config_refactor"
exit 1
fi
mkdir -p "$OUT_DIR"
DIFF_BYTES=$(wc -c < "$DIFF_FILE")
DIFF_LINES=$(wc -l < "$DIFF_FILE")
echo "[scrum] $BUNDLE_LABEL$DIFF_LINES lines · $DIFF_BYTES bytes · 3 reviewers"
# System prompt — same shape as the Rust auditor's review template,
# tightened per feedback_cross_lineage_review.md (lead with verdict).
SYSTEM='You are a senior code reviewer in a 3-lineage cross-review.
Your verdict feeds a convergent-finding gate (≥2 reviewers = real
bug). Be terse, evidence-based, and lead with the verdict.
For each finding, output one block:
SEVERITY: BLOCK | WARN | INFO
WHERE: <file>:<line> (or <file>:<symbol>)
WHAT: one-sentence description
WHY: one-sentence rationale grounded in the diff
Severity ladder:
- BLOCK = ship-blocker. Wrong correctness, security flaw, broken
contract, lost test coverage, panic on real input, secret
leak. Worth blocking the PR.
- WARN = real but non-blocking. Race condition under specific
load, missing edge case, weak naming making future bugs
likely, regression risk in adjacent code.
- INFO = nit / style / better-name suggestion / dead-code remnant.
Skip the analysis preamble. Lead with the first BLOCK/WARN/INFO
block. End with an empty "VERDICT:" line of "ship | ship-with-fixes
| hold" + ≤15 word summary.
Never invent line numbers — only cite lines the diff shows.'
REVIEWERS=(
"opus|opencode/claude-opus-4-7"
"kimi|openrouter/moonshotai/kimi-k2-0905"
"qwen|openrouter/qwen/qwen3-coder"
)
DIFF_CONTENT=$(cat "$DIFF_FILE")
run_review() {
local short="$1" model="$2"
local out="$OUT_DIR/${BUNDLE_LABEL}_${short}.md"
local user="Review the following diff. Bundle: $BUNDLE_LABEL.
\`\`\`diff
$DIFF_CONTENT
\`\`\`"
local body
body=$(jq -n --arg model "$model" --arg sys "$SYSTEM" --arg user "$user" \
'{model:$model, max_tokens:4096, messages:[{role:"system",content:$sys},{role:"user",content:$user}]}')
printf " %-6s %s ... " "$short" "$model"
local t0=$SECONDS
local status
# Pipe the body via stdin (`-d @-`) — large diffs (>~128KB) blow
# past the kernel's argv limit when passed via `--data <literal>`.
# Phase A+B was 128875 bytes and hit "Argument list too long" until
# this fix.
status=$(printf '%s' "$body" | curl -sS -o /tmp/scrum_resp.json -w '%{http_code}' --max-time 240 \
-X POST "$GATEWAY/v1/chat" \
-H 'Content-Type: application/json' \
--data-binary @-)
local elapsed=$((SECONDS - t0))
if [ "$status" != "200" ]; then
printf "✗ HTTP %s (%ds)\n" "$status" "$elapsed"
head -c 300 /tmp/scrum_resp.json
echo
return 1
fi
local content latency tokens_in tokens_out
content=$(jq -r '.content' /tmp/scrum_resp.json)
latency=$(jq -r '.latency_ms' /tmp/scrum_resp.json)
tokens_in=$(jq -r '.input_tokens // 0' /tmp/scrum_resp.json)
tokens_out=$(jq -r '.output_tokens // 0' /tmp/scrum_resp.json)
{
echo "# Scrum review — $BUNDLE_LABEL$short ($model)"
echo
echo "**Latency:** ${latency}ms · **Tokens:** ${tokens_in} in / ${tokens_out} out · **Date:** ${DATE}"
echo
echo "---"
echo
echo "$content"
} > "$out"
printf "✓ %dms · %dt-out → %s\n" "$latency" "$tokens_out" "$out"
}
for r in "${REVIEWERS[@]}"; do
short="${r%%|*}"
model="${r#*|}"
run_review "$short" "$model" || true
done
echo "[scrum] $BUNDLE_LABEL complete"