#!/usr/bin/env bash
# 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.
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"

# Compute binding fields (best-effort; empty outside a git repo).
COMMIT_FULL=$(git rev-parse HEAD 2>/dev/null || true)
TREE=""
WTREE=""
DIRTY=""
if [ -n "$COMMIT_FULL" ]; then
  TREE=$(git rev-parse 'HEAD^{tree}' 2>/dev/null || true)
  WTREE=$("$SCRIPT_DIR/gstack-wtree" 2>/dev/null || true)
  if [ -n "$(git status --porcelain -uno 2>/dev/null | head -1)" ]; then
    DIRTY="true"
  else
    DIRTY="false"
  fi
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 "
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));
" 2>/dev/null) || {
  # Not valid JSON — refuse to append
  echo "gstack-review-log: invalid JSON, skipping" >&2
  exit 1
}

echo "$STAMPED" >> "$GSTACK_HOME/projects/$SLUG/$BRANCH-reviews.jsonl"

# gbrain-sync: enqueue for cross-machine sync (no-op if sync is off).
"$SCRIPT_DIR/gstack-brain-enqueue" "projects/$SLUG/$BRANCH-reviews.jsonl" 2>/dev/null &
