PR base branch policy¶
BenchBox does not support stacked PRs (a PR whose base is another feature branch). Open every change against an integration branch and rebase children after each parent lands.
Allowed bases¶
Base |
When |
|---|---|
|
Normal development PRs |
|
Release-lane PRs only |
|
Published-results lane only |
Any other base (including a sibling feature branch) is out of policy.
Why stacked bases get zero CI¶
Almost every PR workflow filters on those integration branches:
on:
pull_request:
branches: [develop] # or release / published-results
A PR opened against fix/parent therefore triggers no pr.yml, no
ci-required-result, and no browser lane. The GitHub PR page looks calm
(empty check list) rather than broken, and the change can reach develop
only when the parent merges — never validated on its own and attributed to
the parent’s PR.
That silence is intentional branch-filter design, not a CI outage. We do not widen every workflow’s branch filter to “support” stacking; that would dilute the integration-branch contract and still leave squash-merge chains needing rebases.
Loud failure: pr-base-guard.yml¶
.github/workflows/pr-base-guard.yml is the one PR workflow without a
branches: filter. It always reports:
Base is
develop/release/published-results→ pass in seconds; the normal CI lanes apply.Base is anything else → fail with an explicit message to retarget or fold into the parent.
It also listens for edited so retargeting an open PR re-evaluates (a PR
opened on develop and later pointed at a feature branch must not keep a
stale green result).
Unit pins live in tests/unit/workflows/test_stacked_pr_base_guard.py.
After a parent merges¶
develop is squash-merge only. A stacked chain would need a rebase and
force-push after every parent merge anyway. Preferred workflow:
Open each PR against
develop(or the appropriate integration base).If work depends on an unmerged parent, wait or fold into the parent PR.
After the parent squash-merges, rebase the child onto the updated base and force-push with
--force-with-leaseon the feature branch only.
“No checks” is not one failure mode¶
Use REST mergeable_state vocabulary from docs/operations/pr-triage.md
(GraphQL mergeable: CONFLICTING is the same situation as REST dirty):
Symptom |
Cause |
What to do |
|---|---|---|
Guard red; other lanes absent |
Base is not an integration branch |
Retarget to |
|
Conflicts with the base tip |
Rebase/resolve onto the base tip |
Required checks missing/stuck on an integration base; |
CI unfinished, path gate, or ruleset |
Inspect check runs — do not retarget |
Do not treat an empty check list on a feature base as “CI is fine” or as the
same problem as dirty (conflicts) or blocked (unfinished gates) on
develop.
On a conflicting PR, GitHub cannot build the merge ref, so pull_request
workflows (pr.yml and most other lanes) never start. That is not the same as
“no workflows at all”: develop-refresh-shadow.yml and
develop-ruleset-drift.yml use pull_request_target against trusted develop
and can still report. Diagnose with gh pr view <N> --json mergeable plus the
expected workflow names — do not infer conflicts, or a filter bug, from an
empty or partial check list alone.
Agent checklist¶
make pr-open(and manualgh pr create) must targetdevelopunless the change is explicitly forreleaseorpublished-results.Never open a PR with
--baseset to another feature branch.If
pr-base-guardfails, fix the base; do not try to “add CI” to the stacked base by editing branch filters.Short agent-facing summary:
AGENTS.md→ section Verification and close-out (stacked/feature-base PRs are unsupported).