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>
123 lines
4.2 KiB
Bash
Executable File
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"
|