Skip to content

chore: repo hygiene cleanup + test quality/speed uplift#7

Closed
ndycode wants to merge 7 commits into
mainfrom
chore/repo-hygiene-quality-speed
Closed

chore: repo hygiene cleanup + test quality/speed uplift#7
ndycode wants to merge 7 commits into
mainfrom
chore/repo-hygiene-quality-speed

Conversation

@ndycode
Copy link
Copy Markdown
Owner

@ndycode ndycode commented Feb 27, 2026

Summary

  • adds deterministic repository hygiene tooling via scripts/repo-hygiene.js
  • wires hygiene enforcement into npm scripts and CI (clean:repo:check)
  • removes tracked scratch artifact test-results.md
  • broadens cleanup/guard coverage with test/repo-hygiene.test.ts
  • improves test-run reliability/speed by making stall-timeout assertions deterministic in stream-failover tests
  • expands Vitest exclusions for local scratch/state directories to avoid accidental test discovery overhead

Why

The repo had significant local trash/temp drift patterns (.tmp*, tmp*, generated/local state dirs). This change makes cleanup explicit and auditable, then blocks regressions in CI.

What Changed

  • scripts/repo-hygiene.js
    • clean --mode aggressive [--root] [--dry-run]
    • check [--root]
    • allowlisted cleanup only (safe root-level targets)
    • check mode validates required .gitignore hygiene patterns and blocks tracked scratch files
  • package.json
    • clean:repo
    • clean:repo:check
  • .github/workflows/ci.yml
    • adds required Repository hygiene check step
  • .gitignore
    • adds test-results.md
  • test/repo-hygiene.test.ts
    • adds coverage for dry-run, deletion boundaries, tracked scratch detection, and pattern validation
  • test/stream-failover.test.ts
    • replaces timeout-dependent rejection tests with fake-timer deterministic assertions to remove unhandled rejection noise and reduce runtime uncertainty
  • vitest.config.ts
    • excludes additional local/generated dirs (.tmp*, .omx, .opencode, .sisyphus, .history, coverage variants)
  • deletes tracked root scratch artifact: test-results.md

Audit Evidence

Worktree

  • Base: origin/main @ 0636840
  • Worktree branch: chore/repo-hygiene-quality-speed
  • Commit: 5b9cad2

Local aggressive cleanup executed

  • isolated worktree root: no candidates found
  • primary workspace root: deleted .tmp*/tmp* files/dirs and generated/local dirs including .omx, .opencode, .sisyphus, coverage, dist, node_modules, plus local scratch docs (task_plan.md, findings.md, progress.md, test-results.md)

Validation

  • npm run clean:repo:check
  • npm run typecheck
  • npm run lint
  • npm test ✅ (85 files, 2002 tests)
  • npm run build

Runtime snapshot

  • prior baseline (before changes): npm test wall ~9.34s
  • post-change run: npm test wall ~8.39s

Risk

  • no runtime plugin API changes
  • behavior is limited to repository hygiene tooling, CI gating, and test harness reliability

note: greptile review for oc-chatgpt-multi-auth. cite files like lib/foo.ts:123. confirm regression tests + windows concurrency/token redaction coverage.

Greptile Summary

adds deterministic repository hygiene tooling with CI enforcement, removes tracked scratch artifacts, and improves test reliability/speed.

key changes:

  • scripts/repo-hygiene.js provides clean --mode aggressive and check commands with allowlisted cleanup targeting .tmp*, tmp*, generated dirs (coverage, dist, node_modules), and scratch docs
  • CI workflow now gates on npm run clean:repo:check before security audit to block tracked scratch files and validate required .gitignore patterns
  • test quality improvements: stream-failover.test.ts replaces timeout-dependent assertions with fake-timer deterministic flow, reducing runtime from ~9.34s to ~8.39s
  • rotation-integration.test.ts adds removeWithRetry helper with exponential backoff to handle windows filesystem lock races (ENOTEMPTY/EPERM/EBUSY)
  • vitest config expands exclusions to skip local/generated directories (.tmp*, .omx, .opencode, .sisyphus, .history)

critical issue:

  • scripts/repo-hygiene.js line 206 uses bare fs.rm without retry logic for windows filesystem locks. antivirus can hold EBUSY/EPERM/ENOTEMPTY locks during deletion causing the script to fail. the same PR adds removeWithRetry in test/rotation-integration.test.ts for this exact race condition - inconsistent pattern.

Confidence Score: 3/5

  • hygiene tooling is well-scoped and CI-gated, but missing windows filesystem concurrency protection creates merge risk
  • score reflects production-quality test improvements and good hygiene architecture, but downgraded due to missing retry logic in scripts/repo-hygiene.js fs.rm calls. windows users (primary audience per custom rule 637a42e6) will hit antivirus lock failures during cleanup. PR already demonstrates awareness of this pattern via removeWithRetry in rotation-integration.test.ts but doesn't apply it consistently to the hygiene script itself.
  • scripts/repo-hygiene.js needs windows filesystem retry logic before merge to prevent antivirus lock failures in production cleanup

Important Files Changed

Filename Overview
scripts/repo-hygiene.js adds deterministic repo cleanup tooling but lacks windows filesystem concurrency protection (no retry logic for EBUSY/EPERM/ENOTEMPTY errors on fs.rm calls)
test/repo-hygiene.test.ts comprehensive coverage for cleanup/check modes and path separator handling, but test fixture cleanup lacks retry logic for windows filesystem locks
test/stream-failover.test.ts replaces timeout-dependent tests with fake-timer deterministic assertions, eliminates unhandled rejection noise and reduces test runtime uncertainty
test/rotation-integration.test.ts adds removeWithRetry to handle windows filesystem lock race conditions (ENOTEMPTY/EPERM/EBUSY) with exponential backoff during temp directory cleanup
.github/workflows/ci.yml adds hygiene check step before security audit to block tracked scratch files and missing gitignore patterns in CI

Fix All in Codex

Last reviewed commit: 93f069e

Context used:

  • Rule from dashboard - What: Every code change must explain how it defends against Windows filesystem concurrency bugs and ... (source)

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 27, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

walkthrough

adds a repo-hygiene cli with clean and check, npm scripts, and a ci step invoking the check. adds tests for the cli, extends vitest ignores, and removes test-results.md. missing explicit regression tests for windows path/separator edge cases and limited concurrency stress tests; concurrency risks flagged. references: lib/scripts/repo-hygiene.js:1, package.json:1, .github/workflows/ci.yml:1, test/repo-hygiene.test.ts:1, test/stream-failover.test.ts:1, vitest.config.ts:1, .gitignore:1, test-results.md:1.

changes

Cohort / File(s) Summary
repo hygiene cli & scripts
lib/scripts/repo-hygiene.js, package.json
adds a node cli implementing clean (safe/dry-run + --mode aggressive) and check. adds npm scripts clean:repo and clean:repo:check. enforces repo-root confinement for candidate deletions. see lib/scripts/repo-hygiene.js:1, package.json:1.
ci integration
.github/workflows/ci.yml
ci test job adds a "Repository hygiene check" step running npm run clean:repo:check after dependencies installation. see .github/workflows/ci.yml:1.
repo-hygiene tests
test/repo-hygiene.test.ts
adds comprehensive tests using temporary git fixtures for dry-run, deletions, tracked-file failures, missing .gitignore patterns, CRLF handling, and git-failure cases. lacks explicit windows path/separator regression and broader concurrency stress tests — concurrency risks flagged. see test/repo-hygiene.test.ts:1.
timer & stream tests
test/stream-failover.test.ts, vitest.config.ts
refactors stream-failover tests to use fake timers with afterEach restore; expands vitest exclude patterns (coverage, .tmp*, .omx, .opencode, .sisyphus, .history). see test/stream-failover.test.ts:1, vitest.config.ts:1.
ignore and artifact removal
.gitignore, test-results.md
adds test-results.md to .gitignore and deletes the test-results.md file from the repo. see .gitignore:1, test-results.md:1.

sequence diagram(s)

sequenceDiagram
    actor user
    participant cli as "repo-hygiene cli"
    participant git as git
    participant fs as filesystem

    rect rgba(100,150,200,0.5)
    Note over user,git: check command flow
    user->>cli: run check
    cli->>git: git ls-files
    git-->>cli: tracked files
    cli->>fs: read .gitignore
    fs-->>cli: patterns
    cli->>cli: validate tracked-scratch absence & required patterns
    alt violations
        cli-->>user: error + non-zero exit
    else
        cli-->>user: success
    end
    end

    rect rgba(150,100,200,0.5)
    Note over user,fs: clean command flow
    user->>cli: run clean [--dry-run | --mode aggressive]
    cli->>cli: parse args & validate repo root
    cli->>fs: scan for candidates (generated dirs, root-temp, tracked-scratch)
    cli->>cli: filter protected names & safety-check paths
    alt safety fail
        cli-->>user: error + non-zero exit
    else dry-run
        cli-->>user: report planned deletions
    else perform delete
        cli->>fs: delete paths
        fs-->>cli: deletions complete
        cli-->>user: summary
    end
    end
Loading

estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed Title follows conventional commits format with type 'chore', includes clear summary within 72 chars, and is written in lowercase imperative.
Description check ✅ Passed The PR description is directly related to the changeset, detailing specific files changed, functionality added (repo-hygiene tooling, CI enforcement, test improvements), and providing validation evidence.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/repo-hygiene-quality-speed

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/repo-hygiene.js`:
- Around line 149-160: The getTrackedPaths function currently swallows all
errors from execFileSync and returns an empty array; change the catch to capture
the error (e) and either log a clear warning with the error details using
console.warn (including context like "git ls-files failed in getTrackedPaths")
or rethrow/exit to make the failure fatal depending on desired behavior; update
the catch block in getTrackedPaths to include the error variable and call
console.warn or throw the error so missing git or repo corruption doesn't
silently pass.
- Around line 181-183: The code reads .gitignore without handling a missing file
which can throw; wrap the read in a try/catch around the await fs.readFile call
(referencing gitignorePath and the gitignore variable) and if the file is
absent, treat gitignore as an empty string or log a clear diagnostic and
continue; then compute missingPatterns using REQUIRED_GITIGNORE_PATTERNS.filter
as before and ensure the error path returns/continues gracefully instead of
letting the exception bubble.
- Around line 51-62: The loop that parses argv reads argv[i + 1] for "--mode"
and "--root" without checking bounds, so update the parsing in the for-loop to
validate that argv[i + 1] exists and is not another flag before assigning to
args.mode or args.root; if the value is missing, throw or print a clear error
and exit (or set a sensible default) so you don't assign undefined. Specifically
modify the block handling the "--mode" and "--root" cases (the code that sets
args.mode and args.root using argv[i + 1]) to check existence and non-flagness
of argv[i + 1] and only increment i after a successful assignment. Ensure the
"--dry-run" handling remains unchanged.
- Line 183: The current substring check using gitignore.includes(pattern) yields
false positives; change to a line-based match by splitting gitignore into lines
(e.g., gitignore.split(/\r?\n/)), trimming each line and filtering out empty
lines/comments, then compute missingPatterns by checking that no gitignore line
equals the REQUIRED_GITIGNORE_PATTERNS entry (or matches the exact pattern
string) so only whole-line matches satisfy the requirement; update the variable
referenced (missingPatterns) and use the existing REQUIRED_GITIGNORE_PATTERNS
and gitignore identifiers when implementing the stricter check.

In `@test/repo-hygiene.test.ts`:
- Around line 38-49: The test creates a temp repo with makeRepoFixture() and
removes it only at the end of the test body, which leaks temp dirs if assertions
fail; change the test suite to track the created root and perform cleanup in an
afterEach (or wrap each test body in try/finally) so removal always runs.
Specifically, keep using makeRepoFixture() to set root and the existing fs.rm
call but move the removal into an afterEach that checks and deletes the tracked
root (or ensure each test enclosing logic around runRepoHygiene(...),
expect(...), and fs.stat(...) is wrapped in try/finally) so tests in this file
never leave temporary directories behind.
- Around line 31-35: The tests omit a Windows CRLF regression case: update or
add a test in test/** that uses makeRepoFixture and configures the git repo to
produce CRLF output (e.g., set core.autocrlf=true or write a committed file with
\r\n line endings), then run the code path that reads git ls-files and ensure
parsing in scripts/repo-hygiene.js (see the regexp at repo-hygiene.js:156)
handles CRLF correctly; concretely, add a new test which uses makeRepoFixture to
init the repo, creates/commits a file with CRLF line endings, invokes the
repo-hygiene logic, and asserts the expected output to prevent regression.

In `@test/stream-failover.test.ts`:
- Around line 38-40: The file already calls vi.useRealTimers() in the afterEach
cleanup block, so remove the redundant per-test invocations of
vi.useRealTimers() inside individual tests in test/stream-failover.test.ts
(search for and delete the standalone vi.useRealTimers() calls that appear
within test bodies); leave the afterEach(() => { vi.useRealTimers(); }) intact
so timers are restored for every test.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 0636840 and 5b9cad2.

📒 Files selected for processing (8)
  • .github/workflows/ci.yml
  • .gitignore
  • package.json
  • scripts/repo-hygiene.js
  • test-results.md
  • test/repo-hygiene.test.ts
  • test/stream-failover.test.ts
  • vitest.config.ts
💤 Files with no reviewable changes (1)
  • test-results.md
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/stream-failover.test.ts
  • test/repo-hygiene.test.ts
🧬 Code graph analysis (2)
test/stream-failover.test.ts (1)
lib/request/stream-failover.ts (1)
  • withStreamingFailover (115-234)
test/repo-hygiene.test.ts (1)
scripts/repo-hygiene.js (2)
  • args (39-44)
  • args (200-200)
🔇 Additional comments (9)
.gitignore (1)

29-29: lgtm.

addition of test-results.md to .gitignore is consistent with scripts/repo-hygiene.js:17-22 tracked scratch files set and aligns with the hygiene tooling.

package.json (1)

45-46: lgtm.

scripts wired correctly to scripts/repo-hygiene.js. clean command requires --mode aggressive which matches the validation at scripts/repo-hygiene.js:67-69.

.github/workflows/ci.yml (1)

31-33: lgtm.

hygiene check runs after npm ci and before tests. placement is correct. running only on the test matrix is reasonable since the check is node-version-agnostic.

test/stream-failover.test.ts (2)

52-68: deterministic timer approach is correct.

the refactor from real timeouts to fake timers at test/stream-failover.test.ts:53-67 eliminates flakiness. the pattern of capturing textPromise before advancing timers ensures the stream read is in flight when time advances.


89-102: rejection test pattern looks correct.

capturing the assertion promise before advancing timers at test/stream-failover.test.ts:97-100 is the right way to test async rejections with fake timers. this avoids races between timer advancement and promise settlement.

vitest.config.ts (1)

12-17: lgtm.

exclusion patterns align with scripts/repo-hygiene.js:9-16 GENERATED_DIRS. prevents vitest from scanning temp/generated directories that may contain stale .test.ts files.

Also applies to: 22-27

test/repo-hygiene.test.ts (2)

69-81: good coverage for tracked scratch file detection.

test at test/repo-hygiene.test.ts:69-81 correctly uses git add -f to force-add an ignored file and verifies the check command catches it.


93-102: lgtm.

test at test/repo-hygiene.test.ts:93-102 verifies the happy path. assertions for exit code and stdout message are appropriate.

scripts/repo-hygiene.js (1)

127-147: clean function is solid.

scripts/repo-hygiene.js:127-147 has good defensive checks: refuses to delete paths outside root (line 136-138), uses { recursive: true, force: true } for safe removal. no concurrency risks since operations are sequential.

Comment thread scripts/repo-hygiene.js
Comment thread scripts/repo-hygiene.js Outdated
Comment thread scripts/repo-hygiene.js Outdated
Comment thread test/repo-hygiene.test.ts
Comment thread test/repo-hygiene.test.ts
Comment thread test/stream-failover.test.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (5)
scripts/repo-hygiene.js (3)

199-209: ⚠️ Potential issue | 🟠 Major

do not silently treat git failures as clean state

lib/scripts/repo-hygiene.js:207-209 (Line 207) returns [] on any git ls-files error. that can make check pass when git is unavailable or repo metadata is broken.

proposed fix
-	} catch {
-		return [];
+	} catch (error) {
+		throw new Error(
+			`git ls-files failed: ${error instanceof Error ? error.message : String(error)}`,
+		);
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/repo-hygiene.js` around lines 199 - 209, The getTrackedPaths function
currently swallows any git errors and returns an empty array, which can hide
problems and make checks pass incorrectly; change the catch to surface the
failure instead of returning [] — either rethrow the caught error or throw a new
Error with context (e.g., include the original error message) so callers of
getTrackedPaths see that git failed; locate the function getTrackedPaths and
update its catch block to not return a silent empty array but to propagate/log
and throw the error so repo-hygiene checks fail visibly when git is unavailable
or repo metadata is broken.

231-234: ⚠️ Potential issue | 🟠 Major

switch .gitignore validation to line-based matching

lib/scripts/repo-hygiene.js:233 (Line 233) uses substring matching. a line like tmp-special/ can incorrectly satisfy the required tmp entry.

proposed fix
-	const missingPatterns = REQUIRED_GITIGNORE_PATTERNS.filter((pattern) => !gitignore.includes(pattern));
+	const gitignoreLines = new Set(
+		gitignore
+			.split(/\r?\n/)
+			.map((line) => line.trim())
+			.filter((line) => line.length > 0 && !line.startsWith("#")),
+	);
+	const missingPatterns = REQUIRED_GITIGNORE_PATTERNS.filter((pattern) => !gitignoreLines.has(pattern));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/repo-hygiene.js` around lines 231 - 234, The current substring check
using gitignore.includes(...) can produce false positives (e.g., "tmp-special/"
matching required "tmp"), so change the validation to line-based matching: split
the file content from gitignore into lines, trim each line and ignore empty
lines and comment lines (starting with '#'), normalize both existing lines and
REQUIRED_GITIGNORE_PATTERNS by removing trailing slashes before comparison, then
build a Set of normalized lines and compute missingPatterns by checking which
normalized required patterns are not present in that Set (use the variables
gitignore, REQUIRED_GITIGNORE_PATTERNS and update missingPatterns logic in
repo-hygiene.js accordingly).

62-69: ⚠️ Potential issue | 🟡 Minor

validate flag values before consuming argv next token

lib/scripts/repo-hygiene.js:64-69 (Line 64, Line 68) accepts missing values and also accepts another flag as a value. this turns input errors into confusing downstream failures.

proposed fix
 		if (arg === "--mode") {
-			args.mode = argv[i + 1] ?? "";
+			const value = argv[i + 1];
+			if (!value || value.startsWith("--")) {
+				throw new Error("--mode requires a value");
+			}
+			args.mode = value;
 			i += 1;
 		} else if (arg === "--root") {
-			args.root = argv[i + 1] ?? "";
+			const value = argv[i + 1];
+			if (!value || value.startsWith("--")) {
+				throw new Error("--root requires a value");
+			}
+			args.root = value;
 			i += 1;
 		} else if (arg === "--dry-run") {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/repo-hygiene.js` around lines 62 - 69, The flag-parsing loop accepts
missing values or treats the next flag as a value for args.mode and args.root;
update the loop that iterates argv to validate the next token before consuming
it: in the for-loop handling "--mode" and "--root" check that argv[i+1] exists
and does not start with "--" (or is not another known flag), otherwise throw or
print a clear error and exit (or set a default), then only assign args.mode and
args.root and increment i when the next token is valid; reference the existing
argv loop and the args.mode / args.root assignments to locate where to add the
validation.
test/repo-hygiene.test.ts (2)

38-49: ⚠️ Potential issue | 🟡 Minor

guarantee fixture cleanup even when assertions fail

test/repo-hygiene.test.ts:48, test/repo-hygiene.test.ts:69, test/repo-hygiene.test.ts:83, test/repo-hygiene.test.ts:93, and test/repo-hygiene.test.ts:104 (Line 48, Line 69, Line 83, Line 93, Line 104) cleanup only runs on success. use afterEach or per-test try/finally so temp repos are always removed.

as per coding guidelines, "tests must stay deterministic and use vitest."

Also applies to: 51-70, 72-84, 86-94, 96-105

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/repo-hygiene.test.ts` around lines 38 - 49, The tests create temporary
repo fixtures with makeRepoFixture() and remove them with fs.rm(...) but cleanup
only runs on success; change each test using runRepoHygiene(...) to guarantee
removal by wrapping the per-test fixture lifecycle in a try/finally or move rm
into a shared afterEach hook that deletes the fixture root, ensuring
makeRepoFixture(), runRepoHygiene(), and any asserts all run inside the try and
fs.rm(root, { recursive: true, force: true }) runs in finally (or implement
afterEach that references the created root), and apply this pattern to all
affected tests in test/repo-hygiene.test.ts so temp repos are always removed
even if assertions fail.

31-35: ⚠️ Potential issue | 🟠 Major

add a windows/crlf regression case for tracked-path parsing

test/repo-hygiene.test.ts:31-35 (Line 31) initializes fixtures but the suite still lacks a dedicated windows filesystem regression. add one case that exercises git ls-files output with \r\n and validates check behavior remains correct.

as per coding guidelines, "tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/repo-hygiene.test.ts` around lines 31 - 35, Add a deterministic Vitest
regression that simulates Windows CRLF git output for tracked-path parsing: in
repo-hygiene.test.ts alongside makeRepoFixture, add a test which stubs the git
invocation that the module uses (e.g., mock child_process.execFileSync or the
internal helper that calls `git ls-files`) to return a CRLF-separated string
like "file1.txt\r\nsub/dir/file2.txt\r\n", then call the exported check function
(or the parsing function under test) and assert it correctly returns/handles the
two paths; ensure the stub is only active for that test and is restored after to
keep tests deterministic and isolated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/repo-hygiene.js`:
- Around line 152-174: collectCandidates currently deduplicates root-level
entries via the candidatePaths Set in addCandidate, but
collectNestedTempCandidates is adding nested candidates directly so duplicates
(same path) can be added twice; modify collectNestedTempCandidates to use the
same addCandidate/candidatePaths Set or accept addCandidate as a parameter so
all nested additions call addCandidate (or check candidatePaths before pushing)
ensuring collectCandidates, addCandidate, candidatePaths and
collectNestedTempCandidates share the same path-set to avoid processing the same
path twice.

---

Duplicate comments:
In `@scripts/repo-hygiene.js`:
- Around line 199-209: The getTrackedPaths function currently swallows any git
errors and returns an empty array, which can hide problems and make checks pass
incorrectly; change the catch to surface the failure instead of returning [] —
either rethrow the caught error or throw a new Error with context (e.g., include
the original error message) so callers of getTrackedPaths see that git failed;
locate the function getTrackedPaths and update its catch block to not return a
silent empty array but to propagate/log and throw the error so repo-hygiene
checks fail visibly when git is unavailable or repo metadata is broken.
- Around line 231-234: The current substring check using gitignore.includes(...)
can produce false positives (e.g., "tmp-special/" matching required "tmp"), so
change the validation to line-based matching: split the file content from
gitignore into lines, trim each line and ignore empty lines and comment lines
(starting with '#'), normalize both existing lines and
REQUIRED_GITIGNORE_PATTERNS by removing trailing slashes before comparison, then
build a Set of normalized lines and compute missingPatterns by checking which
normalized required patterns are not present in that Set (use the variables
gitignore, REQUIRED_GITIGNORE_PATTERNS and update missingPatterns logic in
repo-hygiene.js accordingly).
- Around line 62-69: The flag-parsing loop accepts missing values or treats the
next flag as a value for args.mode and args.root; update the loop that iterates
argv to validate the next token before consuming it: in the for-loop handling
"--mode" and "--root" check that argv[i+1] exists and does not start with "--"
(or is not another known flag), otherwise throw or print a clear error and exit
(or set a default), then only assign args.mode and args.root and increment i
when the next token is valid; reference the existing argv loop and the args.mode
/ args.root assignments to locate where to add the validation.

In `@test/repo-hygiene.test.ts`:
- Around line 38-49: The tests create temporary repo fixtures with
makeRepoFixture() and remove them with fs.rm(...) but cleanup only runs on
success; change each test using runRepoHygiene(...) to guarantee removal by
wrapping the per-test fixture lifecycle in a try/finally or move rm into a
shared afterEach hook that deletes the fixture root, ensuring makeRepoFixture(),
runRepoHygiene(), and any asserts all run inside the try and fs.rm(root, {
recursive: true, force: true }) runs in finally (or implement afterEach that
references the created root), and apply this pattern to all affected tests in
test/repo-hygiene.test.ts so temp repos are always removed even if assertions
fail.
- Around line 31-35: Add a deterministic Vitest regression that simulates
Windows CRLF git output for tracked-path parsing: in repo-hygiene.test.ts
alongside makeRepoFixture, add a test which stubs the git invocation that the
module uses (e.g., mock child_process.execFileSync or the internal helper that
calls `git ls-files`) to return a CRLF-separated string like
"file1.txt\r\nsub/dir/file2.txt\r\n", then call the exported check function (or
the parsing function under test) and assert it correctly returns/handles the two
paths; ensure the stub is only active for that test and is restored after to
keep tests deterministic and isolated.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 5b9cad2 and 9ddc52e.

📒 Files selected for processing (2)
  • scripts/repo-hygiene.js
  • test/repo-hygiene.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/repo-hygiene.test.ts
🧬 Code graph analysis (1)
test/repo-hygiene.test.ts (1)
scripts/repo-hygiene.js (2)
  • args (50-55)
  • args (250-250)

Comment thread scripts/repo-hygiene.js
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
scripts/repo-hygiene.js (2)

207-221: ⚠️ Potential issue | 🟠 Major

do not let git ls-files failures degrade into a passing check.

Line 207 in lib/scripts/repo-hygiene.js:207 returns [] on git failures, which can falsely pass tracked-scratch validation in check.

proposed fix
 function getTrackedPaths(rootPath) {
 	try {
 		const output = execFileSync("git", ["ls-files"], {
@@
 		});
 		return output.split(/\r?\n/).filter(Boolean);
 	} catch (error) {
-		console.warn(
-			`repo-hygiene warning: git ls-files failed in getTrackedPaths: ${error instanceof Error ? error.message : String(error)}`,
-		);
-		return [];
+		throw new Error(
+			`git ls-files failed in ${rootPath}: ${error instanceof Error ? error.message : String(error)}`,
+		);
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/repo-hygiene.js` around lines 207 - 221, The getTrackedPaths function
currently swallows git errors and returns [] which masks failures; modify
getTrackedPaths so it does not return an empty array on failure but instead
rethrows the caught error (or throw a new Error with context) so the calling
validation (check) fails hard; update the catch block in getTrackedPaths to
throw the error (including a descriptive message and original error details)
rather than console.warn/return [], ensuring callers like check will surface the
failure.

137-158: ⚠️ Potential issue | 🟡 Minor

nested candidate collection still bypasses dedupe.

Line 145 in lib/scripts/repo-hygiene.js:145 pushes directly into candidates, so paths can be processed twice (root scan + nested scan) when names match both collectors.

proposed fix
-async function collectNestedTempCandidates(rootPath, dirPath, candidates) {
+async function collectNestedTempCandidates(rootPath, dirPath, addCandidate) {
 	const entries = await fs.readdir(dirPath, { withFileTypes: true });
 	for (const entry of entries) {
 		const entryPath = path.join(dirPath, entry.name);
@@
 		if (isDeepTempCandidate(entry.name, entry.isDirectory())) {
-			candidates.push({
-				name: path.relative(rootPath, entryPath).replaceAll("\\", "/"),
-				path: entryPath,
-				isDirectory: entry.isDirectory(),
-			});
+			addCandidate(
+				path.relative(rootPath, entryPath).replaceAll("\\", "/"),
+				entryPath,
+				entry.isDirectory(),
+			);
 			if (entry.isDirectory()) {
 				continue;
 			}
 		}
 		if (entry.isDirectory()) {
-			await collectNestedTempCandidates(rootPath, entryPath, candidates);
+			await collectNestedTempCandidates(rootPath, entryPath, addCandidate);
 		}
 	}
 }
@@
-	await collectNestedTempCandidates(rootPath, rootPath, candidates);
+	await collectNestedTempCandidates(rootPath, rootPath, addCandidate);

Also applies to: 160-182

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/repo-hygiene.js` around lines 137 - 158, collectNestedTempCandidates
currently pushes entries directly into candidates, causing duplicates when the
same relative path was already added by the root scan; fix by deduplicating
before push: compute the relative name (as currently done), maintain a seen set
(e.g., seenCandidateNames) or check candidates for an existing entry with the
same name, and only push if not already present; apply the same dedupe check to
any other nested collector functions that add to the same candidates array.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/repo-hygiene.js`:
- Around line 62-81: The CLI currently ignores unrecognized flags in the argv
parsing loop, which can let typos like "--roo" silently fall through; update the
for-loop that reads argv (the block that sets args.mode, args.root, args.dryRun)
to detect any argument that starts with "--" but isn't one of the handled flags
and immediately throw an Error (e.g. "Unknown flag: <flag>") instead of ignoring
it; reference the existing symbols argv, args.mode, args.root, args.dryRun and
the argv parsing loop to add this validation so unknown --flags are rejected.

In `@test/repo-hygiene.test.ts`:
- Around line 117-129: Add a deterministic regression test that verifies Windows
backslash separators are normalized by the tracked-scratch detection: in
test/repo-hygiene.test.ts add a case similar to the CRLF test but create a
nested file (e.g., path.join(root, "nested", "task_plan.md")), ensure git tracks
it, set core.autocrlf as in the existing test, run
runRepoHygiene(["check","--root", root]) and assert the stderr contains the
expected nested filename using a Windows-style separator (backslash) to ensure
the logic in lib/scripts/repo-hygiene.js (candidate naming normalization around
the code at the former line 146) correctly handles '\' separators. Ensure the
test is vitest-compatible and deterministic (create the nested directory, write
the file, add/commit as needed) so the regression is locked down.

In `@test/stream-failover.test.ts`:
- Around line 52-67: Add a deterministic race-regression test that forces the
timeout and a read failure to happen concurrently to ensure
withStreamingFailover only triggers one failover: create a test using
vi.useFakeTimers() and a fallback spy (vi.fn) plus the existing helpers
makeStallingResponse() and makeSseResponse() to craft a response that will both
throw on read and hit the stallTimeoutMs at the same instant, call
response.text(), advance timers with vi.advanceTimersByTimeAsync(...) to create
the race, then await the text and assert the fallback spy was called exactly
once and that the failover marker emitted by withStreamingFailover appears
exactly once in the resulting text (e.g., contains "codex-multi-auth failover
1"). Ensure the test is fully deterministic under vitest fake timers and mirrors
the existing "switches to fallback stream when primary stalls" structure so it
repros the concurrency bug covered by lib/request/stream-failover.ts handling.

---

Duplicate comments:
In `@scripts/repo-hygiene.js`:
- Around line 207-221: The getTrackedPaths function currently swallows git
errors and returns [] which masks failures; modify getTrackedPaths so it does
not return an empty array on failure but instead rethrows the caught error (or
throw a new Error with context) so the calling validation (check) fails hard;
update the catch block in getTrackedPaths to throw the error (including a
descriptive message and original error details) rather than console.warn/return
[], ensuring callers like check will surface the failure.
- Around line 137-158: collectNestedTempCandidates currently pushes entries
directly into candidates, causing duplicates when the same relative path was
already added by the root scan; fix by deduplicating before push: compute the
relative name (as currently done), maintain a seen set (e.g.,
seenCandidateNames) or check candidates for an existing entry with the same
name, and only push if not already present; apply the same dedupe check to any
other nested collector functions that add to the same candidates array.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 9ddc52e and b7d3cf9.

📒 Files selected for processing (3)
  • scripts/repo-hygiene.js
  • test/repo-hygiene.test.ts
  • test/stream-failover.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/stream-failover.test.ts
  • test/repo-hygiene.test.ts
🧬 Code graph analysis (2)
scripts/repo-hygiene.js (1)
scripts/install-codex-auth.js (1)
  • dryRun (31-31)
test/stream-failover.test.ts (1)
lib/request/stream-failover.ts (1)
  • withStreamingFailover (115-234)
🔇 Additional comments (2)
test/stream-failover.test.ts (1)

38-40: timer restoration is clean and deterministic.

test/stream-failover.test.ts:38 centralizes vi.useRealTimers() in afterEach, which keeps fake-timer state isolated across tests.

test/repo-hygiene.test.ts (1)

40-46: fixture teardown is now failure-safe.

test/repo-hygiene.test.ts:40 guarantees temp repo cleanup even when assertions fail.

Comment thread scripts/repo-hygiene.js
Comment thread test/repo-hygiene.test.ts
Comment thread test/stream-failover.test.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
scripts/repo-hygiene.js (1)

62-81: ⚠️ Potential issue | 🟠 Major

reject unknown args instead of silently ignoring them.

lib/scripts/repo-hygiene.js:62-81 ignores unexpected flags/positionals. a typo like --roo falls back to default root and can run cleanup in the wrong repo.

proposed fix
 		} else if (arg === "--dry-run") {
 			args.dryRun = true;
+		} else if (arg.startsWith("--")) {
+			throw new Error(`Unknown flag: ${arg}`);
+		} else {
+			throw new Error(`Unexpected positional argument: ${arg}`);
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/repo-hygiene.js` around lines 62 - 81, The argument parsing loop
currently ignores unknown flags which allows typos to be silently accepted;
update the for-loop that iterates argv (the block handling "--mode", "--root",
"--dry-run") to reject any unrecognized argument by throwing an Error (e.g.,
"Unknown argument: <arg>") instead of falling through, and ensure you still
advance the index for recognized flags (args.mode, args.root, args.dryRun) while
not swallowing or misinterpreting stray positionals or malformed flags.
test/repo-hygiene.test.ts (1)

117-129: ⚠️ Potential issue | 🟠 Major

add a windows separator regression, not just crlf coverage.

test/repo-hygiene.test.ts:117-129 only verifies crlf parsing and root-level filename matching. it still does not lock down windows \ separator behavior for nested tracked scratch paths consumed by lib/scripts/repo-hygiene.js:225-231.

proposed deterministic regression test
+	it("detects nested tracked scratch files with windows-style separators", async () => {
+		fixtureRoot = makeRepoFixture();
+		const root = fixtureRoot;
+		await fs.writeFile(path.join(root, ".gitignore"), requiredGitignore.replaceAll("\n", "\r\n"));
+		await fs.mkdir(path.join(root, "nested"), { recursive: true });
+		await fs.writeFile(path.join(root, "nested", "task_plan.md"), "temp plan\r\n");
+		execFileSync("git", ["config", "core.autocrlf", "true"], { cwd: root, stdio: "ignore" });
+		execFileSync("git", ["add", ".gitignore"], { cwd: root, stdio: "ignore" });
+		execFileSync("git", ["add", "-f", path.join("nested", "task_plan.md")], { cwd: root, stdio: "ignore" });
+
+		const result = runRepoHygiene(["check", "--root", root]);
+		expect(result.status).toBe(1);
+		expect(result.stderr).toMatch(/nested[\\/]+task_plan\.md/);
+	});

As per coding guidelines test/**: tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/repo-hygiene.test.ts` around lines 117 - 129, Add a deterministic
regression test in test/repo-hygiene.test.ts that covers Windows path-separator
behavior for nested tracked scratch files: create a nested directory (e.g.,
"nested/path/task_plan.md" using path.join), write and git-add that file, then
run runRepoHygiene(["check", "--root", root]) and assert the tool returns
non-zero and that stderr contains the nested file name regardless of separator;
specifically ensure the assertion accepts both forward-slash and backslash
variants (or normalize separators before asserting) to validate
lib/scripts/repo-hygiene.js (the code around lines 225-231) correctly handles
backslash-separated git ls-files output.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/repo-hygiene.js`:
- Around line 225-231: The current detection loop using tracked.has(name) misses
nested or Windows-style paths; update the logic around getTrackedPaths(rootPath)
/ tracked / TRACKED_SCRATCH_FILES to normalize and compare paths instead of
exact string equality: normalize all tracked paths and scratch patterns to a
consistent separator (e.g., replace backslashes with forward slashes or use
path.posix.normalize), then detect violations by checking if any normalized
tracked path endsWith the normalized scratch entry (or matches via path.relative
=== '' or only filename match) and push those into trackedViolations; ensure you
update the comparison in the loop that references tracked,
TRACKED_SCRATCH_FILES, and getTrackedPaths so nested paths like
"nested/task_plan.md" are flagged.

In `@test/repo-hygiene.test.ts`:
- Around line 37-144: Add a deterministic regression test that runs repo-hygiene
"check" and "clean" concurrently against the same fixture to exercise the race
windows in the repo-hygiene handlers; use the existing helpers makeRepoFixture,
requiredGitignore and runRepoHygiene to create a fixture root, write .gitignore,
then launch both runRepoHygiene([... "check", "--root", root]) and
runRepoHygiene([... "clean", "--mode", "aggressive", "--root", root]) in
parallel via Promise.all, wait for both results, assert their exit codes and
that the final filesystem state is consistent (expected files
removed/preserved), and ensure determinism by using the fixture sentinel files
(create known files like task_plan.md and a temporary audit file) so the overlap
is reproducible and the assertions validate no intermittent failures.
- Around line 136-140: The test mutates process.env.PATH directly and restores
it only after calling runRepoHygiene, risking leakage if runRepoHygiene throws;
wrap the mutation and invocation in a try/finally so originalPath is always
restored — e.g., save originalPath, set process.env.PATH = "", call
runRepoHygiene(["check","--root", root]) inside try, and restore
process.env.PATH = originalPath in finally (reference runRepoHygiene and the
test block in test/repo-hygiene.test.ts).

---

Duplicate comments:
In `@scripts/repo-hygiene.js`:
- Around line 62-81: The argument parsing loop currently ignores unknown flags
which allows typos to be silently accepted; update the for-loop that iterates
argv (the block handling "--mode", "--root", "--dry-run") to reject any
unrecognized argument by throwing an Error (e.g., "Unknown argument: <arg>")
instead of falling through, and ensure you still advance the index for
recognized flags (args.mode, args.root, args.dryRun) while not swallowing or
misinterpreting stray positionals or malformed flags.

In `@test/repo-hygiene.test.ts`:
- Around line 117-129: Add a deterministic regression test in
test/repo-hygiene.test.ts that covers Windows path-separator behavior for nested
tracked scratch files: create a nested directory (e.g.,
"nested/path/task_plan.md" using path.join), write and git-add that file, then
run runRepoHygiene(["check", "--root", root]) and assert the tool returns
non-zero and that stderr contains the nested file name regardless of separator;
specifically ensure the assertion accepts both forward-slash and backslash
variants (or normalize separators before asserting) to validate
lib/scripts/repo-hygiene.js (the code around lines 225-231) correctly handles
backslash-separated git ls-files output.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between b7d3cf9 and 3865581.

📒 Files selected for processing (2)
  • scripts/repo-hygiene.js
  • test/repo-hygiene.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/repo-hygiene.test.ts

Comment thread scripts/repo-hygiene.js Outdated
Comment thread test/repo-hygiene.test.ts
Comment thread test/repo-hygiene.test.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/repo-hygiene.test.ts`:
- Line 175: The variable `result` is declared without a type causing an
implicit-any lint error; change its declaration to use an explicit type like
`let result: ReturnType<typeof runRepoHygiene>` and update any subsequent
accesses/assertions to use optional chaining (e.g., `result?.someProp`) to be
defensive against null/undefined; locate `result` in the test and the
`runRepoHygiene` call to ensure the type aligns with the function's return shape
and adjust assertions accordingly.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 3865581 and 44bfb49.

📒 Files selected for processing (3)
  • scripts/repo-hygiene.js
  • test/repo-hygiene.test.ts
  • test/stream-failover.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/stream-failover.test.ts
  • test/repo-hygiene.test.ts
🧬 Code graph analysis (2)
test/stream-failover.test.ts (1)
lib/request/stream-failover.ts (1)
  • withStreamingFailover (115-234)
test/repo-hygiene.test.ts (1)
scripts/repo-hygiene.js (2)
  • args (50-55)
  • args (279-279)
🪛 Biome (2.4.4)
test/repo-hygiene.test.ts

[error] 175-175: This variable implicitly has the any type.

(lint/suspicious/noImplicitAnyLet)

🔇 Additional comments (5)
test/stream-failover.test.ts (1)

117-149: good deterministic concurrency regression coverage.

the new race test at test/stream-failover.test.ts:117-149 locks down the timeout/read-error overlap and asserts single failover behavior clearly.

As per coding guidelines test/**: tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior.

scripts/repo-hygiene.js (2)

62-84: argument parsing hardening looks correct.

rejecting unknown flags/extra positionals in lib/scripts/repo-hygiene.js:62-84 is the right safety guard for destructive commands.


229-267: tracked-scratch and gitignore validation are now robust.

the normalization + basename check and line-based pattern matching in lib/scripts/repo-hygiene.js:229-267 correctly closes prior false-negative/false-positive gaps.

test/repo-hygiene.test.ts (2)

152-167: windows path-separator regression is solid.

the nested scratch assertion in test/repo-hygiene.test.ts:152-167 correctly validates separator-agnostic detection.

As per coding guidelines test/**: tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior.


187-206: deterministic concurrent check/clean coverage is well added.

the parallel execution case in test/repo-hygiene.test.ts:187-206 is deterministic and exercises the race window directly.

As per coding guidelines test/**: tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior.

Comment thread test/repo-hygiene.test.ts Outdated
@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

@greptile review

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment thread scripts/repo-hygiene.js
console.log(`[dry-run] delete ${candidate.name}`);
continue;
}
await fs.rm(candidate.path, { recursive: true, force: true });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing retry logic for windows filesystem locks (EBUSY, EPERM, ENOTEMPTY). antivirus can lock files during deletion causing this to fail. test/rotation-integration.test.ts already implements removeWithRetry for this exact issue - same pattern needed here.

Suggested change
await fs.rm(candidate.path, { recursive: true, force: true });
const retryableCodes = new Set(["ENOTEMPTY", "EPERM", "EBUSY"]);
const maxAttempts = 6;
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
try {
await fs.rm(candidate.path, { recursive: true, force: true });
console.log(`deleted ${candidate.name}`);
break;
} catch (error) {
const maybeCode = error instanceof Error && "code" in error ? (error as { code?: string }).code : undefined;
const shouldRetry = maybeCode !== undefined && retryableCodes.has(maybeCode);
if (!shouldRetry || attempt === maxAttempts) {
throw error;
}
await new Promise((resolve) => setTimeout(resolve, attempt * 25));
}
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/repo-hygiene.js
Line: 206

Comment:
missing retry logic for windows filesystem locks (EBUSY, EPERM, ENOTEMPTY). antivirus can lock files during deletion causing this to fail. `test/rotation-integration.test.ts` already implements `removeWithRetry` for this exact issue - same pattern needed here.

```suggestion
		const retryableCodes = new Set(["ENOTEMPTY", "EPERM", "EBUSY"]);
		const maxAttempts = 6;
		for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
			try {
				await fs.rm(candidate.path, { recursive: true, force: true });
				console.log(`deleted ${candidate.name}`);
				break;
			} catch (error) {
				const maybeCode = error instanceof Error && "code" in error ? (error as { code?: string }).code : undefined;
				const shouldRetry = maybeCode !== undefined && retryableCodes.has(maybeCode);
				if (!shouldRetry || attempt === maxAttempts) {
					throw error;
				}
				await new Promise((resolve) => setTimeout(resolve, attempt * 25));
			}
		}
```

How can I resolve this? If you propose a fix, please make it concise.

Fix in Codex

Comment thread test/repo-hygiene.test.ts
if (!fixtureRoot) {
return;
}
await fs.rm(fixtureRoot, { recursive: true, force: true });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test fixture cleanup uses bare fs.rm without retry logic. on windows this can fail with EBUSY/EPERM when antivirus locks the temp directory. use the same removeWithRetry pattern from test/rotation-integration.test.ts

Prompt To Fix With AI
This is a comment left during a code review.
Path: test/repo-hygiene.test.ts
Line: 65

Comment:
test fixture cleanup uses bare `fs.rm` without retry logic. on windows this can fail with EBUSY/EPERM when antivirus locks the temp directory. use the same `removeWithRetry` pattern from `test/rotation-integration.test.ts`

How can I resolve this? If you propose a fix, please make it concise.

Fix in Codex

@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

Superseded by #14 — all changes from this PR have been merged into the unified supersede PR, with additional Windows filesystem safety fixes applied.

@ndycode ndycode closed this Feb 28, 2026
ndycode added a commit that referenced this pull request Feb 28, 2026
feat: unified overhaul — consolidate PRs #7, #8, #10, #11, #12, #13
@ndycode ndycode deleted the chore/repo-hygiene-quality-speed branch March 3, 2026 12:12
ndycode added a commit that referenced this pull request Apr 6, 2026
ndycode added a commit that referenced this pull request Apr 6, 2026
feat: unified overhaul — consolidate PRs #7, #8, #10, #11, #12, #13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant