From 5ef7d4dc365cddfcd0c3426298c7e25da8fc8b59 Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 10 Oct 2026 18:08:38 +0200 Subject: [PATCH] =?UTF-8?q?ADR=200281,=20to-be=2053=20=C2=A711:=20an=20iss?= =?UTF-8?q?ue=20knows=20whether=20its=20fix=20is=20delivered,=20and=20a=20?= =?UTF-8?q?fix=20stuck=20after=20its=20merge=20is=20loud?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The operator approved measuring, from the delivery's own events, whether each fix an issue names reached the machines; delivered is not resolved, and a merged fix that does not arrive must be said rather than found. --- ...and-a-fix-stuck-after-its-merge-is-loud.md | 124 ++++++++++++++++++ 03-DESIGN/01-to-be/53-the-issue-tracker.md | 21 ++- 2 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 02-DECISIONS/0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md diff --git a/02-DECISIONS/0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md b/02-DECISIONS/0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md new file mode 100644 index 00000000..6795afff --- /dev/null +++ b/02-DECISIONS/0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md @@ -0,0 +1,124 @@ +--- +topic: how we work +status: accepted +date: 2026-10-10 +deciders: jochen +reconstructed: false +extends: 02-DECISIONS/0279-the-issue-tracker-triages-every-new-issue-and-an-issue-is-ready-when-what-it-depends-on-is-delivered.md +--- + +# 281. An issue knows whether its fix is delivered, and a fix stuck after its merge is loud + +## Context + +[ADR 0279](0279-the-issue-tracker-triages-every-new-issue-and-an-issue-is-ready-when-what-it-depends-on-is-delivered.md) +made the tracker hear when a pull request in an issue's `fixed-by` is delivered, for one purpose: an issue that +depends on it becomes ready. On 2026-10-10 the operator approved a wider use of the same fact: an issue knows +whether its fix is delivered, every step of the way — merged, held and why, covered by a later merge, delivered — +"a measured fact, never set by hand"; delivered is not resolved; and "a merged-but-stuck fix is loud". + +**What the delivery says, measured on 2026-10-10.** `mesh-delivery` says every change of a delivery as its event +`transition`, with the repository, the pull request's number, the state it moved to (proposed, checked, ready, +published, delivering, held, delivered, failed, superseded, stopped, retired, covered), why, and the trunk commit it +merged as. Its verb `checks` answers a pull request's delivery, its state and what it waits for. No new event is +needed. Today an agent resolves an issue when its pull request merges, and nothing says whether the fix reached the +machines; a merge held for a person's push waits until somebody looks. + +The record was searched first ("delivered", "held", "stuck", "ready to verify"): ADR 0239 and ADR 0270 (the +delivery and its covered state), and ADR 0279. + +**What the mesh offers a module to say something is wrong, measured.** A module raises a condition only through +its health ([ADR 0256](0256-a-machines-hardware-condition-is-read-by-one-module-on-every-machine-and-a-fault-is-said-through-its-health.md)), +and a module that runs only its bundle declares none +([ADR 0240](0240-a-module-says-how-it-is-healthy-and-the-node-engine-judges-it.md) rule 8). The tracker runs only its +bundle. What it can do is tell the operator's channel (`operator-channel.notify`), which deduplicates, caps and +routes a message like the controller's conditions and clears it when told it is over. + +## Considered Options + +### Where the fact comes from + +1. *A link from the issue to its delivery plan, set by the agent.* Refused by the operator: a link set by hand goes + stale, and the plan is the delivery's, not the issue's. +2. **The delivery's own events, for the pull requests the issue's `fixed-by` names.** Chosen: measured, and kept up + to date by the module that owns the delivery. + +### How a stuck fix is said + +1. *A condition of the controller's, raised by the tracker.* Not possible as the mesh stands: a module raises a + condition only through its health, and the tracker has none to declare. Giving it a long-running process for + that alone would be a health check the module runs on itself, which ADR 0240 refuses as a judge. +2. **A note on the issue, and a warning on the operator's channel, cleared when the fix is delivered.** Chosen. The + channel routes it like a condition; the gap — a bundle-only module cannot raise a condition of its own — is named + here and is a question for its own decision. + +## Decision + +### 1. Each fix is measured + +For each pull request an issue's `fixed-by` names (`#`, read in any case, or the forge's page of +it), the tracker keeps what the delivery last said: its state, why, whether it is merged and when. It learns it from +`mesh-delivery.transition` for every state, and, for a pull request it has not heard of when the issue names it, +from `mesh-delivery.checks` when it hears its own `moved` (at most three asks per handling; an ask that fails has the +event offered again). No verb sets it. + +### 2. One line says it + +`get` and every row of `list` carry **delivery**, derived from those states, worst first: + +- "fix not delivered: …" — a delivery failed or was stopped, with why; +- "held: …" — with why, as the delivery says it; +- "fix merged, not delivered: …"; +- "fix not merged: …"; +- "nothing heard from the delivery of …"; +- "delivered to every machine it should reach", or "… (by a later merge that contains it)" for one covered. + +An issue whose `fixed-by` names no pull request has no line. + +### 3. Delivered is not resolved + +`resolved` still means the acceptance was proven live. When every fix of an issue that is not resolved or wontfix is +delivered, the issue gains a note — "Ready to verify: every fix of this issue is delivered … prove the acceptance +live, then resolve it" — and the tracker says the event **`ready-to-verify`**, once for that set of fixes. Agents +therefore name a fix in `fixed-by` when it merges, with the issue still `located`, and resolve it after the proof: +`move` to the status an issue already has is taken when it changes what the issue holds, such as its `fixed-by`, and +refused when it changes nothing. `move` still requires `fixed-by` to resolve. + +### 4. A stuck fix is loud + +A fix merged and not delivered after **two hours** (the module's setting `stuck-after`) is said once: a note on the +issue, and a warning on the operator's channel under the key `issue-tracker.fix-stuck.`, cleared there when +every fix of the issue is delivered. Two hours is past every ordinary delivery — a walk takes minutes, a first-node +gate ten at most — and still the same working day; a hold for a person is what it catches. The tracker looks every +five minutes. One that the channel did not take is said at the next look. + +### 5. Ready, as before + +An issue is ready when everything it depends on is wontfix, or resolved with every fix delivered (ADR 0279 §3): the +same measured fact. + +## Consequences + +- An agent sees from `list` which fixes are merged and not on the machines, and which wait for a person and why. +- A fix held for the operator's push says so to the operator within two hours of its merge, instead of waiting for + somebody to look. +- Resolving moves later: after the delivery and the proof. An issue can sit at `located` with its fix delivered; + the `ready-to-verify` note and event say so. +- The stuck warning is a message on the operator's channel, not a controller condition; `mesh-controller.conditions` + does not list it. + +## How it is checked + +| Rule | Checked by | +|---|---| +| The line follows the delivery's states: not merged, merged, held with why, delivered, covered | the tracker's test feeding transitions | +| Ready to verify once, only for an issue not resolved; it stays unresolved | the tracker's test: the delivered transition twice, one note, one event; a resolved issue gains none | +| A stuck fix is said once on the issue and the channel, cleared when delivered; under the bound nothing; the bound is the setting | the tracker's test with the channel stubbed, including one that fails and is said at the next look | +| A pull request named before the tracker heard it is asked once, failure loud | the tracker's tests of `moved` with `checks` stubbed | +| Live | an issue at `located` names a merged pull request; `get` says "fix merged, not delivered", then "delivered to every machine it should reach", and the issue gains the ready-to-verify note | + +## References + +- [ADR 0279](0279-the-issue-tracker-triages-every-new-issue-and-an-issue-is-ready-when-what-it-depends-on-is-delivered.md) — readiness, which this record widens +- [ADR 0239](0239-a-delivery-is-owned-by-the-mesh-delivery-module-and-runs-from-commit-to-delivered.md) — the delivery and its states +- [To-be 53](../03-DESIGN/01-to-be/53-the-issue-tracker.md) — the design this record amends diff --git a/03-DESIGN/01-to-be/53-the-issue-tracker.md b/03-DESIGN/01-to-be/53-the-issue-tracker.md index f7aa935e..d2996884 100644 --- a/03-DESIGN/01-to-be/53-the-issue-tracker.md +++ b/03-DESIGN/01-to-be/53-the-issue-tracker.md @@ -6,6 +6,7 @@ updated: 2026-10-10 decisions: - 02-DECISIONS/0275-the-mesh-numbers-and-tracks-its-issues-in-a-module-of-its-own-and-hq-keeps-only-the-narrative-of-a-design-or-governance-issue.md - 02-DECISIONS/0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md + - 02-DECISIONS/0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md - 02-DECISIONS/0279-the-issue-tracker-triages-every-new-issue-and-an-issue-is-ready-when-what-it-depends-on-is-delivered.md --- @@ -41,7 +42,7 @@ each with what the operator sees when it is done. ├ setting owners the registered list `located-in` may name ├ verbs open, get, list, move, note, link, import, groom, set, │ served by the control-node's tool runner - ├ events opened, moved, noted, linked, imported, set, triaged, ready + ├ events opened, moved, noted, linked, imported, set, triaged, ready, ready-to-verify └ consumes its own `opened` and `moved`: the triage (§ 9); mesh-delivery's `transition`: when a fix is delivered (§ 10) @@ -170,6 +171,7 @@ in one call, from any session, and the responder on a channel answers from it on | The cycle's rules hold at the write | the module's table test over § 5, one case per row, each refused case changing nothing | | The module creates no bucket | the runtime refuses a module that tries (ADR 0201 §3); the controller's raise asserts both buckets | | The caller is who acted | the module's test that the recorded actor is the call's identity, whatever the arguments say | +| An issue's delivery line follows the delivery; ready to verify once; a stuck fix said once and cleared | the rows of [ADR 0281](../../02-DECISIONS/0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md)'s "How it is checked" | | The triage fills only what is empty, links no cycle, and an issue is ready only when its dependencies' fixes are delivered | the rows of [ADR 0279](../../02-DECISIONS/0279-the-issue-tracker-triages-every-new-issue-and-an-issue-is-ready-when-what-it-depends-on-is-delivered.md)'s "How it is checked" | | The duplicate check acts only on a clear duplicate, never on an older or a resolved issue, once per issue, loudly when it cannot judge | the rows of [ADR 0278](../../02-DECISIONS/0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md)'s "How it is checked" | | Live | on the mesh, an agent calls `issue-tracker.open` and gets the next number; two sessions on two nodes open at the same moment and get two different numbers; `list` shows both | @@ -236,6 +238,23 @@ what it depends on, and the issue is said ready when every fix it waits for is d - The values are optional keys of an issue's one JSON value: an issue kept before reads as having none, and the key-value store has no schema to migrate. +## 11. Whether the fix is delivered + +**An issue knows whether its fix is on the machines, as the delivery says it; delivered is not resolved, and a fix +stuck after its merge is said** ([ADR 0281](../../02-DECISIONS/0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md)). + +- For each pull request `fixed-by` names, the tracker keeps the state `mesh-delivery.transition` last said — merged, + held and why, covered, delivered — or asks `mesh-delivery.checks` once for one it has not heard of. Nothing sets it + by hand. +- `get` and each row of `list` carry **delivery**, one line: "fix not delivered: …", "held: …", "fix merged, not + delivered", "fix not merged", "nothing heard …", "delivered to every machine it should reach". +- When every fix of an issue not yet resolved is delivered, it gains a note and the event `ready-to-verify`, once; + it is resolved after the acceptance is proven live. A fix is named while the issue stays `located`: `move` to the + same status changes what the issue holds. +- A fix merged and not delivered after `stuck-after` (two hours) is said once: a note on the issue and a warning on + the operator's channel, cleared when it is delivered. It is not a controller condition: a module that runs only + its bundle cannot raise one. + ## What it leaves out Priorities beyond severity, claims, notifications beyond the `ready` event, full-text or semantic search, the forge's own issue lists, and an index of any kind: