The path a pull request takes from "I pushed a branch" to "it is in main",
and the two rules along it that surprise people. Companion to
Architecture at a glance — that page tells you where your
change goes, this one tells you what happens to it afterwards.
claim an issue comment on it; a maintainer adds `status:claimed`
↓
branch + commit <your-initials>/<short-slug>, conventional prefix
↓
open a PR against main title in conventional form, `Resolves #<n>` in the body
↓
CI runs `verify` the one required check — see below
↓
review findings sorted Blocking / Secondary / Nit
↓
one approval required; a later push dismisses it
↓
merge queue squash, all-green, up to 5 built at once
↓
main
Claiming is the step people skip. An issue with the status:claimed label
already has someone on it, and a claim with no linked PR and no activity for
7 days is released automatically by
stale-claims.yml. Full rules:
Claiming an issue.
Branch protection on main requires exactly one status check, the verify
job in ci.yml. That single name hides the
steps below, and knowing which one failed saves you a guess:
| Step | Fails when |
|---|---|
npm run typecheck |
TypeScript does not compile |
npm run lint |
ESLint — including the design-system and colour-token rules |
npm run check:fixtures |
a committed résumé PDF carries real contact data |
npm run check:baselines |
a known-failure exemption is charged to a closed issue |
npm run check:core |
the publishable @offlinecv/core tarball is broken |
npm run test:coverage |
a test fails |
npm run build |
the production bundle does not build |
| fallow static analysis | never — the audit's exit code is swallowed, and the report is uploaded to code scanning |
check:fixtures runs before the test suite on purpose: the repo is public,
so a leaked PDF is the one failure that cannot be undone by a follow-up commit.
See the PII policy in
CONTRIBUTING-PROCESS.md
before you add any fixture.
Run the local gates first — npm run verify:quick while you iterate,
npm run verify before you push (a pre-push hook runs it for you). They are
not the sequence above: verify scopes the test run to the files you changed,
where CI always runs the whole suite. Treat a green verify as a fast
pre-flight, not a promise that CI will pass.
Findings are sorted into three buckets, and the label is a claim about the merge, not about tone:
- Blocking — must change before merge: a bug that fires on normal use, real
contact data in a fixture, a hardcoded colour where a semantic token belongs,
a raw
<button>where the design-system primitive belongs, or a description that claims behaviour the code does not have. - Secondary — a real pattern worth fixing, not a merge blocker.
- Nit — style and polish, explicitly non-blocking.
Two properties of the review are worth knowing as an author. The issue is the
spec — a PR that says Resolves #N while an acceptance criterion of #N is
unimplemented is Blocking, because merging closes the issue and nothing reopens
it. And the PR description is read last, as a claim to check against the
diff rather than a guide to what to look at, so a description that overstates
what changed is itself a finding.
One commit per PR. main merges through a merge queue, and the queue's
enqueue API carries no commit-message fields — so GitHub derives the squash
message from the repo's settings, which concatenate every commit on the branch
as bullets. A three-commit PR lands wip and fix lint in main's history
permanently. Collapse the branch before it reaches the queue; the full
derivation is in
Squash messages.
A push dismisses the approval. main has dismiss-stale-reviews-on-push
enabled, so pushing after someone approves sends the PR back to needing one.
Address every comment in one pass and push once, then reply on the threads —
not a push per comment.
Of the last 100 merged PRs (snapshot 2026-08-28): median time open 4.7 hours, 86% merged within a day, median 8 files changed, and 73 carried at least one review. Re-derive it yourself rather than trusting the snapshot:
gh pr list --repo offlinecv/OfflineCV --state merged --limit 100 \
--json number,createdAt,mergedAt,changedFiles,reviewsNobody is watching the queue for you, though. If a PR has been sitting, say so on the issue or in chat — silence from a reviewer who is mid-review and silence from one who has not opened it look identical from the author's side.