diff --git a/00-META/glossary.md b/00-META/glossary.md index 7b79b9d6..39e89a70 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -554,6 +554,10 @@ Not a domain of the mesh but of the way it is built, listed because its words me older issue not resolved already asks; a certain duplicate is closed `wontfix` with its report kept on the older issue ([ADR 0278](../02-DECISIONS/0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md)). Its pass over the backlog is the verb `groom`. +- **triage** — the issue tracker's judgement of an opened issue, made by the agent with no tools: whether it is a + duplicate (the **duplicate check**), its **severity** and what it **needs**, a suggested owner, and the issues it + depends on or relates to; it fills only what nobody gave ([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)). +- **ready** — of an issue: every issue it depends on is wontfix, or resolved with its fix delivered. - **hq check** — one of `00-META/checks/`, run by `merge-check.sh` as this repository's check. ## Homonyms diff --git a/02-DECISIONS/0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md b/02-DECISIONS/0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md index d82d0cda..d9c89e3c 100644 --- a/02-DECISIONS/0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md +++ b/02-DECISIONS/0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md @@ -179,6 +179,11 @@ One pass runs at a time. A pass's findings are kept until the next pass replaces `duplicate-check` (`on` by default; `off` stops the check on new issues, said in the log) and `judge-model` (`sonnet` by default; an alias the judgement tool accepts). Both are the module's ordinary settings. +> **The mechanism changed — 2026-10-10, by ADR 0279.** The judgement this record decided is now one part of the +> **triage** ([ADR 0279](0279-the-issue-tracker-triages-every-new-issue-and-an-issue-is-ready-when-what-it-depends-on-is-delivered.md)): its second question is always asked and also estimates severity, needs, a suggested +> owner and the issues the new one depends on or relates to. What it decided of duplicates stands; the triage's +> notes and links are recorded as by `triage`, and the notes written as `duplicate-check` are still read. + ## Consequences - An issue opened twice is closed as a duplicate within the judgement's time of being opened, with the reason diff --git a/02-DECISIONS/0279-the-issue-tracker-triages-every-new-issue-and-an-issue-is-ready-when-what-it-depends-on-is-delivered.md b/02-DECISIONS/0279-the-issue-tracker-triages-every-new-issue-and-an-issue-is-ready-when-what-it-depends-on-is-delivered.md new file mode 100644 index 00000000..a0310187 --- /dev/null +++ b/02-DECISIONS/0279-the-issue-tracker-triages-every-new-issue-and-an-issue-is-ready-when-what-it-depends-on-is-delivered.md @@ -0,0 +1,171 @@ +--- +topic: how we work +status: accepted +date: 2026-10-10 +deciders: jochen +reconstructed: false +extends: 02-DECISIONS/0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md +--- + +# 279. The issue tracker triages every new issue, and an issue is ready when what it depends on is delivered + +## Context + +[ADR 0278](0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md) +made the tracker judge every opened issue against the older ones, for a duplicate. The same afternoon the operator +asked for more from that judgement: + +- "Add a severity field to an issue. The groomer should pick up new issues and add metadata, like severity, so the + issuer doesn't have to provide all the knowledge — the groomer has a better understanding of the existing issues. + Another good indication: whether an issue needs a decision or the operator's feedback." +- "Issue links matter, especially when one issue depends on another being delivered first." + +Agents choose their next piece of work from `issue-tracker.list`. Today a row says a number, a status, an owner and +a title, so an agent cannot tell a contained fix from one that needs the operator, or one it can start from one that +waits on another fix not yet on the machines. A complexity hint for choosing a model was asked for and withdrawn the +same day as a question to research first; this record leaves it out. + +**What the tracker holds, measured on 2026-10-10.** 42 issues at `open` and more at `diagnosing` and `located`; an +issue is one JSON value in the module's key-value state ([ADR 0201](0201-a-module-keeps-its-current-state-in-key-value-buckets-it-declares-and-reaches-through-the-runtime.md)), +not a table. Links of kind `duplicates` and `relates-to` exist; nothing records an order between two issues. +`mesh-delivery` says every change of a delivery as its `transition` event, with the repository, the pull request's +number and the state it moved to, `delivered` among them; its `checks` verb answers a pull request's delivery and +its state. + +The record was searched first ("severity", "depends on", "ready", "triage"): nothing beside ADR 0278 and research +042, whose model left priorities out "each needs a decision of its own" (to-be 53, "What it leaves out"). + +**A word.** The glossary has none for the judgement as a whole. This record names it **triage**: the duplicate +check of ADR 0278 is one of its parts. + +## Considered Options + +### Who sets severity and needs + +1. *The issuer, required at open.* Refused: the issuer knows the symptom and not the backlog; a required field is + filled with a guess. +2. *A person, later.* Refused as the default: nobody grooms 40 issues by hand, which is why the operator asked. +3. **The triage estimates them, the issuer may give them, a person may change them.** The triage fills only what + is empty and says why in a note; where the issuer gave a value it disagrees with, it adds a note and changes + nothing. Chosen. + +### When an issue is ready + +1. *When every issue it depends on is resolved.* Refused: resolved means merged and recorded, not on the machines; + the operator asked for "delivered first". +2. **When every issue it depends on is resolved, or closed as wontfix, and every pull request its fix names is + delivered** — the delivery's own state, not the merge. Chosen. A wontfix dependency will never be delivered, + and an issue must not wait for it for ever; a duplicate's original is linked by its own `duplicates` link and + carries the work. + +### How the tracker learns a fix is delivered + +1. *Ask `mesh-delivery.checks` for every dependency at every `list`.* Refused: a list over the backlog would make a + call per pull request, past the 30 seconds a call is answered in. +2. **The delivery's own event, kept on the issue.** The tracker consumes `mesh-delivery.transition` and records, on + each issue whose `fixed-by` names that pull request, that it is delivered. A fix delivered before its issue was + resolved is asked of `checks` once, when the tracker hears its own `moved` to resolved. Chosen. + +## Decision + +### 1. What an issue gains + +- `severity`, one of **critical** (something the operator relies on is broken, or data is at risk), **major** (a rule + of the mesh does not hold, or people work around it) and **minor** (a contained fix in one repository; nothing the + operator sees changes). Empty until given or estimated. +- `needs`, one of **none** (an agent can fix it), **decision** (a decision record must be written first) and + **operator** (the operator's answer or feedback is required). Empty until given or estimated. +- `suggested-owner`: an owner from the registered list, suggested by the triage when `located-in` is empty. It is + not `located-in`, which says where a diagnosis found the fault. +- `delivered`: the entries of `fixed-by` the delivery said are delivered. +- `triage`: the triage's judgement as it was made — who judged, when, each value with its reason — kept on the issue + so a second delivery of the event applies the same judgement rather than a new one. +- A link kind **`depends-on`**: this issue waits for that one to be delivered. `link` refuses one that would close a + cycle, naming it. `get` shows both directions: what an issue depends on, and what it is **needed by**, the + issues that depend on it. + +The values are optional keys of the one JSON value an issue is. An issue kept before this record reads as having no +severity, no needs and no dependency: there is no table and no schema to migrate, so no numbered migration and no +consolidated schema exist for this store; a reader that does not know a key ignores it. + +### 2. Verbs + +- `open` takes `severity` and `needs` optionally. +- A new verb **`set`** changes `severity`, `needs` and `suggested-owner` of an issue, as a person decides; each + change is said as the event `set` and kept, with who set it. +- `list` filters on `severity`, `needs` and **`ready`**; its rows carry severity, needs and ready. An agent looking + for work asks for `severity=minor`, `needs=none`, `ready`. +- `get` answers `needed-by` and `ready` beside the issue. + +### 3. Ready + +An issue is **ready** when it is not resolved or wontfix and every issue it depends on is either wontfix, or +resolved with every entry of its `fixed-by` delivered. An issue with no dependency is ready. When a dependency's fix +is delivered and that makes an issue ready, the issue gains a note saying so and naming the dependency, and the +tracker says the event **`ready`** with its number, so a picker can react. + +The tracker consumes `mesh-delivery.transition`; a transition to `delivered` with a pull request's number marks +every issue whose `fixed-by` names `#`. On its own `moved` to `resolved`, it asks +`mesh-delivery.checks` for each pull request in `fixed-by` it has not heard delivered, and marks those whose +delivery is `delivered`. + +### 4. Triage + +The judgement of ADR 0278 becomes the triage, on the same `opened` event and in `groom`. Its two questions stay; +the second is now always asked, with the shortlisted issues (none, when the shortlist is empty) and the registered +owners, and answers beside the duplicate verdict: + +- severity, needs and, when the issue names no owner, a suggested owner — each with a one-line reason; +- the shortlisted issues the new one **depends on** (they must be delivered first) and those it **relates to**, + each with a one-line reason. + +The tracker keeps the judgement on the issue first, then applies it through its own verbs, each step only when it +is not done yet: + +1. one note on the new issue saying the whole judgement, a line per value with its reason; where the issuer gave a + severity or needs the triage disagrees with, the line says so and that the issuer's value stands; +2. each empty field filled; a value a person gave is never changed; +3. each `depends-on` and `relates-to` link added, unless the issue already has it, it would close a cycle, or its + target is resolved or wontfix; the older issue gains a note naming the newer one and why; +4. the duplicate verdict applied as ADR 0278 §2 says. + +The triage only adds: it removes no link and changes no value a person set. What it writes is recorded as by +`triage`; the notes ADR 0278 wrote as `duplicate-check` are still read as that issue's duplicate verdict. + +### 5. The backlog + +`groom` triages every issue not resolved that has no triage yet, as before: a dry run kept as a pass, listing for +each issue the values it would fill, the links it would add and the duplicates it would close; then `confirm` +applies exactly that pass. An issue that ADR 0278 already checked keeps its duplicate verdict and gains the rest. + +## Consequences + +- An agent picks its next fix from one call — `list` with `severity=minor`, `needs=none`, `ready` — without reading + every report. +- An issue waiting on another fix says so in the tracker, and is said ready when that fix reaches the machines, + not when it merges. +- The triage's values are estimates by a model. They are filled only where nobody gave one, each carries its + reason, and a person changes one with `set`. +- The tracker now depends on `mesh-delivery`'s event for readiness; while that module is down, nothing becomes + ready, which is the safe side. + +## How it is checked + +| Rule | Checked by | +|---|---| +| The triage fills only what is empty, says why, and notes a disagreement without changing the issuer's value | the tracker's test with the judgement stubbed: an issue with severity given and needs empty | +| A dependency is linked, a cycle refused, a resolved target not linked; nothing a person set is removed | the tracker's tests of `link` (a cycle of two and of three) and of the triage's links | +| Ready: every dependency wontfix or resolved with its fix delivered | the tracker's table test: no dependency, a dependency open, resolved and not delivered, resolved and delivered, wontfix | +| A delivery's transition marks the fix delivered and the waiting issue gains a note and the `ready` event, once | the tracker's test: the event twice, one note, one `ready` | +| A fix delivered before its issue was resolved is found at the move | the tracker's test with `checks` stubbed | +| `list` filters on severity, needs and ready; `get` says needed-by | the tracker's tests | +| `set` changes the three values, refuses another, and says the event | the tracker's test | +| The backlog's dry run changes nothing; confirm fills only what is empty | the tracker's test | +| Live | an agent opens an issue: within a few minutes `get` shows its severity, needs and the triage's note; after the backlog's pass is applied, `list` with `severity=minor`, `needs=none`, `ready` answers the issues an agent can take | + +## References + +- [ADR 0278](0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md) — the judgement this record widens +- [ADR 0275](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) — the tracker +- [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 e2f1a7ef..85dc46c0 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/0279-the-issue-tracker-triages-every-new-issue-and-an-issue-is-ready-when-what-it-depends-on-is-delivered.md --- # 53 — The issue tracker @@ -38,10 +39,11 @@ each with what the operator sees when it is done. ├ bucket counters one key per sequence; slice 1 has `issue` only ├ bucket grooming the backlog pass of the duplicate check (§ 9) ├ setting owners the registered list `located-in` may name - ├ verbs open, get, list, move, note, link, import, groom, + ├ verbs open, get, list, move, note, link, import, groom, set, │ served by the control-node's tool runner - ├ events opened, moved, noted, linked, imported - └ consumes its own `opened`: the duplicate check (§ 9) + ├ events opened, moved, noted, linked, imported, set, triaged, ready + └ consumes its own `opened` and `moved`: the triage (§ 9); + mesh-delivery's `transition`: when a fix is delivered (§ 10) claude-code (on the same node) └ claude_code_judge one question answered by the agent with no tools (§ 9) @@ -69,6 +71,7 @@ agent finds them with `mesh_search` ("open an issue") and calls them as `issue-t | `move` | a number, a status, and what that status needs | changes the status under the rules of § 5 | | `note` | a number and a text | appends to the issue's notes; a note is never edited | | `link` | a number, a kind and a target, or `remove` | adds or removes a typed link | +| `set` | a number and any of `severity`, `needs`, `suggested-owner` | changes them as a person decides (§ 10) | | `groom` | optionally `confirm` and a pass's id | the duplicate check over the backlog (§ 9): without `confirm`, starts a dry run in the background and answers its id; with the id alone, its progress and findings; with `confirm` and the id, applies exactly that pass's findings, each checked again | | `import` (slice 3) | optionally `dry-run`, a commit | reads every issue folder of hq through the records module and writes each as an issue at its own number; creates what is absent, fills a status the tracker holds empty, never overwrites, never advances the counter; run again, changes nothing and says so | @@ -86,7 +89,10 @@ caller; the module records that identity as who acted. No verb takes an actor. - `notes`: appended, each with who wrote it, when, and the text; - `links`, typed: to another issue (`duplicates`, `relates-to`), a pull request, a decision record, a design, a condition key or a delivery; -- `record`: the path of the issue's folder in hq, when it has one. +- `record`: the path of the issue's folder in hq, when it has one; +- `severity` (critical, major, minor), `needs` (none, decision, operator), `suggested-owner`, `delivered` (the + entries of `fixed-by` the delivery said are delivered) and `triage` (the triage's judgement as made), and the link + kind `depends-on` (§ 10). **The registered list of owners** is a setting of the module, changed through the controller's settings verb like any other module setting. Slice 1 starts it with the repositories in hq's `00-META/repos.md` and the modules the catalogue @@ -164,6 +170,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 | +| 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 | @@ -204,7 +211,32 @@ was checked** ([ADR 0278](../../02-DECISIONS/0278-the-issue-tracker-checks-every `grooming`, then applies that pass's findings on `confirm`. - Settings: `duplicate-check` (`on`) and `judge-model` (`sonnet`). +## 10. Triage, severity and readiness + +**An agent opening an issue need not know the backlog: the triage estimates its severity and what it needs, links +what it depends on, and the issue is said ready when every fix it waits for is delivered** +([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)). + +- **Triage** is the judgement of § 9 widened: its second question is always asked and also answers severity, + needs, a suggested owner when the issue names none, and the shortlisted issues it depends on or relates to, each + with a one-line reason. The judgement is kept on the issue first, then applied through the tracker's own verbs: + one note saying it all, each empty field filled, each link added (never one that closes a cycle, never to a + resolved or wontfix issue), the duplicate verdict last. It fills only what is empty; a value a person gave + stands, and a disagreement is a line in the note. Recorded as by `triage`. +- **Severity**: critical (something the operator relies on is broken, or data is at risk), major (a rule of the + mesh does not hold, or people work around it), minor (a contained fix; nothing the operator sees changes). + **Needs**: none, decision (a decision record first), operator (the operator's answer is required). +- **`depends-on`**: refused when it would close a cycle; `get` shows what an issue depends on and what it is + needed by. +- **Ready**: not resolved or wontfix, and every dependency wontfix, or resolved with every entry of its `fixed-by` + delivered. The tracker consumes `mesh-delivery.transition` and, at its own `moved` to resolved, asks + `mesh-delivery.checks` for what it has not heard; an issue made ready gains a note and the event `ready`. +- `open` takes severity and needs; `set` changes severity, needs and suggested owner; `list` filters on severity, + needs and ready. `groom` triages the backlog, dry run first. +- 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. + ## What it leaves out -Priorities, claims, notifications, full-text or semantic search, the forge's own issue lists, and an index of any kind: +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: none of them is in any slice above, and each needs a decision of its own.