Skip to content

feat(stage): require an approved mirror PR when approval is on - #137

Merged
cabljac merged 1 commit into
gate/02-approval-settingfrom
gate/03-approval-gate
Oct 19, 2026
Merged

cabljac merged 1 commit into
gate/02-approval-settingfrom
gate/03-approval-gate

Conversation

@cabljac

@cabljac cabljac commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner

With approval on, stage now checks the branch's open mirror PR before it rebuilds anything. Through gh api (netExec) it reads each reviewer's latest review (a dismissed one is no opinion), each approver's mirror permission, and every check run and commit status on the PR head. One approval must be on the head, from a User account that is not the PR author and has write or admin access; two-person also excludes the gh login running stage, and only two-person reads that login. Change requests, pending checks and failed checks block. The PR must target the mirror default branch; several candidates refuse and name --internal-pr. Stage pins the rebuild to the approved head and takes the upstream PR body from that PR.

Refusals exit 1 with code approval and a hint. --dry-run reports result.approval. stage issue is not gated.

Tests, red first: tests/integration/approval-gate.test.ts, the envelope case in tests/integration/cli.test.ts, tests/unit/approval.test.ts. bun run verify exits 0. Counts: unit 1272 to 1283, integration 477 to 500.

Not verified: e2e tiers and live GitHub. The gh calls are proven only against the stub in tests/harness/gh-mirror.ts. Zero check runs still pass (deferred).

With `approval` set to `reviewed` or `two-person`, stage reads the
branch's open mirror PR into the mirror default branch through gh api
(netExec) after the fetch: each reviewer's latest review (a dismissed
latest review is no opinion), the mirror permission of each candidate
approver, and every check run and commit status on the PR head. One
approval must be on the head commit, from a User account that is not
the PR author and has write or admin access; two-person also excludes
the gh login running stage, and only two-person reads that login.
Outstanding change requests, pending checks and failed checks block.
A PR into another base is refused (wrong-base); several candidate PRs
are refused (ambiguous-pr) and the hint names --internal-pr. The local
branch must point at the approved head, and stage pins the rebuild to
that commit with source.sha, so new commits after the approval do not
ship. The upstream PR body comes from the approved mirror PR.

Refusals are ApprovalError, code `approval`, exit 1, with a one-line
hint; the self-approval and no-mirror-PR refusals name
`venfork stage approval reviewed`. --dry-run reports the result in
result.approval and still previews. Nothing about the approval goes
into published commits or the upstream PR. With the level off, stage
makes no new gh call. gh reads end in `| tojson` so output is one value
per line on a TTY too. `stage issue` is not gated: it publishes
redacted issue text, not code.
@cabljac
cabljac force-pushed the gate/03-approval-gate branch from a21b7f8 to 595108b Compare October 19, 2026 17:26
@cabljac
cabljac marked this pull request as ready for review October 19, 2026 17:29
@cabljac
cabljac added this pull request to stack #142 October 19, 2026 17:29
@cabljac
cabljac merged commit 4459f58 into main Oct 19, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant