Refuse a branch inventory that cannot be shown to cover every branch - #1398
Conversation
…very branch Complete history is not complete branch coverage, and the second condition survives the first fix. `git clone --depth 1` implies `--single-branch`, which pins remote.origin.fetch to the one cloned branch. `git fetch --unshallow` converts the history — so `--is-shallow-repository` reads false and shallowCloneRefusal is satisfied — but it does not widen the refspec, and an ordinary `git fetch origin` respects the narrow one. Measured in a fixture with `main` and `feature`: after unshallowing, `git ls-remote --heads origin` listed both while refs/remotes/origin held only origin/main, and the sweep exited 0 reporting `"branches": []`. An empty inventory is not a safe failure here — it reads as "nothing to clean up", and where origin/main itself is missing every rev-list fails into 0/0, making every branch a deletion candidate with no unique patch content. Fixed in both directions. The sweep's own fetch now passes an explicit +refs/heads/*:refs/remotes/origin/*, which repairs coverage without rewriting the operator's config, so an ordinary run just works. branchCoverageRefusal then refuses when neither the configured refspec nor a completed wildcard fetch establishes coverage — --no-fetch, offline, or a failed fetch. Its remedy is `git remote set-branches origin '*'`, deliberately not --unshallow, which fixes history and does nothing here. Negative refspecs are handled as their own case. Measured on git 2.43.0 with `+refs/heads/*:refs/remotes/origin/*` plus `^refs/heads/feature`, an ordinary fetch honoured the exclusion and left origin/feature absent, while an explicit command-line wildcard overrode it and restored the ref. So a `^` spec means the config alone cannot establish coverage, but a completed fetch still can. Proof, narrow-refspec fixture, before -> after: exit 0 with `"branches": []` -> exit 1 with the set-branches remedy; with fetch allowed, exit 0 and `feature` appears. Non-regression on this full clone: `sweep:branch-ledger -- --no-fetch` still reports 91 branches and exits 0. Follow-up to #1392 (ledger #109), which merged before this was found. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012YRCXgX4AWZ579bKN6sk6b
This pull request has been ignored for the connected project Preview Branches by Supabase. |
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in:8 minutes Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
Comment |
BigSimmo
commented
Jul 30, 2026
@cursoragent Inspect the PR and repository state, identify the root cause of every failing CI check, unresolved review comment, merge conflict, and related defect. Apply the smallest robust fix using existing project patterns and best practices, without unrelated changes. Run Bugbot and all relevant lint, type-check, test, build, and CI-equivalent commands, fix any issues found, then resolve only the review comments that are fully addressed. Summarise the root causes, changes made, validation results, and any remaining blockers. |
PR #1398 is unblocked. Hosted PR required is green, both Codex threads are resolved, and Root causes
Changes made
Validation
Remaining
|
BigSimmo
commented
Jul 30, 2026
@codex review PR |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:d0345bb187
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
…origin The coverage predicate accepted a refspec on its source alone, which was wrong in the dangerous direction. Git's `<dst>` decides which local ref is updated, and this sweep enumerates refs/remotes/origin and nothing else, so a refspec that fetches every branch somewhere else satisfied the guard while leaving the inventory empty. Measured with `+refs/heads/*:refs/remotes/upstream/*`: refs/remotes/upstream held upstream/main and upstream/feature, refs/remotes/origin was empty, and the sweep exited 0 reporting `"branches": []` — the same "empty inventory presented as trustworthy" defect the guard exists to prevent, reached by a third route. A mirror's `+refs/*:refs/*` behaves the same way, fetching into refs/heads/*. Coverage from config now requires both halves: a source of refs/heads/* or refs/*, AND a destination of refs/remotes/origin/*. The sweep's own fetch already passes an explicit destination, so an ordinary run still repairs its own coverage and only --no-fetch/offline refuses. One of my own test cases asserted that a mirror's `+refs/*:refs/*` counted as covered. It is replaced, with the measurement recorded in the test body. Proof, custom-destination fixture: --no-fetch exits 1 with the refusal; with fetch allowed exits 0 and lists `feature`. 39 cases pass in tests/repo-hygiene.test.ts. Reported by Codex on PR #1398. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012YRCXgX4AWZ579bKN6sk6b
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:de59418676
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Uh oh!
There was an error while loading. Please reload this page.
…analysis-2vcd8a Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Git wildcard substitution nests the matched suffix, so `+refs/*:refs/remotes/origin/*` writes `refs/remotes/origin/heads/<branch>` rather than `origin/<branch>`. Accept only `refs/heads/*` → `refs/remotes/origin/*`. Addresses Codex P2 on PR #1398. Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Uh oh!
There was an error while loading. Please reload this page.


Summary
Follow-up to #1392 (ledger
#109), found in review after that PR merged. Complete history is not complete branch coverage, and the second condition survives the first fix.git clone --depth 1implies--single-branch, which pinsremote.origin.fetchto the one cloned branch.git fetch --unshallowconverts the history — sogit rev-parse --is-shallow-repositoryreadsfalseandshallowCloneRefusalis satisfied — but it does not widen the refspec, and an ordinarygit fetch originrespects the narrow one.Measured in a fixture with
mainandfeature, after unshallowing:An empty inventory is not a safe failure here — it reads as "nothing to clean up". And where
origin/mainitself is the branch not fetched, everyrev-listfails into0/0, making every branch a deletion candidate with no unique patch content. That is the same hazard as#109, reached by a different route and past the guard written for it.Fixed in both directions. The sweep's own fetch now passes an explicit
+refs/heads/*:refs/remotes/origin/*, which populates every remote-tracking ref without rewriting the operator's config, so an ordinary run repairs its own coverage.branchCoverageRefusalthen refuses when neither the configured refspec nor a completed wildcard fetch establishes coverage —--no-fetch, offline, or a failed fetch. Its remedy isgit remote set-branches origin '*', deliberately not--unshallow, which fixes history and does nothing here; sending the operator there means they re-run and get the same empty inventory, now doubly convinced it is right.Negative refspecs are their own case, and the behaviour is asymmetric. Measured on git 2.43.0 with
+refs/heads/*:refs/remotes/origin/*plus^refs/heads/feature:origin/featuregit fetch --prune origin(configured refspec)git fetch --prune origin '+refs/heads/*:refs/remotes/origin/*'So a
^spec means the config alone cannot establish coverage, while a completed fetch still can. Reading the wildcard and ignoring the exclusion would report a partial inventory as complete.Verification
npm run verify:cheap—Test Files 432 passed (432),Tests 4466 passed | 4 skipped (4470).testis last in that&&chain, so the 24 static gates,lintandtypecheckall passed ahead of it.mainexits 0 with"branches": []; with this change--no-fetchexits 1 carrying theset-branchesremedy, and with fetch allowed exits 0 withfeaturepresent in the inventory.--no-fetchrefuses withbranches: nulland exit 1; fetch allowed exits 0 and listsfeature.npm run sweep:branch-ledger -- --no-fetchstill prints the normal inventory and exits 0 (remote.origin.fetchis+refs/heads/*:refs/remotes/origin/*here, so no refusal).tests/repo-hygiene.test.ts(38 in the file): wildcard / mirror / unprefixed accepted; single-branch, several-narrow, absent config rejected; negative refspec rejected from config but permitted after a completed fetch; the refusal'sFix:line must nameset-branchesand must not name--unshallow.npm run check:production-readinessnot run: no clinical workflow, privacy, environment, Supabase, source-governance or deployment change.One of my own assertions was wrong on the first pass — it required the refusal body not to mention
--unshallowat all, when the body mentions it deliberately, as the thing that does not fix a refspec. Narrowed to theFix:line, which is what actually misdirects an operator.Risk and rollout
Clinical Governance Preflight
Not applicable to this diff.
classifyPullRequestFilesreportsclinicalRisk: false,operationalRisk: false,ragRanking: false,ui: falsefor the changed paths —scripts/sweep-branch-ledger.mjs,tests/repo-hygiene.test.ts,docs/branch-cleanup-guide.md,docs/outstanding-issues.md.scripts/**is clinical-risk only when it matches ingest/reindex/migration/governance/production/drift/supabase, and a branch-hygiene reporter matches none of those. No ingestion, answer generation, search/ranking, source rendering, document access, privacy or clinical output behaviour changed.Notes
RAG impact: no retrieval behaviour change — nothing under
src/lib/rag/**, clinical-search, retrieval-selection, released-search-order, ranking-config, answer-ranking, the eval harness, the golden fixture, or the retrieval RPCs is touched.The branch was restarted from the post-merge
mainrather than stacked on the merged history, and the remote branch had been auto-deleted on merge, so this is a new branch and a new PR rather than a reopen. Pruning stale refs also showed that two of the five#108deletion candidates —claude/clinical-kb-pwa-review-asi3wbandclaude/dazzling-blackwell-f348d0— are already gone from the remote;#108still lists them and needs re-checking against the live remote before its ledger rows are appended.docs/branch-cleanup-guide.mdgains the refspec check alongside the shallow one, because the rawgitcommands the guide documents have no guard even now that the script does.🤖 Generated with Claude Code
https://claude.ai/code/session_012YRCXgX4AWZ579bKN6sk6b
Generated by Claude Code