#!/usr/bin/env bash # tests/e2e-fix-round-4-5b2-c.sh # End to end for fix round 4.5b2, part C (plan # .claude/plans/trekplan-2026-11-03-review-gate-4-5b2-fixes.md, Step 6, # End-to-end:). On throwaway fixture repos it writes run evidence the way # /trekexecute Phase 8 does (node lib/review/merge-evidence.mjs write) and # feeds hooks/scripts/pre-bash-executor.mjs Bash inputs the way Claude Code does: # 1. `git merge --no-ff feat -m "merge: trekexecute demo"` on main # (/trekexecute's own order) asks with feat's Tested by line; # 2. `git merge --no-ff feat && git merge --no-ff other` asks with the line # that says the run evidence is not shown, and no `Tested by: End-to-end`; # 3. `git merge --cleanup strip --no-ff feat` asks with feat's Tested by line; # 4. `git merge --no-ff -m "Merge run branch" other` with evidence only for # feat asks and never shows feat's evidence; # 5. a tag named `feat` next to the branch: the writer refuses, and the # question shows no run evidence; # 6. a write from a second worktree whose HEAD lags the branch: the question # reads `review not measured`. # Prints one "ok - " or "FAIL - : " line per case and # "e2e: ok" at the end, exit 0; else exit 1. Starts nothing but node, git and # the usual file tools. bash 3.2 compatible, ASCII only, no arrays. set -u cd "$(dirname "$0")/.." || { echo "FAIL - setup: cannot cd to the repo root"; exit 1; } ROOT="$(pwd)" HOOK="$ROOT/hooks/scripts/pre-bash-executor.mjs" FILL="$ROOT/lib/review/fill-finding-ids.mjs" WRITER="$ROOT/lib/review/merge-evidence.mjs" unset CLAUDE_CODE_SESSION_ID TMP="$(mktemp -d "${TMPDIR:-/tmp}/e2e-fix-round-4-5b2-c.XXXXXX")" || { echo "FAIL - setup: mktemp"; exit 1; } trap 'rm -rf "$TMP"' EXIT TMP="$(cd "$TMP" && pwd -P)" || { echo "FAIL - setup: cannot enter the temp directory"; exit 1; } export LC_ALL=C export LANG=C FAILED=0 pass() { echo "ok - $1"; } bad() { echo "FAIL - $1"; FAILED=$((FAILED + 1)); } mkdir -p "$TMP/hooks-none" || { echo "FAIL - setup: cannot make the temp directories"; exit 1; } # A review_version 1.2 ALLOW review with no findings of .. # (shape copied from tests/e2e-fix-round-4-5b2-b.sh mkreview.mjs). # node mkreview.mjs cat > "$TMP/mkreview.mjs" <<'EOF' import { writeFileSync } from 'node:fs'; const [out, start, end] = process.argv.slice(2); const json = JSON.stringify({ verdict: 'ALLOW', counts: { BLOCKER: 0, MAJOR: 0, MINOR: 0, SUGGESTION: 0 }, allow_blocked_by: [], findings: [] }, null, 2); writeFileSync(out, `--- type: trekreview review_version: "1.2" task: "t" slug: e2e project_dir: .claude/projects/2026-11-03-e2e/ brief_path: .claude/projects/2026-11-03-e2e/brief.md scope_sha_start: ${start} scope_sha_end: ${end} reviewed_files_count: 1 verdict: ALLOW findings: [] --- # Review: t ## Executive Summary x ## Coverage | File | Treatment | Reason | |------|-----------|--------| | a.mjs | deep-review | default | ## Declined to Judge None. Trimmed: 0 ## Remediation Steps None. ## Remediation Summary x \`\`\`json ${json} \`\`\` `); EOF # jfield : prints the JS expression over the parsed JSON # value `j` read from (empty when it does not parse). # (Copied from tests/e2e-fix-round-4-5b2-b.sh.) jfield() { node -e ' const fs = require("fs"); let j; try { j = JSON.parse(fs.readFileSync(process.argv[1], "utf8")); } catch (e) { process.exit(0); } const v = Function("j", "return (" + process.argv[2] + ");")(j); process.stdout.write(v === undefined || v === null ? "" : String(v)); ' "$1" "$2" } # mkrepo : a git repo on main (a.mjs) with two branches one commit ahead # each, feat (b.mjs) and other (c.mjs), checked out on main. Sets MAIN_SHA, # FEAT and OTHER. mkrepo() { r="$1" mkdir -p "$r" || return 1 git -C "$r" init -q -b main || return 1 printf '[user]\n\tname = Test\n\temail = test@example.invalid\n[commit]\n\tgpgsign = false\n[core]\n\thooksPath = "%s"\n' "$TMP/hooks-none" >> "$r/.git/config" printf 'export const a = 1;\n' > "$r/a.mjs" git -C "$r" add a.mjs && git -C "$r" commit -q -m 'chore: start' || return 1 MAIN_SHA="$(git -C "$r" rev-parse HEAD)" git -C "$r" checkout -q -b feat || return 1 printf 'export const b = 1;\n' > "$r/b.mjs" git -C "$r" add b.mjs && git -C "$r" commit -q -m 'feat: the work' || return 1 FEAT="$(git -C "$r" rev-parse HEAD)" git -C "$r" checkout -q main || return 1 git -C "$r" checkout -q -b other || return 1 printf 'export const c = 1;\n' > "$r/c.mjs" git -C "$r" add c.mjs && git -C "$r" commit -q -m 'feat: the other work' || return 1 OTHER="$(git -C "$r" rev-parse HEAD)" git -C "$r" checkout -q main || return 1 } # mkproject : a run's artifacts (plan, progress, ALLOW # review of ..) outside the repo. mkproject() { mkdir -p "$1" || return 1 printf '# Plan\n\n### Step 1: End to end\n\n- **End-to-end:** `/bin/bash tests/e2e-demo.sh` -> expected: `e2e: ok`, exit 0.\n\n## Doors\n\n- two-way: a revert restores the old parser\n' > "$1/plan.md" printf '{"schema_version": "1", "status": "completed", "end_to_end": "verified", "plan_verification": {"status": "passed", "summary": {"total": 3, "passed": 3, "failed": 0, "blocked": 0, "unrunnable": 0}}}\n' > "$1/progress.json" node "$TMP/mkreview.mjs" "$1/review.md" "$2" "$3" && node "$FILL" "$1/review.md" > /dev/null } # writeev : the writer's CLI run from ; # prints its exit status. writeev() { node "$WRITER" write --repo "$1" --branch "$2" --project "$3" --json > "$4" 2>&1 echo $? } # record