fix(test): close remove-tree lint AST false positives and negatives - #825
Conversation
Recognize shorthand maxRetries, shadowed local rm, named promises rebinds from fs/node:fs, and assertion/paren/satisfies option wrappers. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
|
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aeb8c3f1c3
ℹ️ 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".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
…dings as remove-tree shadows
After #823,
scripts/check-test-remove-tree.mjsstill missed four parser-backed cases Codex flagged.Fixes
maxRetries:{ recursive: true, maxRetries }is accepted (ShorthandPropertyAssignment).rm: a later local binding afterimport { rm } from 'node:fs'is not treated as Node-bound.promisesrebind:import { promises as fs } from 'node:fs'(or'fs') is a namespace /.rmcarrier.AsExpression,ParenthesizedExpression, andSatisfiesExpressionso{ recursive: true } as constis still seen as recursive.The gate stays typescript-5 AST-backed. No regex / text masking.
Tests
packages/agent-bundle/tests/check-test-remove-tree.test.tsnow covers both directions:maxRetriesis OKrmis ignored; unshadowed Nodermstill failsimport { promises as fs } from 'node:fs'+ recursivermwithoutmaxRetriesfails; withmaxRetriespasses{ recursive: true } as constwithoutmaxRetriesfails; withmaxRetriespassessatisfieswrappers without retries still failExisting removeTree helper exemption and good-path cases stay in the file.
Notes
tests/**only; no changeset (publishablechangedFilePatternsexemptstests/**).Verification
Ran after
pnpm build(rstest dist-freshness):Local merge gate (head 7fde282, contains origin/main b0b131b)
Merged
origin/maincleanly (no conflicts). Commands and results:pnpm install --frozen-lockfilepnpm buildpnpm typecheckpnpm lint(rslint .+node scripts/check-test-remove-tree.mjs)pnpm test:unitrstest ... check-test-remove-tree.test.tsnode scripts/check-test-remove-tree.mjsFollow-up fixes on this branch
for/for-of/for-ininitializer bindings,case/defaultclause declarations, and named function expressions now count as shadows.rmused in that method's computed key or decorator (false negative). Fixed by applying a parameter shadow only when the lookup comes from the function body or parameters. Regression tests were added, and the reviewer confirmed the fix.varhoisting: declined. Avarhoisted out of a nested block is treated as block-scoped, so the gate can only produce a false positive here, never a false negative. The limit is documented with aponytail:comment.for-in, default-clause,<const>assertion, TSX/.mjsfile-kind, and retry-positive wrapper cases.recursiveRmCallsnow passesfileNametoremovalBindings, so.tsxand.mjsfiles are parsed with the correct script kind.No changeset: only root
scripts/(not a package) andtests/**(excluded by.changeset/config.json) change. TheChangeset presentcheck is green.Root verification verdict: PASS+NOTES at
7fde282430Base
b0b131bbf7(currentorigin/main), head7fde282430. The verdict pins this head. A new head with a different patch-id voids it.Independent lanes, none run by the PR owner:
pnpm install --frozen-lockfile,build,typecheck,lint,test:unit, focused rstest,node scripts/check-test-remove-tree.mjstest:unit4488 passed, 0 failed, 6 skipped. Focused 18/18. Lint exit 0.tests/**is package-relative, rootpackage.jsonis private). Thevarhoisting decline holds: it can only cause false positives.Should-fix, not blocking:
rm, on a parameter that is itself namedrm, is flagged on trunk and missed at head. The parameter shadow is applied to the decorator expression, which JavaScript evaluates in the outer scope. The repro isimport { rm } from 'node:fs/promises'; class S { run(@hook(rm(root, { recursive: true })) rm) {} }.recursiveRmCallsreturns line 2 on trunk and nothing at head. The input is contrived, so this does not block.unwrapExpressionskipsNonNullExpression, sorm(root, { recursive: true }!)passes. Trunk misses it too. The canonicalunwrapinpackages/agent-bundle/src/config/state-extract.tshandles it.Notes:
const { rm } = await import(...),require('node:fs').rm,fs.promises.rmon a default import,maxRetries: 0,recursivepassed through a variable, andrmSync.CI at this head was still running when this verdict was written. Merge after it is green.