* fix(#2358): scope review workflow temp files to a per-run mktemp dir /gsd-review wrote every prompt/section/output temp file to a hardcoded /tmp path keyed only on the phase number, so two GSD projects sharing a small phase number collide on the exact same path and a crashed run's leftover file becomes bait a later, unrelated run can silently read. ship.md's external peer-review stderr capture was strictly worse — one shared, unqualified path across every project/phase/run. Thread a single mktemp -d "${TMPDIR:-/tmp}/gsd-review.XXXXXX" run directory through every review.md temp path (67 sites) via a new {run_dir}/$RUN_DIR placeholder, mirroring the existing {phase} substitution mechanism, and clean it up at the end of the run. Route ship.md's stderr capture through a per-run mktemp file the same way. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(#2358): regenerate fixtures + lint gate-prep * fix(#2358): repair failing tests after gate verification * fix(#2358): thread RUN_DIR scoping into reviewer-instances.md (#1517) review.md's own invoke_reviewers step lazily loads gsd-core/references/reviewer-instances.md for the review.reviewer_instances codepath, but that doc was missed when review.md and ship.md were moved to the run-scoped {run_dir} temp directory. It still read the combined prompt from the old /tmp/gsd-review-prompt-{phase}.md (which build_prompt no longer writes, breaking reviewer-instances functionality outright) and wrote each instance's output to the old unscoped /tmp/gsd-review-${INSTANCE_NAME}-{phase}.md, leaving the exact cross-project temp-file collision bug open for that code path. Both paths now thread through {run_dir}, matching every other reviewer block in review.md. Extends the existing #2358 regression test with assertions pinning reviewer-instances.md's prompt read and output write to {run_dir}, and adds the Fixed changeset fragment. Regenerated the golden-install-parity content hashes for reviewer-instances.md via `npm run gen:golden` (paths unchanged; only the modified file's hash moved). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(#2358): backfill changeset pr (#2433) --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
5
.changeset/jolly-jaguars-swim.md
Normal file
5
.changeset/jolly-jaguars-swim.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 2433
|
||||
---
|
||||
**`/gsd-review` and `/gsd:ship` temp files are now scoped to a single per-run directory** — both workflows previously wrote prompt, section, and reviewer-output files to `/tmp/gsd-review-*-{phase}.*` keyed only on the bare phase number, so two projects sharing a phase number (or a crashed run's leftover file) could collide and silently feed a reviewer another project's stale content with no error; every temp path now lives under one `mktemp`-created run directory that's removed after the review completes. (#2358)
|
||||
@@ -60,27 +60,29 @@ cannot diverge (`DEFECT.GENERATIVE-FIX`; parity-locked in
|
||||
|
||||
For each selected INSTANCE, invoke its base `cli` using the instance's own `model`/`agent` —
|
||||
NOT the global `review.models.<cli>`. Each instance writes to its OWN per-instance output file
|
||||
and runs as a distinct reviewer identity.
|
||||
under the run-scoped `{run_dir}` (`RUN_DIR` from `gather_context`, #2358 — never a bare
|
||||
`{phase}`-keyed `/tmp` path) and runs as a distinct reviewer identity.
|
||||
|
||||
For an OpenCode-backed instance (the motivating adapter):
|
||||
|
||||
```bash
|
||||
# $INSTANCE_MODEL / $INSTANCE_AGENT come from the instance spec; $INSTANCE_NAME is the
|
||||
# reviewer identity (e.g. opencode-deepseek). --agent is OpenCode's native subagent flag;
|
||||
# omit it when the instance has no agent.
|
||||
# omit it when the instance has no agent. {run_dir} is the run-scoped mktemp directory
|
||||
# created once in gather_context (#2358) — same directory every other reviewer block uses.
|
||||
if [ -n "$INSTANCE_AGENT" ] && [ "$INSTANCE_AGENT" != "null" ]; then
|
||||
cat /tmp/gsd-review-prompt-{phase}.md | opencode run --model "$INSTANCE_MODEL" --agent "$INSTANCE_AGENT" - 2>/dev/null > /tmp/gsd-review-${INSTANCE_NAME}-{phase}.md
|
||||
cat {run_dir}/gsd-review-prompt.md | opencode run --model "$INSTANCE_MODEL" --agent "$INSTANCE_AGENT" - 2>/dev/null > {run_dir}/gsd-review-${INSTANCE_NAME}.md
|
||||
else
|
||||
cat /tmp/gsd-review-prompt-{phase}.md | opencode run --model "$INSTANCE_MODEL" - 2>/dev/null > /tmp/gsd-review-${INSTANCE_NAME}-{phase}.md
|
||||
cat {run_dir}/gsd-review-prompt.md | opencode run --model "$INSTANCE_MODEL" - 2>/dev/null > {run_dir}/gsd-review-${INSTANCE_NAME}.md
|
||||
fi
|
||||
if [ ! -s /tmp/gsd-review-${INSTANCE_NAME}-{phase}.md ]; then
|
||||
echo "OpenCode review ($INSTANCE_NAME) failed or returned empty output." > /tmp/gsd-review-${INSTANCE_NAME}-{phase}.md
|
||||
if [ ! -s {run_dir}/gsd-review-${INSTANCE_NAME}.md ]; then
|
||||
echo "OpenCode review ($INSTANCE_NAME) failed or returned empty output." > {run_dir}/gsd-review-${INSTANCE_NAME}.md
|
||||
fi
|
||||
```
|
||||
|
||||
For an instance backed by a DIFFERENT cli, reuse that cli's invocation block with two
|
||||
substitutions: use the instance's `model` in place of the global `review.models.<cli>` value,
|
||||
and write to `/tmp/gsd-review-${INSTANCE_NAME}-{phase}.md`. Only `opencode` honours an
|
||||
and write to `{run_dir}/gsd-review-${INSTANCE_NAME}.md`. Only `opencode` honours an
|
||||
`agent` field in v1; ignore `agent` for other adapters.
|
||||
|
||||
---
|
||||
|
||||
@@ -119,10 +119,20 @@ Collect phase artifacts for the review prompt:
|
||||
```bash
|
||||
INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
|
||||
# #2358: ONE run-scoped temp dir (portable via ${TMPDIR:-/tmp}) so overlapping
|
||||
# runs never collide or read each other's stale files.
|
||||
RUN_DIR=$(mktemp -d "${TMPDIR:-/tmp}/gsd-review-XXXXXX")
|
||||
echo "RUN_DIR=$RUN_DIR"
|
||||
```
|
||||
|
||||
Read from init: `phase_dir`, `phase_number`, `padded_phase`.
|
||||
|
||||
Capture `RUN_DIR` above (created ONCE) and thread it into every `{run_dir}`
|
||||
placeholder and `$RUN_DIR`/`${RUN_DIR}` reference within a bash block. Do NOT
|
||||
re-run `mktemp -d` later — every block must resolve to this same directory, or
|
||||
`build_prompt`'s writes and `invoke_reviewers`' reads split.
|
||||
|
||||
Then read:
|
||||
1. `.planning/PROJECT.md` (first 80 lines — project context)
|
||||
2. Phase section from `.planning/ROADMAP.md`
|
||||
@@ -189,40 +199,42 @@ Focus on:
|
||||
Output your review in markdown format.
|
||||
```
|
||||
|
||||
Write to a temp file: `/tmp/gsd-review-prompt-{phase}.md`
|
||||
Write to a temp file: `{run_dir}/gsd-review-prompt.md`
|
||||
|
||||
Also write individual section files so the budget tool can re-trim per reviewer:
|
||||
|
||||
```bash
|
||||
RUN_DIR="{run_dir}" # from gather_context
|
||||
|
||||
# Write individual section files for per-reviewer budget trimming
|
||||
# These are always written so reviewers with a budget can invoke prompt-budget
|
||||
cp "$INSTRUCTIONS_BLOCK_FILE" "/tmp/gsd-review-${PHASE}-instructions.md"
|
||||
cp "$ROADMAP_SECTION_FILE" "/tmp/gsd-review-${PHASE}-roadmap.md"
|
||||
cp "$INSTRUCTIONS_BLOCK_FILE" "${RUN_DIR}/gsd-review-instructions.md"
|
||||
cp "$ROADMAP_SECTION_FILE" "${RUN_DIR}/gsd-review-roadmap.md"
|
||||
|
||||
# Plan files: copy each PLAN.md to a predictable numbered path
|
||||
PLAN_INDEX=0
|
||||
for PLAN_FILE in "${PHASE_DIR}"/*-PLAN.md; do
|
||||
PADDED_IDX=$(printf '%02d' "$PLAN_INDEX")
|
||||
cp "$PLAN_FILE" "/tmp/gsd-review-${PHASE}-plan-${PADDED_IDX}.md"
|
||||
cp "$PLAN_FILE" "${RUN_DIR}/gsd-review-plan-${PADDED_IDX}.md"
|
||||
PLAN_INDEX=$((PLAN_INDEX + 1))
|
||||
done
|
||||
|
||||
# Optional section files (only if content was included in the combined prompt)
|
||||
if [ -f ".planning/PROJECT.md" ]; then
|
||||
cp .planning/PROJECT.md "/tmp/gsd-review-${PHASE}-project.md"
|
||||
cp .planning/PROJECT.md "${RUN_DIR}/gsd-review-project.md"
|
||||
fi
|
||||
if ls "${PHASE_DIR}/"*"-CONTEXT.md" >/dev/null 2>&1; then
|
||||
cat "${PHASE_DIR}/"*"-CONTEXT.md" > "/tmp/gsd-review-${PHASE}-context.md"
|
||||
cat "${PHASE_DIR}/"*"-CONTEXT.md" > "${RUN_DIR}/gsd-review-context.md"
|
||||
fi
|
||||
if ls "${PHASE_DIR}/"*"-RESEARCH.md" >/dev/null 2>&1; then
|
||||
cat "${PHASE_DIR}/"*"-RESEARCH.md" > "/tmp/gsd-review-${PHASE}-research.md"
|
||||
cat "${PHASE_DIR}/"*"-RESEARCH.md" > "${RUN_DIR}/gsd-review-research.md"
|
||||
fi
|
||||
if [ -f ".planning/REQUIREMENTS.md" ]; then
|
||||
cp .planning/REQUIREMENTS.md "/tmp/gsd-review-${PHASE}-requirements.md"
|
||||
cp .planning/REQUIREMENTS.md "${RUN_DIR}/gsd-review-requirements.md"
|
||||
fi
|
||||
```
|
||||
|
||||
Note: The variable names above (`INSTRUCTIONS_BLOCK_FILE`, `ROADMAP_SECTION_FILE`, `PHASE_DIR`, `PHASE`) reference the variables already established during prompt assembly. In practice the AI implementing this step writes the instruction and roadmap blocks to temp files while assembling the combined prompt, then copies those same temp files to the per-reviewer section paths. If the assembled prompt was built inline (string concatenation rather than file-by-file), write each section to the corresponding path after writing the combined file.
|
||||
Note: `INSTRUCTIONS_BLOCK_FILE`, `ROADMAP_SECTION_FILE`, and `PHASE_DIR` come from prompt assembly; `RUN_DIR` is the run-scoped dir from `gather_context` (#2358) re-assigned from `{run_dir}` above. Copy the temp files written during prompt assembly to these section paths (or write each section here if the prompt was built inline).
|
||||
</step>
|
||||
|
||||
<step name="invoke_reviewers">
|
||||
@@ -260,18 +272,18 @@ For each selected CLI, invoke in sequence (not parallel — avoid rate limits):
|
||||
**Gemini:**
|
||||
```bash
|
||||
if [ -n "$GEMINI_MODEL" ] && [ "$GEMINI_MODEL" != "null" ]; then
|
||||
cat /tmp/gsd-review-prompt-{phase}.md | gemini -m "$GEMINI_MODEL" -p - 2>/dev/null > /tmp/gsd-review-gemini-{phase}.md
|
||||
cat {run_dir}/gsd-review-prompt.md | gemini -m "$GEMINI_MODEL" -p - 2>/dev/null > {run_dir}/gsd-review-gemini.md
|
||||
else
|
||||
cat /tmp/gsd-review-prompt-{phase}.md | gemini -p - 2>/dev/null > /tmp/gsd-review-gemini-{phase}.md
|
||||
cat {run_dir}/gsd-review-prompt.md | gemini -p - 2>/dev/null > {run_dir}/gsd-review-gemini.md
|
||||
fi
|
||||
```
|
||||
|
||||
**Claude (separate session):**
|
||||
```bash
|
||||
if [ -n "$CLAUDE_MODEL" ] && [ "$CLAUDE_MODEL" != "null" ]; then
|
||||
cat /tmp/gsd-review-prompt-{phase}.md | claude --model "$CLAUDE_MODEL" -p - 2>/dev/null > /tmp/gsd-review-claude-{phase}.md
|
||||
cat {run_dir}/gsd-review-prompt.md | claude --model "$CLAUDE_MODEL" -p - 2>/dev/null > {run_dir}/gsd-review-claude.md
|
||||
else
|
||||
cat /tmp/gsd-review-prompt-{phase}.md | claude -p - 2>/dev/null > /tmp/gsd-review-claude-{phase}.md
|
||||
cat {run_dir}/gsd-review-prompt.md | claude -p - 2>/dev/null > {run_dir}/gsd-review-claude.md
|
||||
fi
|
||||
```
|
||||
|
||||
@@ -286,13 +298,13 @@ fi
|
||||
# stdout redirect would append that noise to a non-empty file — slipping past the
|
||||
# `[ ! -s … ]` empty-output guard as a silently polluted review.
|
||||
if [ -n "$CODEX_MODEL" ] && [ "$CODEX_MODEL" != "null" ]; then
|
||||
cat /tmp/gsd-review-prompt-{phase}.md | codex exec --ephemeral $CODEX_BYPASS_FLAG --model "$CODEX_MODEL" --skip-git-repo-check -o /tmp/gsd-review-codex-{phase}.md - 2>/tmp/gsd-review-codex-{phase}.err >/dev/null
|
||||
cat {run_dir}/gsd-review-prompt.md | codex exec --ephemeral $CODEX_BYPASS_FLAG --model "$CODEX_MODEL" --skip-git-repo-check -o {run_dir}/gsd-review-codex.md - 2>{run_dir}/gsd-review-codex.err >/dev/null
|
||||
else
|
||||
cat /tmp/gsd-review-prompt-{phase}.md | codex exec --ephemeral $CODEX_BYPASS_FLAG --skip-git-repo-check -o /tmp/gsd-review-codex-{phase}.md - 2>/tmp/gsd-review-codex-{phase}.err >/dev/null
|
||||
cat {run_dir}/gsd-review-prompt.md | codex exec --ephemeral $CODEX_BYPASS_FLAG --skip-git-repo-check -o {run_dir}/gsd-review-codex.md - 2>{run_dir}/gsd-review-codex.err >/dev/null
|
||||
fi
|
||||
if [ ! -s /tmp/gsd-review-codex-{phase}.md ]; then
|
||||
echo "Codex review failed or returned empty output. stderr:" > /tmp/gsd-review-codex-{phase}.md
|
||||
cat /tmp/gsd-review-codex-{phase}.err >> /tmp/gsd-review-codex-{phase}.md
|
||||
if [ ! -s {run_dir}/gsd-review-codex.md ]; then
|
||||
echo "Codex review failed or returned empty output. stderr:" > {run_dir}/gsd-review-codex.md
|
||||
cat {run_dir}/gsd-review-codex.err >> {run_dir}/gsd-review-codex.md
|
||||
fi
|
||||
```
|
||||
|
||||
@@ -301,7 +313,7 @@ fi
|
||||
Note: CodeRabbit reviews the current git diff/working tree — it does not accept a prompt or model flag. It may take up to 5 minutes. Use `timeout: 360000` on the Bash tool call. The source-grounding requirement in the build_prompt Review Instructions applies only to the prompt-fed reviewers above; CodeRabbit is a diff-only reviewer and never receives it. Treat its output as a diff observation, not a grounded plan-level verdict.
|
||||
|
||||
```bash
|
||||
coderabbit review --prompt-only 2>/dev/null > /tmp/gsd-review-coderabbit-{phase}.md
|
||||
coderabbit review --prompt-only 2>/dev/null > {run_dir}/gsd-review-coderabbit.md
|
||||
```
|
||||
|
||||
**OpenCode (via GitHub Copilot):**
|
||||
@@ -333,30 +345,30 @@ if [ -n "$OPENCODE_MODEL" ] && [ "$OPENCODE_MODEL" != "null" ]; then
|
||||
else
|
||||
set --
|
||||
fi
|
||||
cat /tmp/gsd-review-prompt-{phase}.md | opencode run "$@" --format json - 2>/tmp/gsd-review-opencode-{phase}.err > /tmp/gsd-review-opencode-{phase}.json
|
||||
# Reconstruct the review from the assistant text parts. Capture into a variable and
|
||||
# test its CONTENT (not the output file's size): an empty extraction still prints a
|
||||
# trailing newline, which would fool a `[ -s file ]` check into skipping the stub.
|
||||
OPENCODE_REVIEW=$(jq -rs '[.[] | select(.type=="text") | .part.text // empty] | join("\n")' /tmp/gsd-review-opencode-{phase}.json 2>/dev/null)
|
||||
cat {run_dir}/gsd-review-prompt.md | opencode run "$@" --format json - 2>{run_dir}/gsd-review-opencode.err > {run_dir}/gsd-review-opencode.json
|
||||
# Reconstruct the review from the assistant text parts into a variable and test
|
||||
# its CONTENT, not the file size: an empty extraction still prints a trailing
|
||||
# newline that would fool a `[ -s file ]` check into skipping the stub.
|
||||
OPENCODE_REVIEW=$(jq -rs '[.[] | select(.type=="text") | .part.text // empty] | join("\n")' {run_dir}/gsd-review-opencode.json 2>/dev/null)
|
||||
if [ -n "$OPENCODE_REVIEW" ]; then
|
||||
printf '%s\n' "$OPENCODE_REVIEW" > /tmp/gsd-review-opencode-{phase}.md
|
||||
printf '%s\n' "$OPENCODE_REVIEW" > {run_dir}/gsd-review-opencode.md
|
||||
else
|
||||
# No assistant text (agent emitted no final message, or stdout was not valid JSON events).
|
||||
# No assistant text (no final message, or stdout was not valid JSON):
|
||||
{
|
||||
echo "OpenCode review returned no assistant text (#1936: agent ended its turn with no final message)."
|
||||
OPENCODE_DIAG=$(jq -rs '[.[] | select(.type=="step_finish")] | last | "stop reason=\(.part.reason // "?"), output tokens=\(.part.tokens.output // "?")"' /tmp/gsd-review-opencode-{phase}.json 2>/dev/null)
|
||||
OPENCODE_DIAG=$(jq -rs '[.[] | select(.type=="step_finish")] | last | "stop reason=\(.part.reason // "?"), output tokens=\(.part.tokens.output // "?")"' {run_dir}/gsd-review-opencode.json 2>/dev/null)
|
||||
[ -n "$OPENCODE_DIAG" ] && echo "Diagnostic: $OPENCODE_DIAG"
|
||||
echo "stderr:"
|
||||
cat /tmp/gsd-review-opencode-{phase}.err
|
||||
} > /tmp/gsd-review-opencode-{phase}.md
|
||||
cat {run_dir}/gsd-review-opencode.err
|
||||
} > {run_dir}/gsd-review-opencode.md
|
||||
fi
|
||||
```
|
||||
|
||||
**Qwen Code:**
|
||||
```bash
|
||||
cat /tmp/gsd-review-prompt-{phase}.md | qwen - 2>/dev/null > /tmp/gsd-review-qwen-{phase}.md
|
||||
if [ ! -s /tmp/gsd-review-qwen-{phase}.md ]; then
|
||||
echo "Qwen review failed or returned empty output." > /tmp/gsd-review-qwen-{phase}.md
|
||||
cat {run_dir}/gsd-review-prompt.md | qwen - 2>/dev/null > {run_dir}/gsd-review-qwen.md
|
||||
if [ ! -s {run_dir}/gsd-review-qwen.md ]; then
|
||||
echo "Qwen review failed or returned empty output." > {run_dir}/gsd-review-qwen.md
|
||||
fi
|
||||
```
|
||||
|
||||
@@ -371,11 +383,11 @@ fi
|
||||
# need an explicit root to resolve against. rev-parse (not bare pwd) so the
|
||||
# anchor is correct even when /gsd:review is invoked from a repo subdirectory.
|
||||
_CURSOR_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
||||
CURSOR_PROMPT_ARG="Read the file at /tmp/gsd-review-prompt-{phase}.md in full and carry out the review request it contains. The repository under review is at $_CURSOR_ROOT — resolve every relative file path in the review request against that absolute root. Output only the resulting markdown review. Do not edit any files."
|
||||
cursor-agent -p --mode ask --trust --output-format text "$CURSOR_PROMPT_ARG" 2>/tmp/gsd-review-cursor-{phase}.err > /tmp/gsd-review-cursor-{phase}.md
|
||||
if [ ! -s /tmp/gsd-review-cursor-{phase}.md ]; then
|
||||
echo "Cursor review failed or returned empty output. stderr:" > /tmp/gsd-review-cursor-{phase}.md
|
||||
cat /tmp/gsd-review-cursor-{phase}.err >> /tmp/gsd-review-cursor-{phase}.md
|
||||
CURSOR_PROMPT_ARG="Read the file at {run_dir}/gsd-review-prompt.md in full and carry out the review request it contains. The repository under review is at $_CURSOR_ROOT — resolve every relative file path in the review request against that absolute root. Output only the resulting markdown review. Do not edit any files."
|
||||
cursor-agent -p --mode ask --trust --output-format text "$CURSOR_PROMPT_ARG" 2>{run_dir}/gsd-review-cursor.err > {run_dir}/gsd-review-cursor.md
|
||||
if [ ! -s {run_dir}/gsd-review-cursor.md ]; then
|
||||
echo "Cursor review failed or returned empty output. stderr:" > {run_dir}/gsd-review-cursor.md
|
||||
cat {run_dir}/gsd-review-cursor.err >> {run_dir}/gsd-review-cursor.md
|
||||
fi
|
||||
```
|
||||
|
||||
@@ -475,7 +487,7 @@ fi
|
||||
# #2176: anchor the prompt to the absolute repo root so repo-relative references
|
||||
# in the assembled review prompt resolve even on the no---add-dir fallback, and
|
||||
# require an explicit self-report if the reviewer still cannot read the repo.
|
||||
_AGY_PROMPT="Read the file at /tmp/gsd-review-prompt-{phase}.md in full and carry out the review request it contains. The repository under review is at $_AGY_WS — resolve every relative file path in the review request against that absolute root and verify claims against those files. If you cannot read files under $_AGY_WS, begin your output with the exact line REVIEWED-WITHOUT-REPO-ACCESS before the review. Output only the resulting markdown review. Do not edit any files."
|
||||
_AGY_PROMPT="Read the file at {run_dir}/gsd-review-prompt.md in full and carry out the review request it contains. The repository under review is at $_AGY_WS — resolve every relative file path in the review request against that absolute root and verify claims against those files. If you cannot read files under $_AGY_WS, begin your output with the exact line REVIEWED-WITHOUT-REPO-ACCESS before the review. Output only the resulting markdown review. Do not edit any files."
|
||||
# Capability-probe an external wall-clock killer (GNU coreutils `timeout` or the
|
||||
# macOS Homebrew `gtimeout`). Stock macOS ships NEITHER — a bare `timeout …` would
|
||||
# fail with rc 127 ("command not found") and silently lose the reviewer, so fall
|
||||
@@ -485,20 +497,20 @@ _AGY_PROMPT="Read the file at /tmp/gsd-review-prompt-{phase}.md in full and carr
|
||||
# healthy run. Mirrors the probe in scripts/base64-scan.sh.
|
||||
_AGY_KILLER="$(command -v timeout 2>/dev/null || command -v gtimeout 2>/dev/null || true)"
|
||||
if [ -n "$_AGY_KILLER" ]; then
|
||||
"$_AGY_KILLER" 600 agy --print-timeout 540s "$@" -p "$_AGY_PROMPT" </dev/null 2>/dev/null > /tmp/gsd-review-antigravity-{phase}.md
|
||||
"$_AGY_KILLER" 600 agy --print-timeout 540s "$@" -p "$_AGY_PROMPT" </dev/null 2>/dev/null > {run_dir}/gsd-review-antigravity.md
|
||||
else
|
||||
agy --print-timeout 540s "$@" -p "$_AGY_PROMPT" </dev/null 2>/dev/null > /tmp/gsd-review-antigravity-{phase}.md
|
||||
agy --print-timeout 540s "$@" -p "$_AGY_PROMPT" </dev/null 2>/dev/null > {run_dir}/gsd-review-antigravity.md
|
||||
fi
|
||||
_AGY_RC=$?
|
||||
if [ "$_AGY_RC" -ne 0 ]; then
|
||||
: > /tmp/gsd-review-antigravity-{phase}.md
|
||||
: > {run_dir}/gsd-review-antigravity.md
|
||||
fi
|
||||
|
||||
# Step 2 — transcript fallback: catches Windows agy -p stdout bug (and any future stdout-silent edge cases).
|
||||
# Reads only lines appended AFTER the pre-flight watermark. If agy failed before writing a new response,
|
||||
# _AGY_RESULT is empty and Step 3 fires — no stale content can leak through.
|
||||
# Undocumented paths, verified agy 1.0.0–1.0.2. See maintainer note above if these break.
|
||||
if [ ! -s /tmp/gsd-review-antigravity-{phase}.md ]; then
|
||||
if [ ! -s {run_dir}/gsd-review-antigravity.md ]; then
|
||||
if [ -f "$_AGY_CACHE" ]; then
|
||||
_AGY_CONV=$(jq -r --arg ws "$_AGY_WS" '
|
||||
.[$ws] //
|
||||
@@ -516,7 +528,7 @@ if [ ! -s /tmp/gsd-review-antigravity-{phase}.md ]; then
|
||||
_AGY_RESULT=$(tail -n +"$((_AGY_SKIP + 1))" "$_AGY_TX" 2>/dev/null | \
|
||||
jq -r 'select(.source=="MODEL" and .status=="DONE" and .type=="PLANNER_RESPONSE") | .content' \
|
||||
2>/dev/null | tail -1)
|
||||
[ -n "$_AGY_RESULT" ] && echo "$_AGY_RESULT" > /tmp/gsd-review-antigravity-{phase}.md
|
||||
[ -n "$_AGY_RESULT" ] && echo "$_AGY_RESULT" > {run_dir}/gsd-review-antigravity.md
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -524,7 +536,7 @@ fi
|
||||
|
||||
# Step 3 — final guard: both approaches yielded nothing (auth error, first-run setup,
|
||||
# path schema changed, 404'd pinned model, pre-session stall, etc.)
|
||||
if [ ! -s /tmp/gsd-review-antigravity-{phase}.md ]; then
|
||||
if [ ! -s {run_dir}/gsd-review-antigravity.md ]; then
|
||||
{
|
||||
echo "Antigravity review failed or returned empty output."
|
||||
# #2073 mode 2: a pinned model that 404s exits 0 with empty stdout AND an empty
|
||||
@@ -540,7 +552,7 @@ if [ ! -s /tmp/gsd-review-antigravity-{phase}.md ]; then
|
||||
fi
|
||||
# #2073 mode 3: pre-session stall tell — no new conversation dir appeared.
|
||||
echo "If no agy run started, that is the pre-session-stall case: check whether a new ~/.gemini/antigravity-cli/brain/<conv-id>/ dir appeared within ~30s of launch."
|
||||
} > /tmp/gsd-review-antigravity-{phase}.md
|
||||
} > {run_dir}/gsd-review-antigravity.md
|
||||
fi
|
||||
|
||||
# #2176: blind-review marker. Two tells that the reviewer ran without repo
|
||||
@@ -552,15 +564,15 @@ fi
|
||||
# never mis-stamped. Stamp a machine-readable marker so the Consensus Summary
|
||||
# down-weights the review instead of counting an ungrounded verdict at full
|
||||
# weight. (Temp file + mv, no in-place sed — BSD/GNU safe.)
|
||||
if [ -s /tmp/gsd-review-antigravity-{phase}.md ] && \
|
||||
{ head -5 /tmp/gsd-review-antigravity-{phase}.md | grep -q 'REVIEWED-WITHOUT-REPO-ACCESS' || \
|
||||
grep -qiE '(workspace|working) (directory|dir).{0,40}antigravity-cli/scratch' /tmp/gsd-review-antigravity-{phase}.md; }; then
|
||||
if [ -s {run_dir}/gsd-review-antigravity.md ] && \
|
||||
{ head -5 {run_dir}/gsd-review-antigravity.md | grep -q 'REVIEWED-WITHOUT-REPO-ACCESS' || \
|
||||
grep -qiE '(workspace|working) (directory|dir).{0,40}antigravity-cli/scratch' {run_dir}/gsd-review-antigravity.md; }; then
|
||||
{
|
||||
echo "> [reviewed-without-repo-access] This reviewer ran without visibility into the repo under review — down-weight its verdict in the Consensus Summary."
|
||||
echo ""
|
||||
cat /tmp/gsd-review-antigravity-{phase}.md
|
||||
} > /tmp/gsd-review-antigravity-{phase}.md.tmp && \
|
||||
mv /tmp/gsd-review-antigravity-{phase}.md.tmp /tmp/gsd-review-antigravity-{phase}.md
|
||||
cat {run_dir}/gsd-review-antigravity.md
|
||||
} > {run_dir}/gsd-review-antigravity.md.tmp && \
|
||||
mv {run_dir}/gsd-review-antigravity.md.tmp {run_dir}/gsd-review-antigravity.md
|
||||
fi
|
||||
```
|
||||
|
||||
@@ -581,22 +593,22 @@ prepare_trimmed_prompt_for_reviewer() {
|
||||
[ "$REVIEWER_BUDGET" = "0" ] && return 0
|
||||
|
||||
PLAN_FILE_ARGS=""
|
||||
for p in /tmp/gsd-review-{phase}-plan-*.md; do
|
||||
for p in {run_dir}/gsd-review-plan-*.md; do
|
||||
[ -f "$p" ] && PLAN_FILE_ARGS="$PLAN_FILE_ARGS --plan-file $p"
|
||||
done
|
||||
PROJECT_ARG=""
|
||||
[ -f "/tmp/gsd-review-{phase}-project.md" ] && PROJECT_ARG="--project-file /tmp/gsd-review-{phase}-project.md"
|
||||
[ -f "{run_dir}/gsd-review-project.md" ] && PROJECT_ARG="--project-file {run_dir}/gsd-review-project.md"
|
||||
CONTEXT_ARG=""
|
||||
[ -f "/tmp/gsd-review-{phase}-context.md" ] && CONTEXT_ARG="--context-file /tmp/gsd-review-{phase}-context.md"
|
||||
[ -f "{run_dir}/gsd-review-context.md" ] && CONTEXT_ARG="--context-file {run_dir}/gsd-review-context.md"
|
||||
RESEARCH_ARG=""
|
||||
[ -f "/tmp/gsd-review-{phase}-research.md" ] && RESEARCH_ARG="--research-file /tmp/gsd-review-{phase}-research.md"
|
||||
[ -f "{run_dir}/gsd-review-research.md" ] && RESEARCH_ARG="--research-file {run_dir}/gsd-review-research.md"
|
||||
REQUIREMENTS_ARG=""
|
||||
[ -f "/tmp/gsd-review-{phase}-requirements.md" ] && REQUIREMENTS_ARG="--requirements-file /tmp/gsd-review-{phase}-requirements.md"
|
||||
[ -f "{run_dir}/gsd-review-requirements.md" ] && REQUIREMENTS_ARG="--requirements-file {run_dir}/gsd-review-requirements.md"
|
||||
|
||||
gsd_run query prompt-budget \
|
||||
--budget "$REVIEWER_BUDGET" \
|
||||
--instructions-file "/tmp/gsd-review-{phase}-instructions.md" \
|
||||
--roadmap-file "/tmp/gsd-review-{phase}-roadmap.md" \
|
||||
--instructions-file "{run_dir}/gsd-review-instructions.md" \
|
||||
--roadmap-file "{run_dir}/gsd-review-roadmap.md" \
|
||||
$PLAN_FILE_ARGS $PROJECT_ARG $CONTEXT_ARG $RESEARCH_ARG $REQUIREMENTS_ARG \
|
||||
--output-prompt "$OUTPUT_PROMPT" \
|
||||
--output-metadata "$OUTPUT_META"
|
||||
@@ -610,11 +622,11 @@ if [ -z "$OLLAMA_REVIEWER_BUDGET" ] || [ "$OLLAMA_REVIEWER_BUDGET" = "null" ]; t
|
||||
fi
|
||||
|
||||
# Apply budget trim for Ollama if a budget is configured
|
||||
OLLAMA_PROMPT_FILE="/tmp/gsd-review-prompt-{phase}.md"
|
||||
OLLAMA_PROMPT_FILE="{run_dir}/gsd-review-prompt.md"
|
||||
OLLAMA_SKIP=0
|
||||
if [ -n "$OLLAMA_REVIEWER_BUDGET" ] && [ "$OLLAMA_REVIEWER_BUDGET" != "null" ] && [ "$OLLAMA_REVIEWER_BUDGET" != "0" ]; then
|
||||
OLLAMA_TRIMMED_PROMPT="/tmp/gsd-review-prompt-{phase}-ollama.md"
|
||||
OLLAMA_TRIM_META="/tmp/gsd-review-prompt-{phase}-ollama.metadata.json"
|
||||
OLLAMA_TRIMMED_PROMPT="{run_dir}/gsd-review-prompt-ollama.md"
|
||||
OLLAMA_TRIM_META="{run_dir}/gsd-review-prompt-ollama.metadata.json"
|
||||
prepare_trimmed_prompt_for_reviewer "ollama" "$OLLAMA_REVIEWER_BUDGET" "$OLLAMA_TRIMMED_PROMPT" "$OLLAMA_TRIM_META"
|
||||
OLLAMA_EXIT=$?
|
||||
if [ $OLLAMA_EXIT -ne 0 ]; then
|
||||
@@ -642,9 +654,9 @@ jq -n --rawfile content "$OLLAMA_PROMPT_FILE" \
|
||||
curl -s --max-time 120 -X POST "${OLLAMA_HOST}/v1/chat/completions" \
|
||||
-H "Content-Type: application/json" -d @- 2>/dev/null | \
|
||||
jq -r '.choices[0].message.content // "Ollama review failed or returned empty output."' \
|
||||
> /tmp/gsd-review-ollama-{phase}.md
|
||||
if [ ! -s /tmp/gsd-review-ollama-{phase}.md ]; then
|
||||
echo "Ollama review failed or returned empty output." > /tmp/gsd-review-ollama-{phase}.md
|
||||
> {run_dir}/gsd-review-ollama.md
|
||||
if [ ! -s {run_dir}/gsd-review-ollama.md ]; then
|
||||
echo "Ollama review failed or returned empty output." > {run_dir}/gsd-review-ollama.md
|
||||
fi
|
||||
fi
|
||||
```
|
||||
@@ -658,11 +670,11 @@ if [ -z "$LM_STUDIO_REVIEWER_BUDGET" ] || [ "$LM_STUDIO_REVIEWER_BUDGET" = "null
|
||||
fi
|
||||
|
||||
# Apply budget trim for LM Studio if a budget is configured
|
||||
LM_STUDIO_PROMPT_FILE="/tmp/gsd-review-prompt-{phase}.md"
|
||||
LM_STUDIO_PROMPT_FILE="{run_dir}/gsd-review-prompt.md"
|
||||
LM_STUDIO_SKIP=0
|
||||
if [ -n "$LM_STUDIO_REVIEWER_BUDGET" ] && [ "$LM_STUDIO_REVIEWER_BUDGET" != "null" ] && [ "$LM_STUDIO_REVIEWER_BUDGET" != "0" ]; then
|
||||
LM_STUDIO_TRIMMED_PROMPT="/tmp/gsd-review-prompt-{phase}-lm_studio.md"
|
||||
LM_STUDIO_TRIM_META="/tmp/gsd-review-prompt-{phase}-lm_studio.metadata.json"
|
||||
LM_STUDIO_TRIMMED_PROMPT="{run_dir}/gsd-review-prompt-lm_studio.md"
|
||||
LM_STUDIO_TRIM_META="{run_dir}/gsd-review-prompt-lm_studio.metadata.json"
|
||||
prepare_trimmed_prompt_for_reviewer "lm_studio" "$LM_STUDIO_REVIEWER_BUDGET" "$LM_STUDIO_TRIMMED_PROMPT" "$LM_STUDIO_TRIM_META"
|
||||
LM_STUDIO_EXIT=$?
|
||||
if [ $LM_STUDIO_EXIT -ne 0 ]; then
|
||||
@@ -695,7 +707,7 @@ if [ -n "$LM_STUDIO_ACTUAL_MODEL" ] && [ "$LM_STUDIO_ACTUAL_MODEL" != "null" ] &
|
||||
fi
|
||||
LM_STUDIO_CONTENT=$(echo "$LM_STUDIO_RESPONSE" | jq -r '.choices[0].message.content // ""' 2>/dev/null || echo "")
|
||||
if [ -n "$LM_STUDIO_CONTENT" ]; then
|
||||
echo "$LM_STUDIO_CONTENT" > /tmp/gsd-review-lm_studio-{phase}.md
|
||||
echo "$LM_STUDIO_CONTENT" > {run_dir}/gsd-review-lm_studio.md
|
||||
else
|
||||
echo "Warning: LM Studio returned empty content — skipping review." >&2
|
||||
fi
|
||||
@@ -711,11 +723,11 @@ if [ -z "$LLAMA_CPP_REVIEWER_BUDGET" ] || [ "$LLAMA_CPP_REVIEWER_BUDGET" = "null
|
||||
fi
|
||||
|
||||
# Apply budget trim for llama.cpp if a budget is configured
|
||||
LLAMA_CPP_PROMPT_FILE="/tmp/gsd-review-prompt-{phase}.md"
|
||||
LLAMA_CPP_PROMPT_FILE="{run_dir}/gsd-review-prompt.md"
|
||||
LLAMA_CPP_SKIP=0
|
||||
if [ -n "$LLAMA_CPP_REVIEWER_BUDGET" ] && [ "$LLAMA_CPP_REVIEWER_BUDGET" != "null" ] && [ "$LLAMA_CPP_REVIEWER_BUDGET" != "0" ]; then
|
||||
LLAMA_CPP_TRIMMED_PROMPT="/tmp/gsd-review-prompt-{phase}-llama_cpp.md"
|
||||
LLAMA_CPP_TRIM_META="/tmp/gsd-review-prompt-{phase}-llama_cpp.metadata.json"
|
||||
LLAMA_CPP_TRIMMED_PROMPT="{run_dir}/gsd-review-prompt-llama_cpp.md"
|
||||
LLAMA_CPP_TRIM_META="{run_dir}/gsd-review-prompt-llama_cpp.metadata.json"
|
||||
prepare_trimmed_prompt_for_reviewer "llama_cpp" "$LLAMA_CPP_REVIEWER_BUDGET" "$LLAMA_CPP_TRIMMED_PROMPT" "$LLAMA_CPP_TRIM_META"
|
||||
LLAMA_CPP_EXIT=$?
|
||||
if [ $LLAMA_CPP_EXIT -ne 0 ]; then
|
||||
@@ -744,7 +756,7 @@ LLAMA_CPP_CONTENT=$(jq -n --rawfile content "$LLAMA_CPP_PROMPT_FILE" \
|
||||
-H "Content-Type: application/json" -d @- 2>/dev/null | \
|
||||
jq -r '.choices[0].message.content // ""' 2>/dev/null || echo "")
|
||||
if [ -n "$LLAMA_CPP_CONTENT" ]; then
|
||||
echo "$LLAMA_CPP_CONTENT" > /tmp/gsd-review-llama_cpp-{phase}.md
|
||||
echo "$LLAMA_CPP_CONTENT" > {run_dir}/gsd-review-llama_cpp.md
|
||||
else
|
||||
echo "Warning: llama.cpp returned empty content — skipping review." >&2
|
||||
fi
|
||||
@@ -911,7 +923,11 @@ To incorporate feedback into planning:
|
||||
/gsd:plan-phase {N} --reviews
|
||||
```
|
||||
|
||||
Clean up temp files.
|
||||
Clean up — remove the run's temp directory now that REVIEWS.md is committed:
|
||||
|
||||
```bash
|
||||
rm -rf "{run_dir}"
|
||||
```
|
||||
</step>
|
||||
|
||||
</process>
|
||||
|
||||
@@ -324,7 +324,11 @@ If `REVIEW_CMD` is non-empty and not `"null"`, run the external review:
|
||||
Construct a review prompt containing the diff, diff stats, and phase context, then pipe it to the configured command:
|
||||
```bash
|
||||
REVIEW_PROMPT="You are reviewing a pull request.\n\nDiff stats:\n${DIFF_STATS}\n\nPhase context:\n${STATE_STATUS}\n\nFull diff:\n${DIFF}\n\nRespond with JSON: { \"verdict\": \"APPROVED\" or \"REVISE\", \"confidence\": 0-100, \"summary\": \"...\", \"issues\": [{\"severity\": \"...\", \"file\": \"...\", \"line_range\": \"...\", \"description\": \"...\", \"suggestion\": \"...\"}] }"
|
||||
REVIEW_OUTPUT=$(echo "${REVIEW_PROMPT}" | timeout 120 ${REVIEW_CMD} 2>/tmp/gsd-review-stderr.log)
|
||||
# #2358: a per-run temp file (not a shared, unqualified path) so concurrent
|
||||
# ship runs — same or different phase, same or different project — never
|
||||
# clobber or read each other's stderr. Portable via ${TMPDIR:-/tmp}.
|
||||
REVIEW_STDERR_FILE=$(mktemp "${TMPDIR:-/tmp}/gsd-review-stderr-XXXXXX")
|
||||
REVIEW_OUTPUT=$(echo "${REVIEW_PROMPT}" | timeout 120 ${REVIEW_CMD} 2>"${REVIEW_STDERR_FILE}")
|
||||
REVIEW_EXIT=$?
|
||||
```
|
||||
|
||||
@@ -332,10 +336,11 @@ If `REVIEW_CMD` is non-empty and not `"null"`, run the external review:
|
||||
If `REVIEW_EXIT` is non-zero or the command times out:
|
||||
```bash
|
||||
if [ $REVIEW_EXIT -ne 0 ]; then
|
||||
REVIEW_STDERR=$(cat /tmp/gsd-review-stderr.log 2>/dev/null)
|
||||
REVIEW_STDERR=$(cat "${REVIEW_STDERR_FILE}" 2>/dev/null)
|
||||
echo "WARNING: External review command failed (exit ${REVIEW_EXIT}). stderr: ${REVIEW_STDERR}"
|
||||
echo "Continuing with manual review flow..."
|
||||
fi
|
||||
rm -f "${REVIEW_STDERR_FILE}"
|
||||
```
|
||||
On failure, warn with stderr output and fall through to the manual review flow below.
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ describe('Antigravity reviewer repo grounding in /gsd-review (#2176)', () => {
|
||||
.replace(/\}; then$/, '}')
|
||||
// Convert the native path to POSIX form so it survives bash on Windows
|
||||
// runners (Git Bash accepts D:/... but eats backslashes).
|
||||
.replaceAll('/tmp/gsd-review-antigravity-{phase}.md', toPosixPath(out));
|
||||
.replaceAll('{run_dir}/gsd-review-antigravity.md', toPosixPath(out));
|
||||
const runDetect = (content) => {
|
||||
fs.writeFileSync(out, content);
|
||||
try {
|
||||
|
||||
@@ -52,7 +52,7 @@ describe('Antigravity (agy) reviewer invocation in /gsd-review (#2073)', () => {
|
||||
test('#2073 mode 1 — uses a file-reference prompt (mirrors the Cursor block)', () => {
|
||||
const block = agyBashBlock();
|
||||
assert.ok(
|
||||
/Read the file at \/tmp\/gsd-review-prompt-/.test(block),
|
||||
/Read the file at \{run_dir\}\/gsd-review-prompt\.md/.test(block),
|
||||
'agy should reference the prompt by file path, not inline it',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -79,7 +79,7 @@ describe('Cursor CLI reviewer in /gsd-review (#1960)', () => {
|
||||
test('invocation passes prompt as a file-path argument (not via stdin pipe)', () => {
|
||||
const c = fs.readFileSync(reviewPath, 'utf-8');
|
||||
assert.ok(
|
||||
c.includes('Read the file at /tmp/gsd-review-prompt-'),
|
||||
c.includes('Read the file at {run_dir}/gsd-review-prompt.md'),
|
||||
'review.md cursor-agent invocation should pass prompt by referencing the file path as an argument'
|
||||
);
|
||||
});
|
||||
|
||||
201
tests/fix-2358-review-temp-path-scoping.test.cjs
Normal file
201
tests/fix-2358-review-temp-path-scoping.test.cjs
Normal file
@@ -0,0 +1,201 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* #2358 — review.md (and ship.md's external peer-review step) wrote every
|
||||
* temp file to a hardcoded, phase-number-only path under /tmp
|
||||
* (`/tmp/gsd-review-prompt-{phase}.md`, `/tmp/gsd-review-<reviewer>-{phase}.*`,
|
||||
* `/tmp/gsd-review-stderr.log`). Two GSD projects with a phase sharing the
|
||||
* same small integer number collide on the exact same path; a crashed prior
|
||||
* run's leftover file is bait a later, unrelated run can silently read (the
|
||||
* reporter forensically confirmed agy read a 3-week-old stale prompt from a
|
||||
* DIFFERENT project). Neither file used the portable ${TMPDIR:-/tmp} seam,
|
||||
* and review.md had no cleanup.
|
||||
*
|
||||
* The fix threads a single `mktemp -d "${TMPDIR:-/tmp}/gsd-review-XXXXXX"`
|
||||
* run directory (RUN_DIR / {run_dir}) through every review.md temp path, and
|
||||
* ship.md's stderr capture through a per-run `mktemp` file — eliminating the
|
||||
* shared-path collision by construction rather than by convention.
|
||||
*
|
||||
* review.md and ship.md ARE the product the runtime loads (an AI agent reads
|
||||
* and executes these workflow instructions verbatim), so this is a
|
||||
* static-content regression against the deployed text, mirroring
|
||||
* fix-2194-review-timeout-guidance.test.cjs.
|
||||
*/
|
||||
|
||||
const { describe, test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
|
||||
const REVIEW_MD = path.join(__dirname, '..', 'gsd-core', 'workflows', 'review.md');
|
||||
const SHIP_MD = path.join(__dirname, '..', 'gsd-core', 'workflows', 'ship.md');
|
||||
const REVIEWER_INSTANCES_MD = path.join(__dirname, '..', 'gsd-core', 'references', 'reviewer-instances.md');
|
||||
|
||||
describe('#2358 review.md temp paths are run-scoped, not phase-only', () => {
|
||||
const content = fs.readFileSync(REVIEW_MD, 'utf-8');
|
||||
|
||||
test('no bare, unscoped /tmp/gsd-review-* path remains', () => {
|
||||
assert.ok(
|
||||
!content.includes('/tmp/gsd-review'),
|
||||
'review.md must not contain any hardcoded /tmp/gsd-review* literal — ' +
|
||||
'every review temp path must be rooted under the run-scoped mktemp directory'
|
||||
);
|
||||
});
|
||||
|
||||
test('creates exactly one run-scoped directory via the portable ${TMPDIR:-/tmp} seam', () => {
|
||||
const mktempAssignments = content.match(/RUN_DIR=\$\(mktemp -d "\$\{TMPDIR:-\/tmp\}\/gsd-review-XXXXXX"\)/g) || [];
|
||||
assert.equal(
|
||||
mktempAssignments.length, 1,
|
||||
'review.md must create the run directory with exactly one `mktemp -d "${TMPDIR:-/tmp}/gsd-review-XXXXXX"` — ' +
|
||||
'a hardcoded /tmp (no ${TMPDIR:-/tmp} seam) breaks on Windows, and re-mktemp-ing per block would break the ' +
|
||||
'write/read pairing between build_prompt and the local-reviewer budget-trimming reads'
|
||||
);
|
||||
});
|
||||
|
||||
test('every downstream temp path is threaded through {run_dir} / $RUN_DIR, not re-derived from {phase}', () => {
|
||||
assert.ok(
|
||||
/\{run_dir\}\/gsd-review-/.test(content),
|
||||
'reviewer blocks must reference {run_dir}/gsd-review-... (the run-scoped placeholder)'
|
||||
);
|
||||
assert.ok(
|
||||
/\$\{RUN_DIR\}\/gsd-review-/.test(content),
|
||||
'the build_prompt section-file writes must reference ${RUN_DIR}/gsd-review-... (the run-scoped shell var)'
|
||||
);
|
||||
// The old isolation key must be gone entirely from path construction.
|
||||
assert.ok(
|
||||
!/\/tmp\/gsd-review[^\r\n]*\{phase\}/.test(content),
|
||||
'no temp path may still be keyed on a bare {phase} placeholder'
|
||||
);
|
||||
assert.ok(
|
||||
!/\$\{PHASE\}-(?:instructions|roadmap|plan|project|context|research|requirements)\.md/.test(content),
|
||||
'no temp path may still be keyed on the ${PHASE} shell var'
|
||||
);
|
||||
});
|
||||
|
||||
test('the Antigravity reviewer prompt-instruction string references the run-scoped path', () => {
|
||||
const agyPromptMatch = content.match(/_AGY_PROMPT="Read the file at ([^ ]+)/);
|
||||
assert.ok(agyPromptMatch, '_AGY_PROMPT must contain a "Read the file at <path>" instruction');
|
||||
assert.equal(
|
||||
agyPromptMatch[1], '{run_dir}/gsd-review-prompt.md',
|
||||
'the Antigravity reviewer must be told to read the run-scoped prompt path, not a bare {phase}-only /tmp path — ' +
|
||||
'this is the exact instruction text the reporter forensically traced back to a stale cross-project read'
|
||||
);
|
||||
});
|
||||
|
||||
test('the Cursor reviewer prompt-instruction string references the run-scoped path', () => {
|
||||
const cursorPromptMatch = content.match(/CURSOR_PROMPT_ARG="Read the file at ([^ ]+)/);
|
||||
assert.ok(cursorPromptMatch, 'CURSOR_PROMPT_ARG must contain a "Read the file at <path>" instruction');
|
||||
assert.equal(
|
||||
cursorPromptMatch[1], '{run_dir}/gsd-review-prompt.md',
|
||||
'the Cursor reviewer must be told to read the run-scoped prompt path, not a bare {phase}-only /tmp path'
|
||||
);
|
||||
});
|
||||
|
||||
test('the run directory is cleaned up at the end of the review', () => {
|
||||
const presentResultsStart = content.indexOf('<step name="present_results">');
|
||||
assert.notEqual(presentResultsStart, -1, 'review.md must contain the present_results step');
|
||||
const section = content.slice(presentResultsStart, presentResultsStart + 1500);
|
||||
assert.ok(
|
||||
/rm -rf "\{run_dir\}"/.test(section),
|
||||
'present_results must remove the run-scoped temp directory once REVIEWS.md is written'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#2358 ship.md external-review stderr capture is run-scoped', () => {
|
||||
const content = fs.readFileSync(SHIP_MD, 'utf-8');
|
||||
|
||||
test('no bare, unqualified /tmp/gsd-review-stderr.log path remains', () => {
|
||||
assert.ok(
|
||||
!content.includes('/tmp/gsd-review-stderr.log'),
|
||||
'ship.md must not write/read a shared, unqualified stderr log path — every ship run, phase, and project ' +
|
||||
'shares this exact path with zero disambiguator, which is strictly worse than review.md\'s phase-only keying'
|
||||
);
|
||||
});
|
||||
|
||||
test('stderr is captured to a per-run file via the portable ${TMPDIR:-/tmp} seam', () => {
|
||||
assert.ok(
|
||||
/REVIEW_STDERR_FILE=\$\(mktemp "\$\{TMPDIR:-\/tmp\}\/gsd-review-stderr-XXXXXX"\)/.test(content),
|
||||
'ship.md must create the stderr capture file via `mktemp "${TMPDIR:-/tmp}/gsd-review-stderr-XXXXXX"`'
|
||||
);
|
||||
assert.ok(
|
||||
/2>"\$\{REVIEW_STDERR_FILE\}"/.test(content),
|
||||
'the external review command invocation must redirect stderr to the per-run $REVIEW_STDERR_FILE, not a literal path'
|
||||
);
|
||||
assert.ok(
|
||||
/cat "\$\{REVIEW_STDERR_FILE\}"/.test(content),
|
||||
'the failure-handling block must read back the same per-run $REVIEW_STDERR_FILE'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#2358 reviewer-instances.md (#1517, lazily loaded from invoke_reviewers) is run-scoped too', () => {
|
||||
// review.md's own invoke_reviewers step lazily loads this companion doc for the
|
||||
// review.reviewer_instances codepath — it was missed in the initial pass and still
|
||||
// pointed at the old, unscoped /tmp/gsd-review-*-{phase} paths, which both broke
|
||||
// reviewer-instances functionality (the prompt file build_prompt now writes lives
|
||||
// at {run_dir}/gsd-review-prompt.md, never the old path) and left the exact
|
||||
// cross-project collision bug open for that code path.
|
||||
const content = fs.readFileSync(REVIEWER_INSTANCES_MD, 'utf-8');
|
||||
|
||||
test('no bare, unscoped /tmp/gsd-review-* path remains', () => {
|
||||
assert.ok(
|
||||
!content.includes('/tmp/gsd-review'),
|
||||
'reviewer-instances.md must not contain any hardcoded /tmp/gsd-review* literal — ' +
|
||||
'every review temp path must be rooted under the run-scoped {run_dir} directory'
|
||||
);
|
||||
});
|
||||
|
||||
test('no temp path is still keyed on a bare {phase} placeholder', () => {
|
||||
assert.ok(
|
||||
!/gsd-review[^\r\n]*\{phase\}/.test(content),
|
||||
'no temp path may still be keyed on a bare {phase} placeholder'
|
||||
);
|
||||
});
|
||||
|
||||
test('the instance prompt read and output write are threaded through {run_dir}', () => {
|
||||
assert.ok(
|
||||
/\{run_dir\}\/gsd-review-prompt\.md/.test(content),
|
||||
'reviewer-instances.md must read the prompt from {run_dir}/gsd-review-prompt.md, ' +
|
||||
'the same run-scoped path build_prompt writes in review.md'
|
||||
);
|
||||
assert.ok(
|
||||
/\{run_dir\}\/gsd-review-\$\{INSTANCE_NAME\}\.md/.test(content),
|
||||
'reviewer-instances.md must write each instance\'s output to ' +
|
||||
'{run_dir}/gsd-review-${INSTANCE_NAME}.md, not a {phase}-keyed /tmp path'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#2358 design principle: run-scoped temp dirs never collide across projects/phases', () => {
|
||||
// review.md and ship.md are markdown instructions an AI agent executes, not
|
||||
// node-executable code, so this does not shell out to the literal snippet —
|
||||
// it validates the underlying guarantee the fix relies on (mktemp-style
|
||||
// randomized-suffix isolation) using Node's built-in equivalent, which is
|
||||
// cross-platform (Windows included) unlike shelling out to `mktemp`/bash.
|
||||
test('two runs — even for the same phase number, same or different project — get distinct run dirs', () => {
|
||||
const prefix = path.join(os.tmpdir(), 'gsd-review-');
|
||||
const runDirA = fs.mkdtempSync(prefix);
|
||||
const runDirB = fs.mkdtempSync(prefix);
|
||||
try {
|
||||
assert.notEqual(
|
||||
runDirA, runDirB,
|
||||
'two review runs sharing the same phase number must never resolve to the same run-scoped directory'
|
||||
);
|
||||
const phase = '10'; // same phase number in both "projects" — the historical collision case
|
||||
const staleProjectAPath = path.join(runDirA, `gsd-review-prompt.md`);
|
||||
const laterProjectBPath = path.join(runDirB, `gsd-review-prompt.md`);
|
||||
assert.notEqual(
|
||||
staleProjectAPath, laterProjectBPath,
|
||||
`phase ${phase} in two different runs must not resolve to the same prompt path`
|
||||
);
|
||||
} finally {
|
||||
// helpers.cleanup (not raw fs.rmSync) carries the Windows-EBUSY retry budget.
|
||||
cleanup(runDirA);
|
||||
cleanup(runDirB);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -129,7 +129,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "a602b41bfa081fdf",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "1412472f858b8f41",
|
||||
"gsd-core/references/reviewer-instances.md": "a857fc444baec27a",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -295,14 +295,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "23b9eb0858a2535e",
|
||||
"gsd-core/workflows/remove-workspace.md": "d0bd7e0601138798",
|
||||
"gsd-core/workflows/resume-project.md": "98e2cf8908e73a52",
|
||||
"gsd-core/workflows/review.md": "c4622380e61615c8",
|
||||
"gsd-core/workflows/review.md": "6f6575621367b9ff",
|
||||
"gsd-core/workflows/scan.md": "a7fecd67e5cd655f",
|
||||
"gsd-core/workflows/secure-phase.md": "52ddc46233e8fa66",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "6d25100a9de15b31",
|
||||
"gsd-core/workflows/settings-integrations.md": "4d3001ad2b5dad8f",
|
||||
"gsd-core/workflows/settings.md": "8258f7bd3700d608",
|
||||
"gsd-core/workflows/ship.md": "82a63ca54f4b322f",
|
||||
"gsd-core/workflows/ship.md": "782e5998a4257acf",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "0f842a609851a401",
|
||||
"gsd-core/workflows/sketch.md": "314b7d323c6b57eb",
|
||||
"gsd-core/workflows/smart-entry.md": "3ce5b6228238fdb6",
|
||||
|
||||
@@ -200,7 +200,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "62930e66cc979c1a",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "385501e4f9bbb31d",
|
||||
"gsd-core/references/reviewer-instances.md": "ae64e44f2f132bc2",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -366,14 +366,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "3ce57633978035ad",
|
||||
"gsd-core/workflows/review.md": "f1fbefbd64bd890e",
|
||||
"gsd-core/workflows/scan.md": "003883d71c37da7d",
|
||||
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
|
||||
"gsd-core/workflows/settings-integrations.md": "70515c5838fb9826",
|
||||
"gsd-core/workflows/settings.md": "d96ddf01fb85e61e",
|
||||
"gsd-core/workflows/ship.md": "b377dab2bc92a6e5",
|
||||
"gsd-core/workflows/ship.md": "f486f2731ca8b72a",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "5f5ebb6a80d610c6",
|
||||
"gsd-core/workflows/sketch.md": "8319cedf3f93fc35",
|
||||
"gsd-core/workflows/smart-entry.md": "449238eb94abe187",
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "62930e66cc979c1a",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "1412472f858b8f41",
|
||||
"gsd-core/references/reviewer-instances.md": "a857fc444baec27a",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -365,14 +365,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "8effc8742d58a11a",
|
||||
"gsd-core/workflows/remove-workspace.md": "10882656198d9075",
|
||||
"gsd-core/workflows/resume-project.md": "af9761bcec0f6fe9",
|
||||
"gsd-core/workflows/review.md": "15b8defb2d791092",
|
||||
"gsd-core/workflows/review.md": "8c05b76fe9d00c30",
|
||||
"gsd-core/workflows/scan.md": "75c670d08cee8680",
|
||||
"gsd-core/workflows/secure-phase.md": "8030d2b2a5bfdf07",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "94d61da368e9f85b",
|
||||
"gsd-core/workflows/settings-integrations.md": "dfe3672c4fabf139",
|
||||
"gsd-core/workflows/settings.md": "acdd79110699a608",
|
||||
"gsd-core/workflows/ship.md": "2e50ecfb12d48282",
|
||||
"gsd-core/workflows/ship.md": "3a0262c1197cf2f8",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "d52a5462bafda830",
|
||||
"gsd-core/workflows/sketch.md": "dbe6acc4d976060c",
|
||||
"gsd-core/workflows/smart-entry.md": "1850447c045f36d8",
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "62930e66cc979c1a",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "1412472f858b8f41",
|
||||
"gsd-core/references/reviewer-instances.md": "a857fc444baec27a",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -294,14 +294,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
|
||||
"gsd-core/workflows/remove-workspace.md": "f3ab3a88a7e9e1ed",
|
||||
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
|
||||
"gsd-core/workflows/review.md": "f1dbb3769d461ec2",
|
||||
"gsd-core/workflows/review.md": "013126685d8a9d50",
|
||||
"gsd-core/workflows/scan.md": "47371c2073d6c0be",
|
||||
"gsd-core/workflows/secure-phase.md": "d6ac1f4db6a5da75",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "339def28c34b0797",
|
||||
"gsd-core/workflows/settings-integrations.md": "53649313d20694ae",
|
||||
"gsd-core/workflows/settings.md": "7e7458cdb2b68ec5",
|
||||
"gsd-core/workflows/ship.md": "63e65d06a6038e8e",
|
||||
"gsd-core/workflows/ship.md": "cc8428f393c44f67",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "121ed4b8127abf04",
|
||||
"gsd-core/workflows/sketch.md": "737c0492686fea2d",
|
||||
"gsd-core/workflows/smart-entry.md": "ad20cf74ae2e8291",
|
||||
|
||||
@@ -132,7 +132,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "62930e66cc979c1a",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "385501e4f9bbb31d",
|
||||
"gsd-core/references/reviewer-instances.md": "ae64e44f2f132bc2",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -298,14 +298,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "e336350f8113a328",
|
||||
"gsd-core/workflows/remove-workspace.md": "e685dfbd736dfd90",
|
||||
"gsd-core/workflows/resume-project.md": "e23981178fa37b3d",
|
||||
"gsd-core/workflows/review.md": "8c996dc10e923685",
|
||||
"gsd-core/workflows/review.md": "075ea1aa6a2b8339",
|
||||
"gsd-core/workflows/scan.md": "dfd92717caea0ce7",
|
||||
"gsd-core/workflows/secure-phase.md": "00de56d6d993bb2c",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "69f3a19bf2c61160",
|
||||
"gsd-core/workflows/settings-integrations.md": "76eee76d6eb57657",
|
||||
"gsd-core/workflows/settings.md": "3701faed09d55247",
|
||||
"gsd-core/workflows/ship.md": "baf53afdff3601d4",
|
||||
"gsd-core/workflows/ship.md": "416cfe6c07694753",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "1f44789553180d84",
|
||||
"gsd-core/workflows/sketch.md": "2226779b6003a71c",
|
||||
"gsd-core/workflows/smart-entry.md": "9a64f43927641ca4",
|
||||
|
||||
@@ -200,7 +200,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "62930e66cc979c1a",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "385501e4f9bbb31d",
|
||||
"gsd-core/references/reviewer-instances.md": "ae64e44f2f132bc2",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -366,14 +366,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "3ce57633978035ad",
|
||||
"gsd-core/workflows/review.md": "f1fbefbd64bd890e",
|
||||
"gsd-core/workflows/scan.md": "003883d71c37da7d",
|
||||
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
|
||||
"gsd-core/workflows/settings-integrations.md": "70515c5838fb9826",
|
||||
"gsd-core/workflows/settings.md": "d96ddf01fb85e61e",
|
||||
"gsd-core/workflows/ship.md": "b377dab2bc92a6e5",
|
||||
"gsd-core/workflows/ship.md": "f486f2731ca8b72a",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "2aba89ecd8f41a0d",
|
||||
"gsd-core/workflows/sketch.md": "5eedd93f9a5b49d5",
|
||||
"gsd-core/workflows/smart-entry.md": "449238eb94abe187",
|
||||
|
||||
@@ -235,7 +235,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "a602b41bfa081fdf",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "335a974ea98d5d83",
|
||||
"gsd-core/references/reviewer-instances.md": "1a677abcfba49da4",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -401,14 +401,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "9ee0fddd11a0d9d4",
|
||||
"gsd-core/workflows/remove-workspace.md": "19d7465aaa50cb62",
|
||||
"gsd-core/workflows/resume-project.md": "9965f87eb278f7f8",
|
||||
"gsd-core/workflows/review.md": "a980007626185b70",
|
||||
"gsd-core/workflows/review.md": "e4e61845190da702",
|
||||
"gsd-core/workflows/scan.md": "1a3caa5d724d39e9",
|
||||
"gsd-core/workflows/secure-phase.md": "ab387a4bca381c18",
|
||||
"gsd-core/workflows/session-report.md": "dd8fa011c9394075",
|
||||
"gsd-core/workflows/settings-advanced.md": "2431433811616f76",
|
||||
"gsd-core/workflows/settings-integrations.md": "77730321d3d6d317",
|
||||
"gsd-core/workflows/settings.md": "054c8c31b3905ced",
|
||||
"gsd-core/workflows/ship.md": "b5ac8caee76f3727",
|
||||
"gsd-core/workflows/ship.md": "779e345e22f3f974",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "07724a390fbb43f6",
|
||||
"gsd-core/workflows/sketch.md": "576f300dfdde1b7d",
|
||||
"gsd-core/workflows/smart-entry.md": "6f686195ae531b03",
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "a602b41bfa081fdf",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "1412472f858b8f41",
|
||||
"gsd-core/references/reviewer-instances.md": "a857fc444baec27a",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -296,14 +296,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "e262654e319d1bc4",
|
||||
"gsd-core/workflows/remove-workspace.md": "ceddfeef5f2d6754",
|
||||
"gsd-core/workflows/resume-project.md": "40db7f350f5866d8",
|
||||
"gsd-core/workflows/review.md": "1f44a33c50b1cd36",
|
||||
"gsd-core/workflows/review.md": "a9f422db38ab8bb9",
|
||||
"gsd-core/workflows/scan.md": "dcc2f76d0850e2fb",
|
||||
"gsd-core/workflows/secure-phase.md": "9bec6635ee1cbaed",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "230a658de9c017a6",
|
||||
"gsd-core/workflows/settings-integrations.md": "a1d146d6bfd14db5",
|
||||
"gsd-core/workflows/settings.md": "f611f14f2f447f1e",
|
||||
"gsd-core/workflows/ship.md": "dc4ac0166a559f98",
|
||||
"gsd-core/workflows/ship.md": "3d0022d2d3ed8f78",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "f2590cb6ddbfad94",
|
||||
"gsd-core/workflows/sketch.md": "e2063966439af8c1",
|
||||
"gsd-core/workflows/smart-entry.md": "6c707b959a41900b",
|
||||
|
||||
@@ -200,7 +200,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "62930e66cc979c1a",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "1412472f858b8f41",
|
||||
"gsd-core/references/reviewer-instances.md": "a857fc444baec27a",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -366,14 +366,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
|
||||
"gsd-core/workflows/remove-workspace.md": "433affcd1a200826",
|
||||
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
|
||||
"gsd-core/workflows/review.md": "ad115fadce422eae",
|
||||
"gsd-core/workflows/review.md": "ea18faf9ff633667",
|
||||
"gsd-core/workflows/scan.md": "47371c2073d6c0be",
|
||||
"gsd-core/workflows/secure-phase.md": "3063b0b6f7b56d46",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "bf8ea69c8f7ae019",
|
||||
"gsd-core/workflows/settings-integrations.md": "166ca51b1f33c2a3",
|
||||
"gsd-core/workflows/settings.md": "0623c673eaf04799",
|
||||
"gsd-core/workflows/ship.md": "8a2504f02df7529d",
|
||||
"gsd-core/workflows/ship.md": "36603e9c574e8374",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "5be73b7bdf96b539",
|
||||
"gsd-core/workflows/sketch.md": "373bc0d83368a411",
|
||||
"gsd-core/workflows/smart-entry.md": "c8fc316358cdcd7b",
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "62930e66cc979c1a",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "1412472f858b8f41",
|
||||
"gsd-core/references/reviewer-instances.md": "a857fc444baec27a",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -295,14 +295,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "fce799aae3ab2715",
|
||||
"gsd-core/workflows/remove-workspace.md": "8facde381657dd71",
|
||||
"gsd-core/workflows/resume-project.md": "a0443839f1f83c2d",
|
||||
"gsd-core/workflows/review.md": "d8c53e495b067afe",
|
||||
"gsd-core/workflows/review.md": "cde982eb704261ef",
|
||||
"gsd-core/workflows/scan.md": "ebc3faaf1170dd12",
|
||||
"gsd-core/workflows/secure-phase.md": "5284cfc143ad0e2c",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "49be159144d7f426",
|
||||
"gsd-core/workflows/settings-integrations.md": "1dce76db0aca08a5",
|
||||
"gsd-core/workflows/settings.md": "9ced580679ed0255",
|
||||
"gsd-core/workflows/ship.md": "9ec9f316622ccfd9",
|
||||
"gsd-core/workflows/ship.md": "36029cf37057f466",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "f1ece50ac65ea281",
|
||||
"gsd-core/workflows/sketch.md": "d5887983e62b574a",
|
||||
"gsd-core/workflows/smart-entry.md": "47f5c5e8608e5f7a",
|
||||
|
||||
@@ -200,7 +200,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "a602b41bfa081fdf",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "1412472f858b8f41",
|
||||
"gsd-core/references/reviewer-instances.md": "a857fc444baec27a",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -366,14 +366,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
|
||||
"gsd-core/workflows/remove-workspace.md": "fc83f362a2d0a1b7",
|
||||
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
|
||||
"gsd-core/workflows/review.md": "3d62fa3096f46ecb",
|
||||
"gsd-core/workflows/review.md": "6033fab6d070c993",
|
||||
"gsd-core/workflows/scan.md": "47371c2073d6c0be",
|
||||
"gsd-core/workflows/secure-phase.md": "4977cf9e0462745b",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "edd858cd6cfddaf1",
|
||||
"gsd-core/workflows/settings-integrations.md": "3ea8095d5fad891b",
|
||||
"gsd-core/workflows/settings.md": "1925ecc2225c2216",
|
||||
"gsd-core/workflows/ship.md": "8c68f5a43d9fd578",
|
||||
"gsd-core/workflows/ship.md": "e8ea6299b57fef04",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "888c0548e63197b3",
|
||||
"gsd-core/workflows/sketch.md": "34a0c10fa56af7ea",
|
||||
"gsd-core/workflows/smart-entry.md": "7ffe4fdb93935400",
|
||||
|
||||
@@ -193,7 +193,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "62930e66cc979c1a",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "385501e4f9bbb31d",
|
||||
"gsd-core/references/reviewer-instances.md": "ae64e44f2f132bc2",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -359,14 +359,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "3ce57633978035ad",
|
||||
"gsd-core/workflows/review.md": "f1fbefbd64bd890e",
|
||||
"gsd-core/workflows/scan.md": "003883d71c37da7d",
|
||||
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
|
||||
"gsd-core/workflows/settings-integrations.md": "70515c5838fb9826",
|
||||
"gsd-core/workflows/settings.md": "d96ddf01fb85e61e",
|
||||
"gsd-core/workflows/ship.md": "b377dab2bc92a6e5",
|
||||
"gsd-core/workflows/ship.md": "f486f2731ca8b72a",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "b767a1d3db129a8a",
|
||||
"gsd-core/workflows/sketch.md": "88cfdf4edcf222ab",
|
||||
"gsd-core/workflows/smart-entry.md": "449238eb94abe187",
|
||||
|
||||
@@ -200,7 +200,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "a602b41bfa081fdf",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "1412472f858b8f41",
|
||||
"gsd-core/references/reviewer-instances.md": "a857fc444baec27a",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -366,14 +366,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "dea4661e8f89596f",
|
||||
"gsd-core/workflows/remove-workspace.md": "446847e71aa52504",
|
||||
"gsd-core/workflows/resume-project.md": "ad9f06a10bab8cc0",
|
||||
"gsd-core/workflows/review.md": "98e441bde98a9d92",
|
||||
"gsd-core/workflows/review.md": "fbe68c9f86385274",
|
||||
"gsd-core/workflows/scan.md": "ad8ebcad4626d4a8",
|
||||
"gsd-core/workflows/secure-phase.md": "71e6e689e80288ec",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "252b0d3edc315339",
|
||||
"gsd-core/workflows/settings-integrations.md": "d1711a95f44fdbde",
|
||||
"gsd-core/workflows/settings.md": "2e42ee34c791378a",
|
||||
"gsd-core/workflows/ship.md": "3a80ea841f0a81b9",
|
||||
"gsd-core/workflows/ship.md": "1875ec8f59fcf0b4",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "681800323681c5c6",
|
||||
"gsd-core/workflows/sketch.md": "fcb7af914159ef7b",
|
||||
"gsd-core/workflows/smart-entry.md": "2a253fe437496eea",
|
||||
|
||||
6
tests/fixtures/golden-install-parity/pi.json
vendored
6
tests/fixtures/golden-install-parity/pi.json
vendored
@@ -96,7 +96,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "62930e66cc979c1a",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "385501e4f9bbb31d",
|
||||
"gsd-core/references/reviewer-instances.md": "ae64e44f2f132bc2",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -262,14 +262,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "3ce57633978035ad",
|
||||
"gsd-core/workflows/review.md": "f1fbefbd64bd890e",
|
||||
"gsd-core/workflows/scan.md": "003883d71c37da7d",
|
||||
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
|
||||
"gsd-core/workflows/settings-integrations.md": "70515c5838fb9826",
|
||||
"gsd-core/workflows/settings.md": "d96ddf01fb85e61e",
|
||||
"gsd-core/workflows/ship.md": "b377dab2bc92a6e5",
|
||||
"gsd-core/workflows/ship.md": "f486f2731ca8b72a",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "838c701bd072ae73",
|
||||
"gsd-core/workflows/sketch.md": "c7725562b3efd311",
|
||||
"gsd-core/workflows/smart-entry.md": "449238eb94abe187",
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "62930e66cc979c1a",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "1412472f858b8f41",
|
||||
"gsd-core/references/reviewer-instances.md": "a857fc444baec27a",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -295,14 +295,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "e8ae4fbbfac700f0",
|
||||
"gsd-core/workflows/remove-workspace.md": "4ac64de862dc650e",
|
||||
"gsd-core/workflows/resume-project.md": "7f20769f302e5427",
|
||||
"gsd-core/workflows/review.md": "7e1d8901d796338f",
|
||||
"gsd-core/workflows/review.md": "b3548b43dcf1fb4e",
|
||||
"gsd-core/workflows/scan.md": "949692db4834dd27",
|
||||
"gsd-core/workflows/secure-phase.md": "ef7b5ad194b687bf",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "5e05212fb5cba531",
|
||||
"gsd-core/workflows/settings-integrations.md": "29c5de27fbbb18e9",
|
||||
"gsd-core/workflows/settings.md": "26b9b7979d3a5747",
|
||||
"gsd-core/workflows/ship.md": "cee31be37b2b6c6a",
|
||||
"gsd-core/workflows/ship.md": "a07a8ab18935985a",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "89e0eab2af946b04",
|
||||
"gsd-core/workflows/sketch.md": "483387542d6fc3af",
|
||||
"gsd-core/workflows/smart-entry.md": "d309710bcabd4675",
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "62930e66cc979c1a",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "1412472f858b8f41",
|
||||
"gsd-core/references/reviewer-instances.md": "a857fc444baec27a",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -295,14 +295,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "a46c2fe853bf4e86",
|
||||
"gsd-core/workflows/remove-workspace.md": "ae0e1c6d4438d663",
|
||||
"gsd-core/workflows/resume-project.md": "f242e4c8aba18ea2",
|
||||
"gsd-core/workflows/review.md": "5fd9c6a9d7777eee",
|
||||
"gsd-core/workflows/review.md": "35d023ff48e3827f",
|
||||
"gsd-core/workflows/scan.md": "63631467651d9ca8",
|
||||
"gsd-core/workflows/secure-phase.md": "4a647aec1e4d2dfe",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "39e66386f6c48025",
|
||||
"gsd-core/workflows/settings-integrations.md": "f8f756709ec02363",
|
||||
"gsd-core/workflows/settings.md": "44b10c59215633b8",
|
||||
"gsd-core/workflows/ship.md": "da81bf2515257822",
|
||||
"gsd-core/workflows/ship.md": "1d471a767b11e961",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "dbec602d104cb951",
|
||||
"gsd-core/workflows/sketch.md": "44ff275150b6d045",
|
||||
"gsd-core/workflows/smart-entry.md": "d8018578571f08d5",
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "62930e66cc979c1a",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "1412472f858b8f41",
|
||||
"gsd-core/references/reviewer-instances.md": "a857fc444baec27a",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -295,14 +295,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "e7a6af429b36e77b",
|
||||
"gsd-core/workflows/remove-workspace.md": "b5e60fbb33b3e33a",
|
||||
"gsd-core/workflows/resume-project.md": "82cfe1b8cb17c085",
|
||||
"gsd-core/workflows/review.md": "a63e0b5bb3b73730",
|
||||
"gsd-core/workflows/review.md": "805bea46bed56bd8",
|
||||
"gsd-core/workflows/scan.md": "12c11b2edc165df9",
|
||||
"gsd-core/workflows/secure-phase.md": "185a15d389951e6e",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "2f86ec7b998f9485",
|
||||
"gsd-core/workflows/settings-integrations.md": "b082fc518b484c07",
|
||||
"gsd-core/workflows/settings.md": "002eb0ce3c10c741",
|
||||
"gsd-core/workflows/ship.md": "bce39010f9518cf3",
|
||||
"gsd-core/workflows/ship.md": "4ff6590f1e04f124",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "10063f56c2c7f141",
|
||||
"gsd-core/workflows/sketch.md": "25c1f8f7acfb1da9",
|
||||
"gsd-core/workflows/smart-entry.md": "bd81482cb6a7ac53",
|
||||
|
||||
@@ -200,7 +200,7 @@
|
||||
"gsd-core/references/research-documentation-lookup.md": "c070007d1d72ab71",
|
||||
"gsd-core/references/research-philosophy.md": "62930e66cc979c1a",
|
||||
"gsd-core/references/research-verification-protocol.md": "9c38c9d9a687e679",
|
||||
"gsd-core/references/reviewer-instances.md": "385501e4f9bbb31d",
|
||||
"gsd-core/references/reviewer-instances.md": "ae64e44f2f132bc2",
|
||||
"gsd-core/references/revision-loop.md": "e55ff32dd98c63df",
|
||||
"gsd-core/references/scout-codebase.md": "ba266ecc18fbf172",
|
||||
"gsd-core/references/security-asvs-levels.md": "4774fac3b94b6ca8",
|
||||
@@ -366,14 +366,14 @@
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "3ce57633978035ad",
|
||||
"gsd-core/workflows/review.md": "f1fbefbd64bd890e",
|
||||
"gsd-core/workflows/scan.md": "003883d71c37da7d",
|
||||
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
|
||||
"gsd-core/workflows/settings-integrations.md": "70515c5838fb9826",
|
||||
"gsd-core/workflows/settings.md": "d96ddf01fb85e61e",
|
||||
"gsd-core/workflows/ship.md": "b377dab2bc92a6e5",
|
||||
"gsd-core/workflows/ship.md": "f486f2731ca8b72a",
|
||||
"gsd-core/workflows/sketch-wrap-up.md": "86db87b16548117e",
|
||||
"gsd-core/workflows/sketch.md": "e96f1866d3e60cab",
|
||||
"gsd-core/workflows/smart-entry.md": "449238eb94abe187",
|
||||
|
||||
@@ -216,7 +216,7 @@ describe('bug #687 → #2073: agy print mode bounded by --print-timeout PAIRED w
|
||||
const c = read();
|
||||
assert.doesNotMatch(c, /agy[^\n]*-p "\$\(cat/,
|
||||
'review.md must not feed agy the prompt inline via "$(cat …)" — a large review prompt overflows the exec arg list (rc 126)');
|
||||
assert.match(c, /Read the file at \/tmp\/gsd-review-prompt-/,
|
||||
assert.match(c, /Read the file at \{run_dir\}\/gsd-review-prompt\.md/,
|
||||
'review.md should pass agy a file-reference prompt (mirrors the Cursor block)');
|
||||
});
|
||||
|
||||
@@ -228,7 +228,7 @@ describe('bug #687 → #2073: agy print mode bounded by --print-timeout PAIRED w
|
||||
test('discards partial output on non-zero exit so the fallback fires (#687)', () => {
|
||||
const c = read();
|
||||
assert.match(c, /_AGY_RC.*-ne 0/, 'review.md must check the agy exit code');
|
||||
assert.match(c, /: > \/tmp\/gsd-review-antigravity-/,
|
||||
assert.match(c, /: > \{run_dir\}\/gsd-review-antigravity\.md/,
|
||||
'review.md must truncate the output file when agy timed out / failed');
|
||||
});
|
||||
|
||||
@@ -323,7 +323,7 @@ describe('enh-773: automated codex exec invocations include --ephemeral and --da
|
||||
);
|
||||
}
|
||||
assert.ok(
|
||||
/\[ ! -s \/tmp\/gsd-review-codex-\{phase\}\.md \]/.test(workflow),
|
||||
/\[ ! -s \{run_dir\}\/gsd-review-codex\.md \]/.test(workflow),
|
||||
'review.md must guard against an empty codex review output and surface the failure'
|
||||
);
|
||||
});
|
||||
@@ -369,8 +369,8 @@ describe('#1698 regression: codex review is captured via --output-last-message,
|
||||
test('every codex exec invocation captures the review via -o <FILE>', () => {
|
||||
for (const line of codexExecLines) {
|
||||
assert.ok(
|
||||
/\s-o\s+\/tmp\/gsd-review-codex-\{phase\}\.md\b/.test(line),
|
||||
`codex exec invocation must capture the review via -o /tmp/gsd-review-codex-{phase}.md:\n ${line.trim()}`
|
||||
/\s-o\s+\{run_dir\}\/gsd-review-codex\.md\b/.test(line),
|
||||
`codex exec invocation must capture the review via -o {run_dir}/gsd-review-codex.md:\n ${line.trim()}`
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -378,7 +378,7 @@ describe('#1698 regression: codex review is captured via --output-last-message,
|
||||
test('no codex exec invocation redirects stdout into the review file', () => {
|
||||
for (const line of codexExecLines) {
|
||||
assert.ok(
|
||||
!/>\s*\/tmp\/gsd-review-codex-\{phase\}\.md\b/.test(line),
|
||||
!/>\s*\{run_dir\}\/gsd-review-codex\.md\b/.test(line),
|
||||
`codex exec must not redirect stdout into the review file (teardown noise pollutes it); use -o + >/dev/null:\n ${line.trim()}`
|
||||
);
|
||||
assert.ok(
|
||||
@@ -428,7 +428,7 @@ function openCodeBlock() {
|
||||
describe('bug #1936: OpenCode reviewer must not silently yield an empty review', () => {
|
||||
test('captures opencode stderr to a sidecar, never /dev/null', () => {
|
||||
const block = openCodeBlock();
|
||||
assert.match(block, /opencode run [^\n]*2>\/tmp\/gsd-review-opencode-\{phase\}\.err/,
|
||||
assert.match(block, /opencode run [^\n]*2>\{run_dir\}\/gsd-review-opencode\.err/,
|
||||
'the opencode invocation must send stderr to a .err sidecar so failures are diagnosable');
|
||||
assert.doesNotMatch(block, /opencode run [^\n]*2>\/dev\/null/,
|
||||
'the opencode invocation must not discard stderr to /dev/null (#1936)');
|
||||
@@ -450,7 +450,7 @@ describe('bug #1936: OpenCode reviewer must not silently yield an empty review',
|
||||
assert.match(block, /OPENCODE_REVIEW=\$\(jq/, 'must capture the extraction into a variable');
|
||||
assert.match(block, /\[ -n "\$OPENCODE_REVIEW" \]/,
|
||||
'must branch on the content of $OPENCODE_REVIEW, not on the size of the .md file');
|
||||
assert.doesNotMatch(block, /\[ ! -s \/tmp\/gsd-review-opencode-\{phase\}\.md \]/,
|
||||
assert.doesNotMatch(block, /\[ ! -s \{run_dir\}\/gsd-review-opencode\.md \]/,
|
||||
'must not gate the stub on `[ ! -s ...opencode...md ]` (a lone newline defeats it)');
|
||||
});
|
||||
|
||||
@@ -459,14 +459,14 @@ describe('bug #1936: OpenCode reviewer must not silently yield an empty review',
|
||||
assert.match(block, /#1936/, 'the empty-output stub must reference the issue');
|
||||
assert.match(block, /step_finish[\s\S]*\.part\.reason[\s\S]*\.part\.tokens\.output/,
|
||||
'the stub must surface the stop reason and output-token count from the final step_finish');
|
||||
assert.match(block, /cat \/tmp\/gsd-review-opencode-\{phase\}\.err/,
|
||||
assert.match(block, /cat \{run_dir\}\/gsd-review-opencode\.err/,
|
||||
'the stub must append the captured stderr');
|
||||
});
|
||||
|
||||
test('does not regress the Codex reviewer block (still captures stderr to .err)', () => {
|
||||
// #1936 changes only the OpenCode block; the Codex block's existing
|
||||
// stderr-to-sidecar contract must remain intact.
|
||||
assert.match(read(), /codex exec [^\n]*2>\/tmp\/gsd-review-codex-\{phase\}\.err/,
|
||||
assert.match(read(), /codex exec [^\n]*2>\{run_dir\}\/gsd-review-codex\.err/,
|
||||
'the Codex reviewer block must be left unchanged');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -64,14 +64,14 @@
|
||||
"remove-phase.md": 8513,
|
||||
"remove-workspace.md": 7551,
|
||||
"resume-project.md": 17270,
|
||||
"review.md": 50362,
|
||||
"review.md": 50497,
|
||||
"scan.md": 7732,
|
||||
"secure-phase.md": 13622,
|
||||
"session-report.md": 4044,
|
||||
"settings-advanced.md": 40019,
|
||||
"settings-integrations.md": 15892,
|
||||
"settings.md": 33467,
|
||||
"ship.md": 25222,
|
||||
"ship.md": 25555,
|
||||
"sketch-wrap-up.md": 14267,
|
||||
"sketch.md": 20004,
|
||||
"smart-entry.md": 11124,
|
||||
|
||||
Reference in New Issue
Block a user