Files
hq/04-ISSUES/347-a-pull-request-opened-after-its-push-is-never-checked/00-report.md
T
jochen fc449617be
mesh/merge-gate pass: the change touches no module of the mesh's graph
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
Issue 347: a base requiring only mesh/repo-check counts, and the delivery's owner says it
The confirmation review: hq requires only mesh/repo-check, so a watcher
that looked for mesh/merge-gate alone would never have said this
repository's own unchecked pull request. And the delivery domain's own
condition, a delivery held past its bound, says it rather than a provider
standing.
2026-10-09 13:16:27 +02:00

4.8 KiB

status, opened, located-in, fixed-by, amended-design
status opened located-in fixed-by amended-design
located 2026-10-09
mesh-catalog (modules/gitea
the announcer in index.ts and the client; modules/mesh-delivery
saying it)
mesh-controller (its words)

347. A pull request opened after its push is never checked, and nothing says so

Symptom

On 2026-10-09 a pull request on the SDK's repository (opened at 10:11 UTC, its branch pushed at 10:03) had no mesh/merge-gate and no mesh/repo-check status twenty minutes later, while four pull requests opened in the same minutes on other repositories were checked within three. mesh-delivery.checks answered "no status" for it, and no condition, log line or status said why. The pull request could not merge, because its base requires both statuses, and nothing in the mesh knew it existed.

The same day a call of the forge module's gitea_get_pull_request with a misnamed argument asked the forge for pulls/NaN.

Why it is an issue

  • Work is silently never checked. The forge's announcer is the only way the mesh learns of a pull request's head (to-be 45 §9). A pull request it misses waits for ever, with no status and no condition: failure is not loud, the worst kind.
  • The cause, measured by the review of ADR 0272. Each pass of the announcer (pollPulls, modules/gitea index.ts) reads only the repositories whose update time moved since its last pass, less a minute's margin (movedSince, issue 250). A push moves a repository's update time; opening, reopening or retargeting a pull request does not. So a pull request opened more than about a minute after its branch was pushed is in a repository the announcer skips, on every pass, until something else is pushed there.
  • A number that is not one reaches the forge. A tool's numeric argument read under the wrong name is NaN, and the client asked the forge about it rather than refusing.

Acceptance test (the operator's view)

A pull request I open on a protected branch, whenever I open it after pushing its branch, has a merge check within a minute. If one ever has none for ten minutes — on a branch that requires mesh/merge-gate or only mesh/repo-check — the mesh tells me which pull request, as a condition. A tool asked with a pull request number that is not one refuses and says why.

The fix

In the forge module (mesh-catalog, modules/gitea):

  1. Each pass also looks at every repository holding a pull request updated since the floor, found by the forge's own search of open pull requests by their own update time; and every ten minutes, or when the search fails, it looks at every repository (a sweep).
  2. Every five minutes the delivery's owner (mesh-delivery) asks the forge itself for every open pull request (gitea_open_pulls), never what the announcer remembers. A head on a base that requires the merge check — mesh/merge-gate or mesh/repo-check, since this repository requires only the second — with neither on it for ten minutes since the owner first saw it so (its own time: the forge's update time moves on every comment) is a delivery held past its bound: a stalled line in state unannounced, which the controller raises as delivery.<id>.stalled like any other, with words that say to push a new commit. The delivery domain's own word, chosen over a provider standing and over a health check of the forge: that check would need a tools list (turning the forge's tools into seat verbs) and a check of another kind beside it, and would hold the forge's own deliveries at the first-node gate.
  3. The client refuses a pull request or issue number that is not a whole number above zero before it asks.

How it is checked

  • test/announcing.test.ts (modules/gitea): a pull request opened two minutes after its push, with the last pass thirty seconds after that, is in a repository the pass looks at; without its own update time nothing is looked at (the fault, held as a case); a sweep and the first pass look at every repository. Every open pull request is read with its base's required statuses and its head's. pulls/NaN, 0 and 1.5 are refused and the forge is never asked. Each fails on the commit before the fix.
  • unchecked_test.go (modules/mesh-delivery): a head on a base requiring only mesh/repo-check, with no check, is a stalled unannounced line after ten minutes, counted from when the owner first saw it; one with a check, on a base requiring none, announced, closed, or behind a forge that cannot be asked is not said or not changed.
  • TestAnUnannouncedPullRequestSaysToPushANewCommit (mesh-controller): the condition's words say to push a new commit, never to stop or release a delivery that does not exist.
  • Live, after delivery: the next pull request opened later than a minute after its push has its statuses within a minute; mesh-controller.conditions shows none for it.