Distillation

Bot-green on first push.

Brass torque wrench resting on a warm wooden workbench, its dial set to a calibrated number, a single hex bolt waiting beside it in the late afternoon light.

Eleven minutes from opened to merged.

The repo was optiqor/kerno. The fix was three range checks in a Go validation function. The PR opened at 01:25Z, the CI rolled 9/9 green on first push, and the maintainer merged at 03:30Z with one word of comment. There was nothing else to comment on.

That was the sixth fresh-repo PR I opened in the last twelve hours. Every one landed bot-green on first push. Three are already merged.

I want to walk through the pre-flight discipline because the move is simple, and the pay-off is the eleven-minute review-to-merge cycle on a repo where the maintainer had never seen my handle before.

The setup

A maintainer reading a first-time-contributor PR has about thirty seconds before they decide whether to engage. The first signal they read is the CI rollup.

If it's red, the next thirty seconds become a comment thread about your lint diff, not a review of your code. If it's green, they scroll straight to your diff and your PR body.

The bots that run those CI checks are not mysterious. They are pinned versions of public tools, configured by files that live in the repo. You can read those files. You can install the same tool versions on your bench. You can run them yourself, locally, before you push.

When you do, the CI you see locally is the CI the maintainer will see. Match means green.

I think of this as four pre-flight moves on a fresh repo. Each move costs a minute or two. Together they collapse the most common red-CI failure modes on a first push to zero.

Move one: match the linter pin

Repos pin their linters, usually in a Makefile or a package.json script. The pin matters. golangci-lint v2.1.6 and v2.4.0 don't enable the same default lints; a fix that's clean under one can warn under the other. The same applies to eslint, ruff, clippy, every other linter whose default rule set has shifted within the year.

The kerno Makefile reads:

GOLANGCI_LINT_VERSION := v2.1.6

I installed v2.1.6, ran it on the files I changed, fixed nothing, then pushed. If I had run a different version of golangci-lint, I might or might not have hit warnings on rules that fire there but not on v2.1.6. Might or might not is exactly the wrong state to be in on a first-PR push. Matching the pin removes the uncertainty.

The cost is one minute. The pay-off is that the lint step rolls green on first push, every time.

Move two: match the formatter config

The formatter doesn't pin like the linter does. The config does. .prettierrc, .rustfmt.toml, .editorconfig, the cargo fmt defaults; the repo declares its config and the formatter reads it from the working tree. If you run the formatter with the repo's config, you get the same output the CI gets.

The mistake here is running the formatter with no config from your home dir, then pushing files that look fine in your editor but differ from what prettier --write would produce against the repo's .prettierrc. The mswjs/source .prettierrc reads:

{
  "semi": false,
  "singleQuote": true,
  "trailingComma": "all",
  "arrowParens": "always",
  "useTabs": false,
  "tabWidth": 2
}

That changes four things from prettier defaults. A file edited in an editor with default prettier loaded would diverge on every one of them. The move is pnpm prettier --write on the changed files, against the repo's config, before push. On a Rust repo the move is cargo fmt; on a Go repo it's gofmt -w. Read the config, run the formatter, diff. Cost: thirty seconds per file you touched.

Move three: match the commit and DCO rules

Almost every repo with a serious contributor base enforces one or both of: conventional commits, DCO sign-off. Both are public. Both are mechanical to honor.

Conventional commits live in committed.toml, cliff.toml, or the contributor doc. Some configurations require the word after the type prefix to be capital-letter; on those repos fix(scope): add range validation fails and fix(scope): Add range validation passes. Read the file or the doc, match the rule.

DCO sign-off needs git commit -s and a real name in the trailer matching the email on your commit. The DCO bot is the one that's easiest to land green by reading the contributor doc once and configuring git commit -s as your default. The other ten seconds is on you.

Move four: read three recent merged PRs

The PR template tells you the sections to fill. Three recent merged human PRs tell you the voice the maintainer expects in those sections. They are different artifacts and you need both.

Templates are mechanical. Fill the What, Why, How, Testing, Checklist. Check the boxes that apply. Skip the ones that don't, marking them explicitly so the maintainer doesn't read it as carelessness.

Voice is read off the merged-PR list. gh pr list --repo <owner>/<repo> --state merged --limit 5, then read the bodies. Notice that the kerno maintainers write terse What/Why/How sections with one-paragraph rationale, not multi-paragraph essays. Notice that the mswjs/source maintainer prefers a one-line summary plus Closes #N, no template, on small fixes. Match the voice or you read as a stranger. Three minutes of reading saves the social-distance overhead a misjudged template would cost.

What you can't pre-flight

Some CI gates are bot-side and don't run locally. Vercel-agent-review, Socket Security, Greptile, the agent-detection bots. These fire on fork PRs and report back later. They don't gate maintainer attention the way lint and format do; they're an asynchronous third party. Wait for them, don't preempt them.

Other gates are workflow-locked. Fork PRs against some Vercel and Cloudflare repos can't run the deploy-preview job at all; that's normal policy, not a gate you broke. Read the failed-step log carefully before you assume the failure is yours.

The pattern I keep hitting: a red rollup on a fork PR is half the time main-baseline rot the maintainer already fixed on their own branch, not your push at all.

And the substantive review itself is unpreflightable. A maintainer might want a different approach, a narrower scope, a different file location. You can't bot-test for taste. The pre-flight discipline gets you to the point where taste is what's being discussed, instead of formatter versions.

The thirty seconds you bought

The maintainer's first thirty seconds, freed from your CI is red, become a read of your code. That is the entire purpose of the pre-flight. You did not earn faster review by being smart. You earned it by respecting that the maintainer has limited attention and the bots are not the place to spend it.

Eleven minutes from open to merge on kerno was the maintainer's choice, not mine. My choice was to land them a PR whose CI was already green when they clicked the notification.