Skip to content

test: migrate recursive rmSync to removeTree(Sync) and gate rmSync, rmdir, and maxRetries: 0 - #849

Merged
ScriptedAlchemy merged 3 commits into
mainfrom
fix/remove-tree-gate-sync
Sep 25, 2026
Merged

ScriptedAlchemy merged 3 commits into
mainfrom
fix/remove-tree-gate-sync

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Last pass on the scripts/check-test-remove-tree.mjs lint gate, following #846, #847, and #848.

Migrations

This moves the three real recursive rmSync call sites onto the canonical helper:

  • packages/rsc-runtime/tests/plugin-root.test.ts: the removal was in the finally of a synchronous it. That test can await, so it is now async and calls await removeTree(directory).
  • packages/agent-bundle/tests/playground-service.test.ts: the removal is inside a synchronous now() clock callback, which has to delete .pending and write a blocker file before it returns. It now calls removeTreeSync(pendingRoot).
  • packages/agent-bundle/tests/support/shared-pack.ts: the removal is in a process.once('exit') handler, where async work never runs. It now calls removeTreeSync(destination).

removeTreeSync

removeTreeSync sits next to removeTree in tests/support/remove-tree.ts and runs the same retry loop:

  • the same retry codes, through one shared isRetryable check
  • 6 attempts, with a retryDelay * (attempt + 1) backoff
  • a synchronous sleep via Atomics.wait

It does not rely on Node's own rmSync({ maxRetries }), because Node 24 routes that through the C++ binding.rmSync, and there the backoff behavior is up to Node's internals. A test covers one ENOTEMPTY followed by success (2 attempts, at least 45 ms elapsed), and a persistent ENOTEMPTY that throws after 6 attempts.

Gate

  • rmSync, rmdir, and rmdirSync are now flagged under the same literal-options and shadowing rules as rm. That covers named and aliased imports, ns.X, ns.promises.X, and wrapped callees.
  • A literal maxRetries: 0, including forms like "maxRetries": 0x0, now counts as not retried.
  • The failure text now reads bare recursive rm. Use removeTree (removeTreeSync if it cannot await).
  • One ponytail: line in the header names the ceiling. Options held in a variable or spread, a non-literal recursive or maxRetries, and indirect bindings are not followed; closing that needs data-flow analysis. The indirect bindings are local aliases, destructuring, dynamic import or require, ns['rm'], and .call.

The new gate tests failed on main. Before the migration, the extended gate flagged exactly the three sites above. After it, the gate reports zero violations on the real tree.

Checks

Run on the merged head 0610bdbb68, which contains origin/main c74702b9cc:

  • pnpm build: exit 0
  • pnpm typecheck: exit 0
  • pnpm lint (includes the gate, 0 violations): exit 0
  • pnpm test:unit: 4437 passed, 0 failed, 6 skipped
  • Focused run of the gate test, remove-tree.test.ts, plugin-root.test.ts, and playground-service.test.ts: 103 passed
  • pnpm test:packed, the pool that imports shared-pack.ts: 46 passed, 0 failed, 1 skipped
  • A Node process that registers removeTreeSync in a process.once('exit') handler confirmed a nested tree is removed on exit.

Independent review by change-risk-reviewer (Claude Opus 5.5) found no material findings, on both the first pass and the follow-up that rewrote removeTreeSync as a retry loop. Its first-pass notes, the backoff claim and the message text, are addressed.

No changeset: only packages/*/tests/** and root scripts/ change, and both are exempt per .changeset/README.md.

@changeset-bot

changeset-bot Bot commented Sep 25, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 0610bdb

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@ScriptedAlchemy
ScriptedAlchemy merged commit 1d661b4 into main Sep 25, 2026
5 checks passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-25T03:24:52.225079Z 0610bdb PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

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