Independently verified by a second review before publication.
We trained an AI agent for Gumroad. It reviews each PR merged to antiwork/gumroad with knowledge of how the codebase has evolved and which changes have caused bugs before, and every finding is independently verified before it is published, phrased as a question to verify. This is its first published finding.
Layout.tsx and WithContent.tsx from the DownloadPage mapping so that changes to them escalate to the full rspec suite. But an earlier rule exempts any TypeScript file that has a co-located .test.tsx. The moment someone adds Layout.test.tsx or WithContent.test.tsx, source-only changes to those components map to nothing, the exemption swallows them, the selector exits 0 with an empty list, and CI runs a placeholder spec instead of the full suite.
bin/branch-specs:175 · merged diff touched 2 files, 37 insertionsbin/branch-specs maps changed files to the rspec files a PR must run, escalating to the full suite when a change maps to nothing. The PR narrows the DownloadPage mapping with a negative lookahead so its two shared components no longer resolve to DOWNLOAD_PAGE_SPECS, and adds tests asserting that changing either file escalates. Neither component has a co-located Vitest file today, which is why the new tests pass.
app/javascript/components/DownloadPage/Layout.test.tsx, a normal thing to do for a shared component.Layout.tsx. The mapping at line 175 now excludes it, so spec_candidates and the fanout contribute nothing..test.tsx exists and hits next at 516, so the file is never added to the unattributed set that triggers escalation..github/workflows/tests.yml an empty list is treated as "nothing rspec-affecting" and CI substitutes spec/models/user_spec.rb as a floor. The full-suite run the PR was written to guarantee never happens.173 %r{\Aapp/javascript/components/DiscordButton} => DOWNLOAD_PAGE_SPECS + CHECKOUT_FLOW_SPECS, 174 # Importers: DownloadPage/WithContent.tsx:19, Checkout/Receipt.tsx:13. 175 %r{\Aapp/javascript/components/DownloadPage/(?!Layout\.tsx\z|WithContent\.tsx\z)} => DOWNLOAD_PAGE_SPECS, 176 # Layout.tsx and WithContent.tsx escalate: the former renders the 177 # UrlRedirects pages, the latter reaches ProductEdit/ContentTab through 178 # components/Download/{RichContent,FileList}.
505 elsif path.match?(%r{\Aapp/javascript/.*\.test\.(ts|tsx)\z}) || 506 path.match?(%r{\Aapp/javascript/.*\.d\.ts\z}) || 507 (path.match?(%r{\Aapp/javascript/.*\.(ts|tsx)\z}) && 508 File.exist?(path.sub(/\.(ts|tsx)\z/, '.test.\1'))) || 509 path == "app/javascript/components/EvaporateUploader.tsx" || 510 path == "vendor/assets/javascripts/evaporate.cjs" 511 # Covered by vitest, which lint_js runs on every PR (npm test). A 512 # co-located .test file is that module's suite; type-only .d.ts 513 # files ride with the consuming module. Evaporate's context hook and 514 # vendored client are exercised by useConfigureEvaporate vitest. 515 # None of these need rspec. 516 next
986 COUNT=$(echo "$SPECS" | grep -c . || true) 987 # An empty list here means the diff touched nothing rspec-affecting 988 # (selector escalates on app-code diffs that map to nothing). 989 if [ "$COUNT" -eq 0 ]; then 990 echo "No rspec-affecting changes; running the schema smoke spec as a floor." 991 SPECS="spec/models/user_spec.rb" 992 COUNT=1 993 fi
Add a case to spec/bin/branch_specs_test.rb whose base contains Layout.tsx and Layout.test.tsx and whose head changes only Layout.tsx, with expect_escalate: true, then run ruby spec/bin/branch_specs_test.rb. It reports expected exit 3 but got 0 with empty stdout. Repeat for WithContent.tsx.
The selector already has the right mechanism: ESCALATE_PATTERNS, checked before the per-path loop, lists files that must always run the full suite. One pattern for these two paths there replaces the lookahead, and a test case with a co-located test file present locks it in.
| Commit | What it showed |
|---|---|
e5cd221c1 2026-08-06Close branch-specs mapping gaps that escalated four PRs this week (#7102) | Introduced the co-located Vitest exemption that this PR's mapping does not account for. The two rules were written five weeks apart by the same agent with opposite goals for the same files. |
ee397989e 2026-09-14This PR | Adds the lookahead and tests for the no-test-file case only. |
| Both changed files are recent, so their own history is short; the agent's record of earlier cross-flow DownloadPage regressions is what made the shared components worth escalating in the first place. | |
bin/branch-specs line 175: the lookahead excludes Layout.tsx and WithContent.tsx from the DownloadPage mapping so they escalate, but the co-located-test exemption at lines 507 to 516 runs first. If either component gains a .test.tsx, a source-only change appears to map to nothing, get exempted, and exit 0 with an empty selection, which CI turns into the user_spec.rb floor rather than the full suite. Verifiable by a branch_specs_test.rb case with Layout.test.tsx present in base and only Layout.tsx changed, expecting escalation; it currently exits 0. Adding the two paths to ESCALATE_PATTERNS, which already runs before the loop, would make the promise hold regardless of test files.The agent learns from every merged PR and from every reply. When a finding is confirmed or fixed, that pattern is weighted up; when one is refuted, the correction is fed back and the agent stops raising it. Each review is more precise than the one before it.
Paying is optional. If this was useful, you can pay what you believe is fair: https://buy.stripe.com/4gMeVdfpVgl18Td4hg5AQ00. Reply on the pull request if you want to keep receiving our recommendations on every merged PR. The findings stand either way.
Produced by an automated review with independent verification, read-only, no tests executed. These reviews improve over time: confirmed findings sharpen the agent, refuted ones are fed back so they are not raised again. All claims are code-reading claims phrased for verification.