v1.87.3.0 fix: bind review evidence to the reviewed tree (#2875)

* fix(review): bind evidence to completed unchanged review passes

* fix(review): keep unresolved Codex findings unverified

* docs: update review evidence documentation for v1.87.3.0

* test(cso): let Windows integration finish within subprocess budgets

* docs: update project documentation for v1.87.3.0

---------

Co-authored-by: garrytan <19957+garrytan@users.noreply.github.com>
This commit is contained in:
Garry Tan
2026-09-15 20:53:58 +00:00
committed by GitHub
co-authored by garrytan
parent 43c9e45ea7
commit 85b8c038fc
37 changed files with 640 additions and 166 deletions
+36 -23
View File
@@ -2,27 +2,31 @@
# gstack-review-log — atomically log a review result
# Usage: gstack-review-log '{"skill":"...","timestamp":"...","status":"..."}'
#
# Binding fields (content-addressed staleness): every appended record is
# stamped with commit_full, tree, dirty (informational) and wtree (the GATING
# working-tree fingerprint from bin/gstack-wtree). These are computed
# AUTHORITATIVELY here — caller-supplied values for the four keys are ignored,
# so a stale rendered template (or a forged field) cannot bind a record to
# content it wasn't made on. All other caller fields pass through untouched.
# Outside a git repo the fields are simply omitted (legacy consumers fall back
# to their heuristics).
#
# Known limitation: binding happens at LOG time, not review-START time — edits
# made between finishing a review and logging it (including fixes the review
# itself applied) are certified by the stamped fingerprint. gstack-evidence
# closes this window for test runs (before/after capture); review flows log
# immediately after reviewing, which keeps the window small but nonzero.
# Before reading a diff: gstack-review-log --start review
# After that pass: gstack-review-log '{...,"completed":true,"converged":true}' --finish TOKEN
# Diff reviews get wtree only from a consumed, matching start capture on an
# unchanged tree. Completion/convergence are reviewer-reported, not proof that
# a model read the code. Caller-supplied binding fields are always discarded.
# Plan-tier rows retain their legacy binding behavior.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
eval "$("$SCRIPT_DIR/gstack-slug" 2>/dev/null)"
GSTACK_HOME="${GSTACK_HOME:-$HOME/.gstack}"
mkdir -p "$GSTACK_HOME/projects/$SLUG"
INPUT="$1"
INPUT="${1:-}"
export GSTACK_REVIEW_DIR="$GSTACK_HOME/projects/$SLUG"
export GSTACK_REVIEW_REPO="$(git rev-parse --show-toplevel 2>/dev/null || true)"
export GSTACK_REVIEW_BRANCH="$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse HEAD 2>/dev/null || true)"
export GSTACK_REVIEW_LIB="$SCRIPT_DIR/../lib/review-evidence.ts"
case "$(uname -s)" in
MINGW*|MSYS*|CYGWIN*)
if command -v cygpath >/dev/null 2>&1; then
GSTACK_REVIEW_LIB="$(cygpath -m "$GSTACK_REVIEW_LIB")"
GSTACK_REVIEW_DIR="$(cygpath -m "$GSTACK_REVIEW_DIR")"
fi
;;
esac
# Compute binding fields (best-effort; empty outside a git repo).
COMMIT_FULL=$(git rev-parse HEAD 2>/dev/null || true)
@@ -39,17 +43,26 @@ if [ -n "$COMMIT_FULL" ]; then
fi
fi
export GSTACK_STAMP_COMMIT_FULL="$COMMIT_FULL" GSTACK_STAMP_TREE="$TREE" GSTACK_STAMP_WTREE="$WTREE" GSTACK_STAMP_DIRTY="$DIRTY"
if [ "$INPUT" = --start ]; then
GSTACK_REVIEW_SKILL="${2:-}" bun -e '
const { captureReviewStart } = await import(process.env.GSTACK_REVIEW_LIB);
console.log(captureReviewStart(process.env.GSTACK_REVIEW_SKILL));
'
exit $?
fi
if [ "$#" -ne 1 ] && { [ "$#" -ne 3 ] || [ "${2:-}" != --finish ]; }; then
echo 'Usage: gstack-review-log JSON [--finish TOKEN] | --start SKILL' >&2
exit 1
fi
# Validate (reject malformed or injection attempts) AND stamp in one pass.
# Caller values for the binding keys are dropped before stamping.
STAMPED=$(printf '%s' "$INPUT" | GSTACK_STAMP_COMMIT_FULL="$COMMIT_FULL" GSTACK_STAMP_TREE="$TREE" GSTACK_STAMP_WTREE="$WTREE" GSTACK_STAMP_DIRTY="$DIRTY" bun -e "
STAMPED=$(printf '%s' "$INPUT" | GSTACK_REVIEW_TOKEN="${3:-}" bun -e "
const { bindReview } = await import(process.env.GSTACK_REVIEW_LIB);
const rec = JSON.parse(await Bun.stdin.text());
for (const k of ['commit_full', 'tree', 'wtree', 'dirty']) delete rec[k];
const env = process.env;
if (env.GSTACK_STAMP_COMMIT_FULL) rec.commit_full = env.GSTACK_STAMP_COMMIT_FULL;
if (env.GSTACK_STAMP_TREE) rec.tree = env.GSTACK_STAMP_TREE;
if (env.GSTACK_STAMP_WTREE) rec.wtree = env.GSTACK_STAMP_WTREE;
if (env.GSTACK_STAMP_DIRTY) rec.dirty = env.GSTACK_STAMP_DIRTY === 'true';
console.log(JSON.stringify(rec));
if (!rec || Array.isArray(rec) || typeof rec !== 'object') throw new Error('expected object');
console.log(JSON.stringify(bindReview(rec, process.env.GSTACK_REVIEW_TOKEN)));
" 2>/dev/null) || {
# Not valid JSON — refuse to append
echo "gstack-review-log: invalid JSON, skipping" >&2
+24 -5
View File
@@ -2,23 +2,42 @@
# gstack-review-read — read review log and config for dashboard
# Usage: gstack-review-read
#
# Emits, in order: the raw reviews JSONL, ---CONFIG--- (skip_eng_review),
# Emits, in order: reviews JSONL with computed review_freshness, ---CONFIG--- (skip_eng_review),
# ---HEAD--- (short sha), ---WTREE--- (current working-tree fingerprint from
# bin/gstack-wtree, or "unknown"), ---TREE--- (HEAD tree, informational) and
# ---DIRTY--- (tracked-file dirty flag). Consumers grade diff-scoped review
# rows CURRENT when a record's `wtree` equals ---WTREE---; everything needed
# for that rule ships in this one output so graders run no extra commands.
# rows using review_freshness; legacy or uncaptured diff evidence cannot fall
# back to HEAD equality. Plan rows are unchanged.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
case "$(uname -s)" in
MINGW*|MSYS*|CYGWIN*) command -v cygpath >/dev/null 2>&1 && SCRIPT_DIR="$(cygpath -m "$SCRIPT_DIR")" ;;
esac
eval "$("$SCRIPT_DIR/gstack-slug" 2>/dev/null)"
GSTACK_HOME="${GSTACK_HOME:-$HOME/.gstack}"
cat "$GSTACK_HOME/projects/$SLUG/$BRANCH-reviews.jsonl" 2>/dev/null || echo "NO_REVIEWS"
WTREE=$("$SCRIPT_DIR/gstack-wtree" 2>/dev/null || echo "unknown")
if [ -f "$GSTACK_HOME/projects/$SLUG/$BRANCH-reviews.jsonl" ]; then
GSTACK_REVIEW_LIB="$SCRIPT_DIR/../lib/review-evidence.ts" GSTACK_REVIEW_WTREE="$WTREE" bun -e '
const { reviewFreshness } = await import(process.env.GSTACK_REVIEW_LIB);
for (const line of (await Bun.stdin.text()).split("\n").filter(Boolean)) {
try {
const rec = JSON.parse(line);
delete rec.review_freshness;
const freshness = reviewFreshness(rec, process.env.GSTACK_REVIEW_WTREE);
if (freshness) rec.review_freshness = freshness;
console.log(JSON.stringify(rec));
} catch { console.error("gstack-review-read: skipping malformed review row"); }
}
' < "$GSTACK_HOME/projects/$SLUG/$BRANCH-reviews.jsonl"
else
echo "NO_REVIEWS"
fi
echo "---CONFIG---"
"$SCRIPT_DIR/gstack-config" get skip_eng_review 2>/dev/null || echo "false"
echo "---HEAD---"
git rev-parse --short HEAD 2>/dev/null || echo "unknown"
echo "---WTREE---"
"$SCRIPT_DIR/gstack-wtree" 2>/dev/null || echo "unknown"
echo "$WTREE"
echo "---TREE---"
git rev-parse 'HEAD^{tree}' 2>/dev/null || echo "unknown"
echo "---DIRTY---"