Product
A reviewer that runs
the code it reads.
Most review tools read the diff. Firstpass builds the branch, runs the tests and reports only what it can show.
01
Reads the whole change.
A diff shows what moved, not what it touches. Firstpass follows every changed function to the code that calls it and the code it calls, in files the pull request never opened.
Call graph traced across files and packages
Reads up to 40,000 lines around a change
Works on monorepos without extra setup
#4127 payments-api · what was read
src/capture/retry.ts
+31 −8
calls
src/gateway/client.ts
throws
src/errors/capture-error.ts
src/capture/backoff.ts
+3 −2
src/capture/retry.test.ts
+14 −2
3 files changed
2 more files read
212 lines traced
build and test
exit 1
$ git fetch origin pull/4127/head
$ pnpm install --frozen-lockfile
dependencies restored from cache (2.1 s)
$ pnpm build
build ok · 9.8 s
$ pnpm test --changed
captures on the first attempt (38 ms)
backs off between attempts (112 ms)
retries stop after 5 attempts (timeout 5000 ms)
42 tests · 41 passed · 1 failed · 12.4 s
02
Runs it.
Your branch is checked out and built in an isolated container. The tests that cover the changed code run first, so a failure arrives with its name, its file and its line.
Affected tests first, the full suite if you ask
Uses your lockfile, your scripts, your CI image
The container is destroyed when the review is posted
03
Comments on the exact line.
One comment per problem, on the line that causes it, with the measured cost or the failing test. Where the fix is safe, it comes as a suggestion you apply in one click.
No comments on style or naming
Suggestions are applied as a normal commit
Dismiss a comment and it stays dismissed
Reviewed in 01:12
Load orders with their items
+31 −9 2 files
app/models/order.rb
17
has_many :items, dependent: :destroy
18
scope :recent, -> { order(placed_at: :desc).limit(200) }
19
20
def self.dashboard(account)
21
−
where(account: account).recent
21
+
where(account: account).recent.map do |order|
firstpass
SHOULD FIX
line 21
This loads items once per order: 1 query becomes 201 on the orders page. Preload them.
−
where(account: account).recent.map do |order|
+
where(account: account).recent.includes(:items).map do |order|
22
+
{ id: order.id, lines: order.items.size }
23
+
end
24
end
1 more comment · 1 nit
firstpass.yml
1
rules:
2
- “Money is stored as integer cents, never floats”
3
- “Every new endpoint needs an auth test”
4
- “No direct imports from /internal in /web”
5
ignore:
6
- “**/*.snap”
7
severity: block_on_bug
firstpass
Rule 1
amount is parsed with parseFloat
billing/invoice.ts · line 88
firstpass
Rule 2
POST /refunds has no test for a signed-out caller
api/refunds.ts · line 12
04
Learns the team’s rules.
Write the rules once, in plain words, in a file in the repository. Firstpass applies them to every pull request and names the rule in the comment, so nobody has to remember them.
Rules live in `firstpass.yml`, reviewed like code
Every rule comment cites its rule
Rules can warn or block, per rule
05 Severity
Three levels.
You choose what stops a merge.
Should fix
1,104
comments
Likely problem, measured cost.
Nit
0 shown
of 2,870 found
Small and optional. Off by default.
Counts from one sample week across 40 repositories. Replace with your own.
06 Languages and hosts
Ten languages,
built and tested.
GitHub
GitLab
Bitbucket
Azure Repos
Language
Build
Tests
Status
TypeScript
pnpm, npm, yarn, bun
Jest, Vitest
Stable
Python
uv, pip, poetry
pytest
Stable
Go
go build
go test, -race
Stable
Ruby
bundler
RSpec, Minitest
Stable
Java
Gradle, Maven
JUnit
Stable
Kotlin
Gradle
JUnit, Kotest
Stable
Rust
cargo
cargo test
Stable
C#
dotnet
xUnit, NUnit
Stable
PHP
composer
PHPUnit, Pest
Beta
Swift
SwiftPM, xcodebuild
XCTest
Beta