Summary
lib/rules/git_state.ex runs GS001–GS008. None of them detects the invariant that failed on metadatastician/ziz on 2026-09-28: a ref with no merge base against the default branch.
Measured instance — arena/01a0d3a4-rsr-template-repo @ 121ebaeeb9b2543c119babae3fdc086bfd99f5e8:
git log -1 --format=%P # (empty) -> root commit, no parent
gh api repos/metadatastician/ziz/compare/main...arena/01a0d3a4-rsr-template-repo
# -> HTTP 404 "No common ancestor between main and arena/01a0d3a4-rsr-template-repo."
- The shared compare URL 404s, so the change could not be reviewed at all.
- It could not be merged without
--allow-unrelated-histories, which would have imported 478 files already present and reverted a dependabot bump.
- 478 of its 479 files were byte-identical to
main's. It was a session initialisation commit, not work.
- No PR existed, and none could have: there is nothing to diff against.
Why the existing rules miss it
| rule |
why it does not fire |
| GS003 branch divergence |
Compares the current branch against its own upstream — never the default branch — and requires ahead > 0 and behind > 0. On unrelated histories git rev-list --left-right --count HEAD...@{u} returns two arbitrary totals, so the one case where "diverged" is factually wrong is the one case the rule cannot name. |
| GS007 stale remote branches |
Advisory, and flags any non-main ref with equal weight. It renders a 1-commit abandoned orphan and a 100-commit branch with an open PR identically. Measured estate-wide: MetaManifold-WebUI had 4 branches that were content-identical to main (compare → files: 0) sitting alongside a branch backing an open PR. GS007 called all five the same thing. |
Proposed rule
GS009 — a ref has no merge base with the default branch.
# empty output == no common ancestor
git merge-base origin/main <ref>
Severity: high where the ref also has no PR; advisory where a PR exists (an open PR on an unrelated branch is a workflow problem, not a defect in the change).
Note the test must be merge base, not ancestry. Squash-merging is the estate default, so a fully merged side branch is routinely not an ancestor of main once the squashed commit replaces it. git merge-base --is-ancestor would fire on every squash-merged branch; git merge-base returning empty fires on none of them and exactly one of the orphans.
Suggested refinement to GS007
Classify by PR state before reporting, so the cleanup instruction is actionable:
| PR state |
content vs default |
disposition |
| merged |
identical |
prune — safe to delete |
| merged |
differs |
investigate |
| open |
— |
leave — live work |
| none |
— |
unmerged work — needs a decision |
| none |
— |
and no merge base → escalate (this class) |
Acceptance criteria
AC1 — GS009 exists, fires on a synthetic unrelated-history branch, and does not fire on a squash-merged branch.
AC2 — GS007 separates "merged and content-identical → prunable" from "orphaned → escalate".
AC3 — No muting. Specifically: no exemption keyed on a branch-name prefix. A rule that ignored refs starting arena/ would have hidden this one entirely, and the estate's own arena/* refs are the observed instance of the class.
AC4 — The rule reports the measured fact (empty merge base), not an interpretation ("diverged"), so the finding stays true when the history shape is unexpected.
Provenance
Found while triaging an orphan branch on metadatastician/ziz. The branch was deleted and its (correct) diff re-landed with real history in metadatastician/ziz#2. Estate-wide footprint: 8 arena/* refs across 420 repos — 5 merged-and-identical (prunable), 3 backing open PRs.
Summary
lib/rules/git_state.exruns GS001–GS008. None of them detects the invariant that failed onmetadatastician/zizon 2026-09-28: a ref with no merge base against the default branch.Measured instance —
arena/01a0d3a4-rsr-template-repo@121ebaeeb9b2543c119babae3fdc086bfd99f5e8:--allow-unrelated-histories, which would have imported 478 files already present and reverted a dependabot bump.main's. It was a session initialisation commit, not work.Why the existing rules miss it
ahead > 0 and behind > 0. On unrelated historiesgit rev-list --left-right --count HEAD...@{u}returns two arbitrary totals, so the one case where "diverged" is factually wrong is the one case the rule cannot name.mainref with equal weight. It renders a 1-commit abandoned orphan and a 100-commit branch with an open PR identically. Measured estate-wide:MetaManifold-WebUIhad 4 branches that were content-identical tomain(compare→files: 0) sitting alongside a branch backing an open PR. GS007 called all five the same thing.Proposed rule
GS009 — a ref has no merge base with the default branch.
Severity: high where the ref also has no PR; advisory where a PR exists (an open PR on an unrelated branch is a workflow problem, not a defect in the change).
Note the test must be merge base, not ancestry. Squash-merging is the estate default, so a fully merged side branch is routinely not an ancestor of
mainonce the squashed commit replaces it.git merge-base --is-ancestorwould fire on every squash-merged branch;git merge-basereturning empty fires on none of them and exactly one of the orphans.Suggested refinement to GS007
Classify by PR state before reporting, so the cleanup instruction is actionable:
Acceptance criteria
AC1 — GS009 exists, fires on a synthetic unrelated-history branch, and does not fire on a squash-merged branch.
AC2 — GS007 separates "merged and content-identical → prunable" from "orphaned → escalate".
AC3 — No muting. Specifically: no exemption keyed on a branch-name prefix. A rule that ignored refs starting
arena/would have hidden this one entirely, and the estate's ownarena/*refs are the observed instance of the class.AC4 — The rule reports the measured fact (empty merge base), not an interpretation ("diverged"), so the finding stays true when the history shape is unexpected.
Provenance
Found while triaging an orphan branch on
metadatastician/ziz. The branch was deleted and its (correct) diff re-landed with real history inmetadatastician/ziz#2. Estate-wide footprint: 8arena/*refs across 420 repos — 5 merged-and-identical (prunable), 3 backing open PRs.