From 2ba0d683065971d4aa7d315d0c8ea9a18cfaef82 Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 9 Oct 2026 12:55:26 +0200 Subject: [PATCH 1/2] Issue 347: a pull request opened after its push is never checked, and nothing says so The forge's announcer reads only repositories whose update time moved; a pull request opened after its push does not move it. Located in the forge module, with the fix (mesh-catalog) and how it is checked. --- .../00-report.md | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 04-ISSUES/347-a-pull-request-opened-after-its-push-is-never-checked/00-report.md diff --git a/04-ISSUES/347-a-pull-request-opened-after-its-push-is-never-checked/00-report.md b/04-ISSUES/347-a-pull-request-opened-after-its-push-is-never-checked/00-report.md new file mode 100644 index 00000000..7634f255 --- /dev/null +++ b/04-ISSUES/347-a-pull-request-opened-after-its-push-is-never-checked/00-report.md @@ -0,0 +1,65 @@ +--- +status: located +opened: 2026-10-09 +located-in: [mesh-catalog (modules/gitea, the announcer in index.ts and the client)] +fixed-by: +amended-design: +--- + +# 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, 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 forge itself is asked for every open pull request. One whose base requires + `mesh/merge-gate` and whose head has had no such status for ten minutes is said as the forge failing *the + merge check of that pull request*, a provider standing ([ADR 0224](../../02-DECISIONS/0224-a-provider-that-keeps-failing-a-consumer-is-a-problem-the-controller-reports.md)) + the controller keeps as a condition and says to the operator. It is said again every pass while it lasts and + recovered when a check arrives or the pull request closes. The watcher asks the forge, not the announcer's + record, so a fault in the announcer cannot hide itself. +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. An open pull request on a + protected base with no merge check for ten minutes is said; one with a check, on a base that requires none, or + five minutes old is not. `pulls/NaN`, `0` and `1.5` are refused and the forge is never asked. All three fail on + the commit before the fix. +- 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. From fc449617be07deef2035cc876f7461843cacacb7 Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 9 Oct 2026 13:16:27 +0200 Subject: [PATCH 2/2] 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. --- .../00-report.md | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/04-ISSUES/347-a-pull-request-opened-after-its-push-is-never-checked/00-report.md b/04-ISSUES/347-a-pull-request-opened-after-its-push-is-never-checked/00-report.md index 7634f255..e6a8a2bb 100644 --- a/04-ISSUES/347-a-pull-request-opened-after-its-push-is-never-checked/00-report.md +++ b/04-ISSUES/347-a-pull-request-opened-after-its-push-is-never-checked/00-report.md @@ -1,7 +1,7 @@ --- status: located opened: 2026-10-09 -located-in: [mesh-catalog (modules/gitea, the announcer in index.ts and the client)] +located-in: [mesh-catalog (modules/gitea, the announcer in index.ts and the client; modules/mesh-delivery, saying it), mesh-controller (its words)] fixed-by: amended-design: --- @@ -35,7 +35,8 @@ The same day a call of the forge module's `gitea_get_pull_request` with a misnam ## 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, the mesh tells me which pull request, as a condition. A tool asked +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 @@ -45,21 +46,28 @@ 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 forge itself is asked for every open pull request. One whose base requires - `mesh/merge-gate` and whose head has had no such status for ten minutes is said as the forge failing *the - merge check of that pull request*, a provider standing ([ADR 0224](../../02-DECISIONS/0224-a-provider-that-keeps-failing-a-consumer-is-a-problem-the-controller-reports.md)) - the controller keeps as a condition and says to the operator. It is said again every pass while it lasts and - recovered when a check arrives or the pull request closes. The watcher asks the forge, not the announcer's - record, so a fault in the announcer cannot hide itself. +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..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. An open pull request on a - protected base with no merge check for ten minutes is said; one with a check, on a base that requires none, or - five minutes old is not. `pulls/NaN`, `0` and `1.5` are refused and the forge is never asked. All three fail on - the commit before the fix. + (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.