Merge pull request 'ADR 0279, to-be 53 §10: the issue tracker triages every new issue, and an issue is ready when what it depends on is delivered' (#285) from decisions/0279-the-issue-tracker-triages-every-new-issue into main
This commit was merged in pull request #285.
This commit is contained in:
@@ -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
|
||||
|
||||
+5
@@ -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
|
||||
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
---
|
||||
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 — except one closed as the duplicate of another: the work moved
|
||||
there, so it lets go only when its original does.
|
||||
|
||||
### 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 pull requests 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`, with who set it; a value cannot be cleared, only changed.
|
||||
- `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 pull request its `fixed-by` names (`<repository>#<number>`, in any case, or the forge's page of
|
||||
it) delivered; an entry that names no pull request, such as a commit, is not waited for. A wontfix dependency
|
||||
closed as the duplicate of another counts as its original does. Readiness is said once per spell: an issue keeps
|
||||
the dependencies it was said ready for, so one delivery letting two of them go says it once. 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 `<repository>#<number>`. On its own `moved` to `resolved`, it asks
|
||||
`mesh-delivery.checks` for each pull request in `fixed-by` it has not heard delivered, at most three per handling,
|
||||
and marks those whose delivery is `delivered`. An ask that fails, or more left to ask, has the event offered again,
|
||||
so a delivery never asked ends in `max-deliveries` rather than in silence.
|
||||
|
||||
### 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 every pull request of 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
|
||||
@@ -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
|
||||
pull requests 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 (one closed as a duplicate counts as its
|
||||
original), or resolved with every pull request its `fixed-by` names delivered; said once per spell. 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.
|
||||
|
||||
Reference in New Issue
Block a user