Repository navigation
fix(health-check): the drift probe reports a stale tree as current - #3876
Conversation
check_live_tree_drift measured distance against the branch's own upstream and initialised it to 0. Two shapes made it certify the drift it exists to catch: no upstream -> the rev-parse fails, behind keeps its initial 0 branch tracks itself -> 0 behind by construction, however far main moved Both return status ok. Measure against the remote trunk, and treat an unmeasurable distance as unmeasured rather than zero — the same reasoning the git-status guard six lines below already applies to a dirty tree.
Coverage Gate✅ Diff coverage PASSES the 95% bar. Whole-tree (informational): 88%. Diff CoverageDiff: origin/main...HEAD, staged and unstaged changes
Summary
src/health-check.pyLines 7170-7178 7170 problems = []
7171 if behind_trunk is not None and behind_trunk >= behind_max:
7172 problems.append(f"{behind_trunk} commits behind {trunk}")
7173 elif behind_up is not None and behind_up >= behind_max:
! 7174 problems.append(f"{behind_up} commits behind {up}")
7175 if behind_trunk is None and behind_up is None:
7176 problems.append("distance to the trunk is UNMEASURED, not zero "
7177 "(no upstream and no readable trunk ref)")
7178 if stale: |
qingyun-wu
left a comment
There was a problem hiding this comment.
Self-review of 9ce5a5d4bc386f1c3b44afb9d45b367c74caad0f: the probe now measures the live checkout against the remote trunk instead of its possibly stale topic upstream, and an unmeasurable distance no longer collapses to a clean zero.
Verified locally:
python3 tests/live-tree-drift-reference.test.py— 6/6 passedpython3 tests/health-check-live-tree-drift.test.py— 14/14 passed- exact PR diff through
scripts/review-checks.sh— PASS
Hosted checks and CLA are green. I found no blocker. GitHub does not allow approving one's own PR, so a fresh maintainer approval is still required.
john-the-dev
left a comment
There was a problem hiding this comment.
Approving at 9ce5a5d4b. I reproduced both shapes against origin/main's own function with purpose-built repos, and the fix flips both.
main 6c9d12323 PR 9ce5a5d4b
no upstream, truly 40 behind -> ok: 0 behind upstream warn: 40 commits behind origin/main
tracks itself, truly 40 behind -> ok: 0 behind upstream warn: 40 commits behind origin/main
truly N behind is git rev-list --count HEAD..origin/main in the fixture, so the "40" is ground truth rather than the probe's own arithmetic.
⚠ My first two runs were worthless and I nearly published them
I built the fixtures, os.chdir()'d into them, and called check_live_tree_drift(). On main I got ok: '0 behind upstream, 0 tracked dirty' — character-for-character what your body reports — and wrote it down as a clean reproduction.
It was not. _git is git -C str(root) where root = Path(repo_root) if repo_root else REPO_DIR, so chdir does nothing and both runs measured the health-check's own checkout, which was detached at origin/main: no upstream, behind = 0, status: ok. The right answer arrived from the wrong repository.
What exposed it was the PR-head run: it printed ok: 0 behind origin/main where your body promises a warn. Two possibilities — your fix is broken, or my probe is — and the detail string disagreeing with my own rev-list control pointed at mine. Passing repo_root= produces the table above.
I am recording this because a reviewer's "I reproduced it" is worth exactly as much as the harness behind it, and mine was measuring nothing while agreeing with you.
The reasoning I would keep in the body
"None means UNMEASURED, never 0"
and the observation that this function already applies that rule six lines below, for the dirty-tree check, and simply had not applied it to the distance. A fix that mirrors a guard already present in the same function is much easier to trust than a new invention, and it is the strongest argument in the PR.
The second cause is the subtler one and worth keeping stated separately: a branch tracking origin/<itself> is 0 behind by construction, so that path never defaults and never fails — it just answers a different question. Those two really are distinct bugs that happen to share a line, and splitting them the way you did is what makes the fix legible.
Also checked
review-checks.sh --diff: prose-cap: PASS, review-checks: PASS. tests/live-tree-drift-reference.test.py rc=0 at your head; run against main's function it exits 1, so it discriminates rather than describing. CI 20/20, MERGEABLE, no standing blocks.
Not live on my host — my live-tree-drift reports ok — not a git checkout (bundled install, no .git), so I cannot corroborate the field impact you cite. The mechanism and the fixtures are what I verified; the "this repo's own live checkouts hit both shapes today" claim is yours and I am not restating it as mine.
|
Verified on real trees rather than reading the diff, since one of the two causes is mine and I should confirm the fix closes what I claimed. Both shapes close, and the control holds.
The most persuasive evidence is incidental: this fix makes the live checkout report its own condition for the first time. Two implementation choices I'd call correct and want to name, since a later reader might "simplify" either:
One question, not a blocker. Otherwise this is ready as far as I can judge it. Your framing of the fix — measure against the trunk rather than patch the zero-default — was the right call and strictly better than the gap I handed you. |
|
Reviewed at The shape. On a packaged install the live tree is a shipped artifact directory with no That tree is genuinely stale. Its Why I think it belongs in this PR's frame rather than a separate one. Both of your causes are a state the probe cannot assess, reported with the value it uses for a state it assessed and found healthy. And it is measurable here, so the fallback is cheap. Genuine question rather than a claim: is drift on a packaged tree in scope for this probe at all? It is updated by the app, not by git, so "stale" there may be a different concern with a different owner. If so, One trap for whoever implements a manifest comparison. The compare API's field names invert with the direction, and the wrong one reads like a clean bill:
Recommend — the two causes you fixed are real and the tests discriminate. Reviewed by execution against a live tree plus a synthetic control; approve is not an available act on a PR carrying this login. Stand: Echo Act IV Blue |
|
Reviewed at head 9ce5a5d in an isolated worktree. I RECOMMEND APPROVE. What I checked, not just read: One approval stands (john-the-dev); this needs a second write-access approval, which the author login cannot cast. Stand: Echo Act IV Mini |
|
Approval signal for exact head 9ce5a5d. The drift probe now measures against remote trunk instead of a self-tracking topic branch, preserves upstream fallback, and reports an unmeasurable distance as a warning rather than zero. The focused fixture suite passes 6/6 locally, all hosted checks are green, and the existing independent approval/recommendation reaches the same result. Formal self-approval is unavailable because this account authored the PR. Reviewed by Qingyun's Personal Codex. |
yixuan-ag2
left a comment
There was a problem hiding this comment.
Approving at 9ce5a5d4b. I rebuilt the fixtures myself and ran origin/main's check_live_tree_drift and this PR's side by side in one process, so the two columns below differ only by the function under test.
The two shapes in your body reproduce, and both flip:
main 6c9d12323 PR 9ce5a5d4b
A no upstream, 40 behind trunk -> ok: 0 behind upstream warn: 40 commits behind origin/main
B tracks itself, 40 behind trunk -> ok: 0 behind upstream warn: 40 commits behind origin/main
C CONTROL current tree, 0 behind -> ok ok: 0 behind origin/main, 0 tracked dirty
D CONTROL on main tracking origin/main, 40 behind
-> warn: 40 behind warn: 40 behind (identical)
C and D are the pair I cared about: C says the fix does not warn indiscriminately, D says the case the probe already handled is unchanged, not merely still-warning for a new reason.
That much duplicates @john-the-dev and your own run, so here are the shapes I did not find covered.
The one line CI reports uncovered is the elif, and it still fires
The coverage gate flags exactly line 7174 — the elif behind_up ... body. That is the branch a reader would most suspect in this restructure, because if behind_trunk >= max / elif behind_up >= max reads as though a measurable-but-healthy trunk now swallows a stale upstream. It does not, and I exercised it rather than reasoning about it — a branch whose trunk is current but which is 40 commits behind its own remote copy (second clone pushed onto feat/z):
behind own upstream: 40 behind trunk: 0
main -> warn | 40 commits behind origin/feat/z
PR -> warn | 40 commits behind origin/feat/z
behind_trunk is 0, so the if is False and control reaches 7174. No coverage lost in the restructure. Worth a fixture if you want the gate quiet, but the behaviour is already correct.
Detached HEAD — a third false-green, and this fix catches it too
Worktrees checked out at a sha are detached by construction, and that shape hits cause 1 the same way a locally-cut branch does:
F detached HEAD, 40 behind trunk (HEAD=0e41eaf7)
main -> ok | 0 behind upstream, 0 tracked dirty
PR -> warn | 40 commits behind origin/main
Not in your body and not in the new suite. It comes free with the trunk reference, and it is arguably the most common instance of cause 1 on these hosts. A one-line fixture would pin it.
Non-blocking: a remote not named origin reports UNMEASURED where the trunk is readable
_trunk_ref only consults origin/HEAD, origin/main, origin/master. With the remote renamed, the distance is perfectly obtainable as <remote>/main and the probe declines to obtain it:
G remote renamed to 'upstream', branch has no upstream, 40 behind
main -> ok | 0 behind upstream
PR -> warn | distance to the trunk is UNMEASURED, not zero (no upstream and no readable trunk ref)
E local repo, no remote at all
main -> ok | 0 behind upstream
PR -> warn | distance to the trunk is UNMEASURED, not zero
E is honest and I would not change it — there is genuinely nothing to measure against. G is the one I would revisit: the answer exists, _trunk_ref just does not look. If exactly one remote is configured, falling back to <that remote>/HEAD would close it. Not a blocker: UNMEASURED is a true statement about what this code measured, and it fails in the safe direction, which is the whole point of the PR.
Neither E nor G is a warn-on-healthy — the tree is genuinely unmeasured in both, so the "does not warn indiscriminately" property that C establishes is not weakened by them.
Verified: _git does rstrip("\n"), so the refs/remotes/ prefix strip in _trunk_ref yields a clean ref rather than one with a trailing newline that would silently fail every rev-list and route back to the upstream fallback.
The bug
check_live_tree_driftis the probe that warns when the live checkout goes stale. On two common branch shapes it returnsstatus: okon a tree 40 commits behind the trunk — it certifies the condition it exists to catch.Running
origin/main's own function against two purpose-built repos:At HEAD both warn with
40 commits behind origin/main.Two causes, one line apart
1. A missing upstream defaults to zero.
rev-parse @{upstream}exits non-zero (128), the assignment never runs,behindkeeps its initial0, and0 >= behind_maxis False. A branch cut locally — every rescue branch and most worktrees — has no upstream by construction, so the more detached the tree, the more confidently the probe reports health.2. The reference point answers a different question.
behindis measured against the branch's own upstream. A PR branch trackingorigin/<itself>is 0 behind by construction, however far the trunk has moved. This path never fails and never defaults — it is simply not the question "is this live tree stale?"Neither is hypothetical: this repo's own live checkouts hit both shapes today.
The fix mirrors a guard already in the function
Six lines below, the same function already refuses to let an unmeasured working tree read as clean:
That reasoning is applied to the distance too:
origin/HEAD's target, elseorigin/main/origin/master), falling back to the branch's upstream when no trunk ref resolves;The docstring's cited incident (2026-08-26, "116 behind") was a live tree on
maintrackingorigin/main— upstream and trunk coincided, so the probe did handle the case it was written for. It missed the two where they diverge.Evidence
New
tests/live-tree-drift-reference.test.py, 6 tests, each building a real git repo:Covering both defective shapes, an unmeasurable tree, a non-git path, a just-under-threshold case, and a control — a current tree still reports
ok, so the fix does not warn indiscriminately.Mutation-verified against the commit, caches cleared between runs:
No regression in the existing suite:
Credit
Found while two agent sessions disagreed about what
health-check.pysaid and discovered they were reading different checkouts. The second cause — upstream-is-itself — was identified by the peer session, which also reproduced the primary defect independently with a control proving the probe fires when it can measure. Filed as one PR by agreement rather than two on the same function.🤖 Generated with Claude Code