From 91509cb9b2ae833b4af79ef1020082b5773fc369 Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Sat, 26 Sep 2026 21:14:20 +1000 Subject: [PATCH] docs: name the conflict watchpr will never report --- AGENTS.md | 23 ++++++++++++++++------- README.md | 16 ++++++++++++---- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ff6b99d..ab4c09e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -224,18 +224,27 @@ wrapped per stage (login / read denied / write denied) via `ErrVaultDenied`. read, so a PR that is already conflicted or already CI-failing when watching starts is polled on rather than reported. That suppression is printed with the baseline, since a silent watcher and a watcher with nothing to say look alike. + The baseline goes to stderr in both modes — a JSON record under `--json` — + because automation is what most needs it and stdout carries the result. - Mergeability is the one `watchpr` rule needing a run of observations (`prWatch`). Gitea 1.26 sends `mergeable` as a plain bool — always present, never null — and sends `false` both for a real conflict and while it recomputes the merge base after a push, which is exactly when an agent starts a watch. So `false` counts only once this watch has seen a merge computation start (the PR - was mergeable, or `head.sha`/`base.sha` moved), and then only across two - consecutive polls of an unchanged head and base; a mergeable poll, an unknown - one or a failed poll all break the run. `base.sha` is the base branch tip as of - the response, not the merge base at PR creation, so it moves when main does. - `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 this rule — a push never alerts. + was mergeable, or `head.sha`/`base.sha` moved), and then only once the run of + `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 + 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). - 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 diff --git a/README.md b/README.md index f3fef7b..3ef3cac 100644 --- a/README.md +++ b/README.md @@ -88,13 +88,21 @@ gets a **new comment from someone other than the agent**, its **CI fails** transitions — CI `pending`→`success`, the agent's own comments and pushes — are ignored, and so is any condition the PR was already in at the baseline: a PR that is already conflicted or already failing when watching starts keeps being -watched, and the baseline line says which conditions it is staying silent about. +watched, and the baseline line — written to stderr, as a JSON record under +`--json` — says which conditions it is staying silent about. Gitea reports `mergeable: false` both for a real conflict and while it recomputes the merge base after a push, so a conflict is only reported once the -watch has seen a merge computation start — the PR was mergeable, or its head or -base commit moved — and then only across two consecutive polls of an unchanged -head and base. +watch has seen a merge computation start (the PR was mergeable, or its head or +base commit moved) **and** the non-mergeable polls have then run unbroken for +two minutes. The debounce is a duration, not a poll count, because what it has +to outlast is Gitea's recompute and `--interval` ranges from seconds to hours. + +One case is therefore never reported: a conflict introduced by the push +immediately before the watch started, on a PR whose head and base never move +again. Nothing in Gitea's payload separates that from a merge check still in +flight, so watchpr stays silent about it for as long as it runs — the baseline +line is how you see it. ```bash # Watch until something meaningful happens (default interval 60s)