watchpr: start the conflict window at the arm
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful

The window ran from the first non-mergeable poll even while the rule was
disarmed, so the poll that armed it confirmed a run it played no part in.
Measure from the later of the run's start and the arm; re-arming stays a
no-op so a base moving under every poll still confirms.
This commit is contained in:
2026-09-26 22:14:25 +10:00
parent 7ffa123e3d
commit 5928233a97
6 changed files with 218 additions and 41 deletions
+17 -9
View File
@@ -235,16 +235,24 @@ wrapped per stage (login / read denied / write denied) via `ErrVaultDenied`.
`false`s has spanned `conflictWindow` (2m); a mergeable poll, an unknown one or
a failed poll all break the run. The debounce is wall-clock, measured from the
tick that fired each poll, because what it outlasts is the recompute and
`--interval` spans seconds to hours. Arming and the run are independent:
movement arms and nothing else, since `base.sha` is the base branch tip as of
the response, so it moves for every open PR whenever main does and a run reset
there could never complete on a busy repo. `Mergeability` stays tri-state for
what the bool cannot carry: an absent/null flag from some other Gitea, and a
state no successful poll ever filled in. Head and base SHAs are read only to
`--interval` spans seconds to hours. The window starts at the later of the
run's first observation and the arm, which is what keeps arming and the run
independent: movement arms without resetting the run — `base.sha` is the base
branch tip as of the response, so it moves for every open PR whenever main does
and a reset there could never complete on a busy repo — while an arming poll
still cannot confirm a run it played no part in. Re-arming an already-armed
watch is a no-op, so a base moving under every poll advances the window once
and never again. Both times are pointers because the zero `time.Time` is a
legal clock value and cannot also mean "no run". `Mergeability` stays tri-state
for what the bool cannot carry: an absent/null flag from some other Gitea, and
a state no successful poll ever filled in. Head and base SHAs are read only to
arm — a push never alerts. The residue: a conflict landed by the push just
before the watch began, on a head and base that never move again, is never
reported; Gitea's payload has no field separating it from a check in flight
(`merge_base` is the true merge base and does not move on recheck).
before the watch began is unreported while the commits stay put, since Gitea's
payload has no field separating it from a check in flight (`merge_base` is the
true merge base and does not move on recheck). The commits rarely stay put —
a merge to the base branch moves `base.sha` under every open PR — but that
move only arms; the two minutes it then has to outlast are the recompute the
move started, not the falses before it.
- CI "combined status" comes from `/commits/{sha}/status`; an empty head SHA
yields an empty state without an API call.
- Gitea backs every PR with an issue of the same number and serves comments from