From 4884b65d3760845d8398321fbf5739536538c8ea Mon Sep 17 00:00:00 2001 From: garrytan Date: Mon, 2 Nov 2026 15:00:01 +0000 Subject: [PATCH] fix: pre-landing review fixes (Step 11: read-tree fallback and ..x root dirs) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- bin/gstack-wtree | 6 ++++-- test/review-start-evidence.test.ts | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/bin/gstack-wtree b/bin/gstack-wtree index 87e3ae1b3..3df71b05c 100755 --- a/bin/gstack-wtree +++ b/bin/gstack-wtree @@ -111,11 +111,13 @@ if [ -e "$TOP/.bun" ] && ! git -C "$TOP" check-ignore --no-index -q .bun 2>/dev/ EXCLUDES=() if [ -n "$EXCLUDE_BUN" ]; then EXCLUDES+=("$EXCLUDE_BUN"); fi if [ -n "$REVIEW" ]; then - for DOTDIR in "$TOP"/.[!.]*; do + TRACKED_INDEX="$TMPIDX" + if [ -n "$REAL_INDEX" ] && [ -f "$REAL_INDEX" ]; then TRACKED_INDEX="$REAL_INDEX"; fi + for DOTDIR in "$TOP"/.[!.]* "$TOP"/..?*; do NAME=${DOTDIR##*/} case "$NAME" in .git|.github|.gstack|.bun) continue ;; esac if [ ! -d "$DOTDIR" ] || [ -L "$DOTDIR" ]; then continue; fi - if git -C "$TOP" ls-files --error-unmatch -- ":(top,literal)$NAME" >/dev/null 2>&1; then continue; fi + if GIT_INDEX_FILE="$TRACKED_INDEX" git -C "$TOP" ls-files --error-unmatch -- ":(top,literal)$NAME" >/dev/null 2>&1; then continue; fi if git -C "$TOP" check-ignore --no-index -q -- "$NAME" 2>/dev/null; then continue; fi EXCLUDES+=(":(exclude,top,literal)$NAME") done diff --git a/test/review-start-evidence.test.ts b/test/review-start-evidence.test.ts index c8ef072f6..848784512 100644 --- a/test/review-start-evidence.test.ts +++ b/test/review-start-evidence.test.ts @@ -202,6 +202,28 @@ describe('review start/end binding (#2803)', () => { expect(log(token).review_binding.state).toBe('changed'); }); + test('GST-985: a root ..x scratch dir created mid-review still binds', () => { + const token = cli('gstack-review-log', ['--start', 'review']); + mkdirSync(join(repo, '..x')); + writeFileSync(join(repo, '..x', 'unit.json'), '{}\n'); + expect(log(token).review_binding.state).toBe('verified'); + }); + + test('GST-985: a git add -N file in a new dot-directory counts under the read-tree HEAD fallback', () => { + mkdirSync(join(repo, '.newdir')); + writeFileSync(join(repo, '.newdir', 'a.ts'), 'export const a = 1;\n'); + git('add', '-N', '.newdir/a.ts'); + const fakeBin = mkdtempSync(join(tmpdir(), 'review-start-faketouch-')); + writeFileSync(join(fakeBin, 'touch'), '#!/bin/sh\nexit 1\n'); + chmodSync(join(fakeBin, 'touch'), 0o755); + try { + const env = { PATH: `${fakeBin}:${process.env.PATH}` }; + expect(cli('gstack-wtree', ['--review'], repo, env)).toBe(cli('gstack-wtree', [], repo, env)); + } finally { + rmSync(fakeBin, { recursive: true, force: true }); + } + }); + test('GST-985: gstack-wtree refuses an unknown or extra argument', () => { for (const args of [['--bogus'], ['--review', 'x']]) { const r = spawnSync(join(ROOT, 'bin', 'gstack-wtree'), args, { cwd: repo, encoding: 'utf8', timeout: 10_000 });