fix(repo): pick the pre-push base by history, never a local main - #1192
Merged
Merged
Conversation
… a local main A clone whose remote is named upstream has no origin/main, so the hook fell back to the local main branch. A local main that lags the remote made every remote commit since look like this branch's change, and a docs-only push ran the gates of every stack those commits touched. The hook now uses upstream/main, then origin/main, and stops with a fix-it message when neither exists.
Contributor
Preview: disabled
|
Among every <remote>/main, the hook now compares against the one this branch has the fewest commits on top of, instead of looking for upstream/main or origin/main by name. A remote called sc-remote works, and a stale second remote no longer wins by its name.
…main's latest commit run_gates.py's append-only test check diffs its base against the working tree, so a test that main changed after the branch was cut read as this branch's edit and went red. The hook now passes git merge-base <remote>/main HEAD.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TLDR
The pre-push hook (
.githooks/pre-push) runs before everygit push. It works out which stacks the branch changed and runs each one's full checks: ruff, pyright, and the whole test suite with coverage. It finds "what changed" by comparing the branch against a base, the main branch it was cut from.The rule: the base must be the remote's current main. Otherwise commits already on main get counted as this branch's changes.
Where it broke:
origin/main. A clone whose remote is namedupstreamhas none, so the hook fell back to the localmainbranch.mainlags the remote. On 2026-10-01 it was 8 commits behind, and those commits touched the engine, aigateway and aigateway-ui. A docs-only push then ran all three stacks' checks: the engine's ~4,650-test suite with coverage, aigateway's suite, and an npm build.The change: the hook picks the base by history, not by remote name. Among every
<remote>/main, it uses the one the branch has the fewest commits on top of, which is the main it was cut from. Example: a docs-only branch is 1 commit ahead ofsc-remote/mainbut 41 ahead of a fork's staleorigin/main, sosc-remote/mainwins and no stack is checked. The checks then get the commit the branch was cut from, not that remote main's latest commit: the append-only test check compares its base with your working copy, so a test main changed after you branched would otherwise read as your edit and go red. It stops with a fix-it message when there is no remote main, and never uses a local branch. Blast radius: one shell hook plus its new test; no app code changes, and a clone with a single up-to-dateoriginbehaves exactly as before.Closes OME-1443.
Before / After
flowchart TB subgraph TODAY["TODAY — a docs-only push runs three stacks' full test suites"] direction LR a1["👤 push a branch that only edits docs"] -->|no origin/main, falls back| a2["⚠️ compares against local main<br/>8 commits behind upstream"] a2 -->|upstream's commits look like this branch's| a3["engine + aigateway + aigateway-ui<br/>checks run, minutes per push"] end subgraph AFTER["AFTER — only stacks the branch really changed are checked"] direction LR b1["👤 push a branch that only edits docs"] -->|any remote name| b2["compares against the remote main<br/>the branch was cut from"] b2 -->|no stack changed| b3["✅ no checks run<br/>this branch's push took 4 s"] end TODAY ~~~ AFTER classDef bad fill:#7f1d2b,stroke:#e5484d,color:#ffe8ea classDef good fill:#14532d,stroke:#30a46c,color:#dcfce7 classDef stage fill:#1e3a8a,stroke:#4a7fd4,color:#dbeafe classDef plain fill:#374151,stroke:#9ca3af,color:#f3f4f6 class a1,b1,b2 stage class a2 bad class a3 plain class b3 goodFor the developer, a push that touches no stack goes from minutes to seconds. A push that really touches the engine still runs the engine's full suite, as designed.
Don't regress
--base upstream/main(pinned by a test).originkeeps working unchanged (pinned by a test).Architecture / Design
Skipped: the mechanism is one loop in the hook. It carries one "because": the base is chosen by counting commits, not by remote name, because names differ between clones (
origin,upstream,sc-remote) and a fork can have two remotes where one is stale. Counting the commits between each remote main and the branch picks the main the branch was really cut from, whatever it's called.Known limitations of this design
repostack's own checks (the scripts under.claude/scripts/), becauserepoisn't in its stack list. That was already true before this change, and it's out of scope here.Review order
.githooks/pre-push: the whole change. Check that onlyrefs/remotes/*/mainare considered, so a local branch can never be picked; that the fewest commits ahead wins; that "no remote main" exits 1 before any check runs; and that the checks getgit merge-base <remote>/main HEAD, not the remote main itself..claude/scripts/tests/test_pre_push.py: runs the real hook in throwaway git repos with a stubuvthat records which stacks would be checked. Eight cases: remotes namedupstream,originandsc-remote; a stale second remote namedupstream, and one namedoriginthat sorts first (only counting commits gets that one right; a hook that took the first remote main failed it); a real engine change, checked against the branch point; main adding an engine test after the cut; no remote at all. Theupstreamdocs-only test failed on the old hook, withscreamingface-engine main..claude/sdlc.local.mdregisters the test under therepostack's checks; the ledger and thedocs/tasksmirror.🤖 Generated with Claude Code