fix(#4097): evidence preservation no longer sweeps the run's own input copies into .review-diagnostics/ (#4329)

* test(#4097): failing-first regression — run input copies swept into .review-diagnostics

The present_results preserve+cleanup glob `_DIAG_MD=( "$RUN_DIR"/gsd-review-*.md )`
matches both lane outputs and the run's own assembled input copies (prompt,
instructions, roadmap, per-plan copies, project/context/research/requirements,
per-lane trimmed prompts) because both share the gsd-review- prefix.

Seed the full input-copy set into the existing runWriteReviewsFlow fixture and
assert (a) the diagnostics dir holds exactly the lane report + .err sidecar and
no input basenames, and (b) an inputs-only run creates no diagnostics dir at all
and still cleans up. Both RED against the shipped block.

* fix(#4097): preserve lane output only — exclude the run's input copies from evidence

The preserve+cleanup glob treated every gsd-review-*.md in RUN_DIR as lane
evidence, but the workflow itself writes the run's assembled INPUTS there under
the same prefix (prompt, instructions, roadmap, per-plan copies, project/
context/research/requirements, per-lane trimmed prompts). Filter _DIAG_MD by
basename against that closed input set instead.

Direct glob iteration + case filter — identical under bash and zsh (#4099/
#4109), nullglob-safe (#2962), and the exclusion list is closed and owned in
this step: a future input basename cannot silently rejoin the evidence set.
Lane reports, diagnostic stubs and non-empty .err sidecars are unchanged.

Emitted-Drift-Ack-Growth: review.md — #4097 narrows the present_results evidence-preservation glob: the closed input-basename exclusion list (case filter) plus its rationale note are deliberate additions so a future input basename cannot silently rejoin the evidence set.

* changeset(#4097): fixed — review diagnostics no longer sweep run input copies

* changeset(#4097): backfill PR number 4329

---------

Co-authored-by: sim <sim@local>
This commit is contained in:
Tom Boucher
2026-09-05 16:45:43 -04:00
committed by GitHub
parent 4c60879b5d
commit 2f920c5af3
3 changed files with 119 additions and 1 deletions

View File

@@ -845,6 +845,19 @@ NOT a preservation failure. This copy is deliberately NOT part of the commit abo
step names only `{padded_phase}-REVIEWS.md` explicitly, never a directory glob, so
`.review-diagnostics/` is never swept into it.
**#4097: preserve lane OUTPUT, never the run's own input copies.** `RUN_DIR` holds not only
lane outputs — prompt assembly (the `gather_context`/section-copy step above) also writes the
run's assembled INPUTS there under the same `gsd-review-` prefix: the combined prompt, the
instructions/roadmap sections, a copy of every plan under review, the project/context/research/
requirements sections, and the per-lane trimmed prompts. Those are byte-identical duplicates of
files already committed under `.planning/`; sweeping them into `.review-diagnostics/` buries
the actual evidence under plan duplicates and grows the phase directory on every run. The
exclusion list below is CLOSED and owned here: this workflow itself writes every input
basename at prompt-assembly time, so a future input file CANNOT silently join the evidence
set — adding one means adding its stem to this list consciously. Lane slugs never begin with
any excluded stem (`prompt`, `instructions`, `plan-`, `project`, `roadmap`, `context`,
`research`, `requirements`), so a lane report can never be excluded by accident.
Preservation and cleanup MUST run in the same fenced block below (a shell variable cannot
survive across separate fences — each is its own process). `mkdir -p` and every `cp` are
exit-status checked; `rm -rf "$RUN_DIR"` runs ONLY if nothing was preserved (nothing to
@@ -858,7 +871,20 @@ shopt -s nullglob 2>/dev/null; setopt NULL_GLOB 2>/dev/null
RUN_DIR="{run_dir}"
DIAG_DIR="{phase_dir}/.review-diagnostics"
_DIAG_MD=( "$RUN_DIR"/gsd-review-*.md )
# #4097: `gsd-review-*.md` matches BOTH lane outputs (reports, diagnostic stubs) and the
# run's own assembled input copies (see the #4097 note above). Filter by basename against
# the closed input set this workflow itself writes — direct glob iteration with a `case`
# filter, no string accumulator, identical under bash and zsh (#4099/#4109), and the
# `nullglob` set at the top of this fence keeps an empty RUN_DIR an empty array (#2962).
# `gsd-review-prompt*` deliberately covers BOTH the combined prompt (`gsd-review-prompt.md`)
# and the per-lane trimmed prompts (`gsd-review-prompt-<slug>.md`).
_DIAG_MD=()
for f in "$RUN_DIR"/gsd-review-*.md; do
case "$(basename "$f")" in
gsd-review-prompt*|gsd-review-instructions*|gsd-review-plan-*|gsd-review-project*|gsd-review-roadmap*|gsd-review-context*|gsd-review-research*|gsd-review-requirements*) ;;
*) _DIAG_MD+=("$f") ;;
esac
done
_DIAG_ERR=()
for f in "$RUN_DIR"/gsd-review-*.err; do
[ -s "$f" ] && _DIAG_ERR+=("$f")